aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/traccar/BaseDataHandler.java (renamed from src/org/traccar/BaseDataHandler.java)21
-rw-r--r--src/main/java/org/traccar/BaseFrameDecoder.java37
-rw-r--r--src/main/java/org/traccar/BaseHttpProtocolDecoder.java39
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java173
-rw-r--r--src/main/java/org/traccar/BaseProtocol.java (renamed from src/org/traccar/BaseProtocol.java)47
-rw-r--r--src/main/java/org/traccar/BaseProtocolDecoder.java (renamed from src/org/traccar/BaseProtocolDecoder.java)163
-rw-r--r--src/main/java/org/traccar/BaseProtocolEncoder.java92
-rw-r--r--src/main/java/org/traccar/CharacterDelimiterFrameDecoder.java (renamed from src/org/traccar/CharacterDelimiterFrameDecoder.java)28
-rw-r--r--src/main/java/org/traccar/Context.java (renamed from src/org/traccar/Context.java)287
-rw-r--r--src/main/java/org/traccar/DeviceSession.java (renamed from src/org/traccar/DeviceSession.java)14
-rw-r--r--src/main/java/org/traccar/EventLoopGroupFactory.java37
-rw-r--r--src/main/java/org/traccar/ExtendedObjectDecoder.java (renamed from src/org/traccar/ExtendedObjectDecoder.java)56
-rw-r--r--src/main/java/org/traccar/GlobalTimer.java (renamed from src/org/traccar/GlobalTimer.java)4
-rw-r--r--src/main/java/org/traccar/Main.java156
-rw-r--r--src/main/java/org/traccar/MainEventHandler.java161
-rw-r--r--src/main/java/org/traccar/MainModule.java383
-rw-r--r--src/main/java/org/traccar/NetworkMessage.java38
-rw-r--r--src/main/java/org/traccar/PipelineBuilder.java24
-rw-r--r--src/main/java/org/traccar/Protocol.java37
-rw-r--r--src/main/java/org/traccar/ServerManager.java (renamed from src/org/traccar/ServerManager.java)36
-rw-r--r--src/main/java/org/traccar/StringProtocolEncoder.java (renamed from src/org/traccar/StringProtocolEncoder.java)36
-rw-r--r--src/main/java/org/traccar/TrackerServer.java115
-rw-r--r--src/main/java/org/traccar/WebDataHandler.java (renamed from src/org/traccar/WebDataHandler.java)118
-rw-r--r--src/main/java/org/traccar/WindowsService.java231
-rw-r--r--src/main/java/org/traccar/WrapperContext.java255
-rw-r--r--src/main/java/org/traccar/WrapperInboundHandler.java94
-rw-r--r--src/main/java/org/traccar/WrapperOutboundHandler.java99
-rw-r--r--src/main/java/org/traccar/api/AsyncSocket.java (renamed from src/org/traccar/api/AsyncSocket.java)7
-rw-r--r--src/main/java/org/traccar/api/AsyncSocketServlet.java (renamed from src/org/traccar/api/AsyncSocketServlet.java)0
-rw-r--r--src/main/java/org/traccar/api/BaseObjectResource.java (renamed from src/org/traccar/api/BaseObjectResource.java)31
-rw-r--r--src/main/java/org/traccar/api/BaseResource.java (renamed from src/org/traccar/api/BaseResource.java)0
-rw-r--r--src/main/java/org/traccar/api/CorsResponseFilter.java (renamed from src/org/traccar/api/CorsResponseFilter.java)24
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java (renamed from src/org/traccar/api/ExtendedObjectResource.java)0
-rw-r--r--src/main/java/org/traccar/api/MediaFilter.java92
-rw-r--r--src/main/java/org/traccar/api/ObjectMapperProvider.java (renamed from src/org/traccar/api/ObjectMapperProvider.java)0
-rw-r--r--src/main/java/org/traccar/api/ResourceErrorHandler.java (renamed from src/org/traccar/api/ResourceErrorHandler.java)12
-rw-r--r--src/main/java/org/traccar/api/SecurityRequestFilter.java (renamed from src/org/traccar/api/SecurityRequestFilter.java)17
-rw-r--r--src/main/java/org/traccar/api/SimpleObjectResource.java (renamed from src/org/traccar/api/SimpleObjectResource.java)0
-rw-r--r--src/main/java/org/traccar/api/UserPrincipal.java (renamed from src/org/traccar/api/UserPrincipal.java)0
-rw-r--r--src/main/java/org/traccar/api/UserSecurityContext.java (renamed from src/org/traccar/api/UserSecurityContext.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/AttributeResource.java (renamed from src/org/traccar/api/resource/AttributeResource.java)44
-rw-r--r--src/main/java/org/traccar/api/resource/CalendarResource.java (renamed from src/org/traccar/api/resource/CalendarResource.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/CommandResource.java (renamed from src/org/traccar/api/resource/CommandResource.java)11
-rw-r--r--src/main/java/org/traccar/api/resource/DeviceResource.java (renamed from src/org/traccar/api/resource/DeviceResource.java)25
-rw-r--r--src/main/java/org/traccar/api/resource/DriverResource.java (renamed from src/org/traccar/api/resource/DriverResource.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/EventResource.java (renamed from src/org/traccar/api/resource/EventResource.java)4
-rw-r--r--src/main/java/org/traccar/api/resource/GeofenceResource.java (renamed from src/org/traccar/api/resource/GeofenceResource.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/GroupResource.java (renamed from src/org/traccar/api/resource/GroupResource.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/MaintenanceResource.java36
-rw-r--r--src/main/java/org/traccar/api/resource/NotificationResource.java (renamed from src/org/traccar/api/resource/NotificationResource.java)34
-rw-r--r--src/main/java/org/traccar/api/resource/PermissionsResource.java (renamed from src/org/traccar/api/resource/PermissionsResource.java)5
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java (renamed from src/org/traccar/api/resource/PositionResource.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/ReportResource.java (renamed from src/org/traccar/api/resource/ReportResource.java)123
-rw-r--r--src/main/java/org/traccar/api/resource/ServerResource.java (renamed from src/org/traccar/api/resource/ServerResource.java)15
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java (renamed from src/org/traccar/api/resource/SessionResource.java)22
-rw-r--r--src/main/java/org/traccar/api/resource/StatisticsResource.java (renamed from src/org/traccar/api/resource/StatisticsResource.java)0
-rw-r--r--src/main/java/org/traccar/api/resource/UserResource.java (renamed from src/org/traccar/api/resource/UserResource.java)3
-rw-r--r--src/main/java/org/traccar/config/Config.java166
-rw-r--r--src/main/java/org/traccar/config/ConfigKey.java36
-rw-r--r--src/main/java/org/traccar/config/ConfigSuffix.java28
-rw-r--r--src/main/java/org/traccar/config/Keys.java370
-rw-r--r--src/main/java/org/traccar/database/ActiveDevice.java (renamed from src/org/traccar/database/ActiveDevice.java)7
-rw-r--r--src/main/java/org/traccar/database/AttributesManager.java (renamed from src/org/traccar/database/AttributesManager.java)0
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java (renamed from src/org/traccar/database/BaseObjectManager.java)7
-rw-r--r--src/main/java/org/traccar/database/CalendarManager.java (renamed from src/org/traccar/database/CalendarManager.java)0
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java (renamed from src/org/traccar/database/CommandsManager.java)41
-rw-r--r--src/main/java/org/traccar/database/ConnectionManager.java (renamed from src/org/traccar/database/ConnectionManager.java)38
-rw-r--r--src/main/java/org/traccar/database/DataManager.java (renamed from src/org/traccar/database/DataManager.java)117
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java (renamed from src/org/traccar/database/DeviceManager.java)149
-rw-r--r--src/main/java/org/traccar/database/DriversManager.java (renamed from src/org/traccar/database/DriversManager.java)0
-rw-r--r--src/main/java/org/traccar/database/ExtendedObjectManager.java (renamed from src/org/traccar/database/ExtendedObjectManager.java)9
-rw-r--r--src/main/java/org/traccar/database/GeofenceManager.java (renamed from src/org/traccar/database/GeofenceManager.java)0
-rw-r--r--src/main/java/org/traccar/database/GroupTree.java (renamed from src/org/traccar/database/GroupTree.java)0
-rw-r--r--src/main/java/org/traccar/database/GroupsManager.java (renamed from src/org/traccar/database/GroupsManager.java)13
-rw-r--r--src/main/java/org/traccar/database/IdentityManager.java50
-rw-r--r--src/main/java/org/traccar/database/LdapProvider.java179
-rw-r--r--src/main/java/org/traccar/database/MailManager.java (renamed from src/org/traccar/notification/NotificationMail.java)87
-rw-r--r--src/main/java/org/traccar/database/MaintenancesManager.java27
-rw-r--r--src/main/java/org/traccar/database/ManagableObjects.java (renamed from src/org/traccar/database/ManagableObjects.java)0
-rw-r--r--src/main/java/org/traccar/database/MediaManager.java (renamed from src/org/traccar/database/MediaManager.java)42
-rw-r--r--src/main/java/org/traccar/database/NotificationManager.java (renamed from src/org/traccar/database/NotificationManager.java)78
-rw-r--r--src/main/java/org/traccar/database/PermissionsManager.java (renamed from src/org/traccar/database/PermissionsManager.java)62
-rw-r--r--src/main/java/org/traccar/database/QueryBuilder.java (renamed from src/org/traccar/database/QueryBuilder.java)23
-rw-r--r--src/main/java/org/traccar/database/QueryExtended.java (renamed from src/org/traccar/database/QueryExtended.java)0
-rw-r--r--src/main/java/org/traccar/database/QueryIgnore.java (renamed from src/org/traccar/database/QueryIgnore.java)0
-rw-r--r--src/main/java/org/traccar/database/SimpleObjectManager.java (renamed from src/org/traccar/database/SimpleObjectManager.java)7
-rw-r--r--src/main/java/org/traccar/database/StatisticsManager.java (renamed from src/org/traccar/database/StatisticsManager.java)68
-rw-r--r--src/main/java/org/traccar/database/UsersManager.java (renamed from src/org/traccar/database/UsersManager.java)0
-rw-r--r--src/main/java/org/traccar/geocoder/Address.java (renamed from src/org/traccar/geocoder/Address.java)10
-rw-r--r--src/main/java/org/traccar/geocoder/AddressFormat.java (renamed from src/org/traccar/geocoder/AddressFormat.java)2
-rw-r--r--src/main/java/org/traccar/geocoder/BanGeocoder.java66
-rw-r--r--src/main/java/org/traccar/geocoder/BingMapsGeocoder.java (renamed from src/org/traccar/geocoder/BingMapsGeocoder.java)8
-rw-r--r--src/main/java/org/traccar/geocoder/FactualGeocoder.java (renamed from src/org/traccar/geocoder/FactualGeocoder.java)5
-rw-r--r--src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java (renamed from src/org/traccar/geocoder/GeocodeFarmGeocoder.java)34
-rw-r--r--src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java60
-rw-r--r--src/main/java/org/traccar/geocoder/Geocoder.java (renamed from src/org/traccar/geocoder/Geocoder.java)4
-rw-r--r--src/main/java/org/traccar/geocoder/GeocoderException.java (renamed from src/org/traccar/geocoder/GeocoderException.java)0
-rw-r--r--src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java (renamed from src/org/traccar/geocoder/GisgraphyGeocoder.java)13
-rw-r--r--src/main/java/org/traccar/geocoder/GoogleGeocoder.java (renamed from src/org/traccar/geocoder/GoogleGeocoder.java)15
-rw-r--r--src/main/java/org/traccar/geocoder/HereGeocoder.java88
-rw-r--r--src/main/java/org/traccar/geocoder/JsonGeocoder.java121
-rw-r--r--src/main/java/org/traccar/geocoder/MapQuestGeocoder.java (renamed from src/org/traccar/geocoder/MapQuestGeocoder.java)5
-rw-r--r--src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java82
-rw-r--r--src/main/java/org/traccar/geocoder/NominatimGeocoder.java (renamed from src/org/traccar/geocoder/NominatimGeocoder.java)12
-rw-r--r--src/main/java/org/traccar/geocoder/OpenCageGeocoder.java (renamed from src/org/traccar/geocoder/OpenCageGeocoder.java)9
-rw-r--r--src/main/java/org/traccar/geofence/GeofenceCircle.java (renamed from src/org/traccar/geofence/GeofenceCircle.java)6
-rw-r--r--src/main/java/org/traccar/geofence/GeofenceGeometry.java (renamed from src/org/traccar/geofence/GeofenceGeometry.java)0
-rw-r--r--src/main/java/org/traccar/geofence/GeofencePolygon.java (renamed from src/org/traccar/geofence/GeofencePolygon.java)0
-rw-r--r--src/main/java/org/traccar/geofence/GeofencePolyline.java (renamed from src/org/traccar/geofence/GeofencePolyline.java)0
-rw-r--r--src/main/java/org/traccar/geolocation/GeolocationException.java (renamed from src/org/traccar/geolocation/GeolocationException.java)0
-rw-r--r--src/main/java/org/traccar/geolocation/GeolocationProvider.java (renamed from src/org/traccar/geolocation/GeolocationProvider.java)0
-rw-r--r--src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java (renamed from src/org/traccar/geolocation/GoogleGeolocationProvider.java)0
-rw-r--r--src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java (renamed from src/org/traccar/geolocation/MozillaGeolocationProvider.java)0
-rw-r--r--src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java (renamed from src/org/traccar/geolocation/OpenCellIdGeolocationProvider.java)38
-rw-r--r--src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java58
-rw-r--r--src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java (renamed from src/org/traccar/geolocation/UnwiredGeolocationProvider.java)61
-rw-r--r--src/main/java/org/traccar/handler/ComputedAttributesHandler.java (renamed from src/org/traccar/processing/ComputedAttributesHandler.java)58
-rw-r--r--src/main/java/org/traccar/handler/CopyAttributesHandler.java (renamed from src/org/traccar/processing/CopyAttributesHandler.java)23
-rw-r--r--src/main/java/org/traccar/handler/DefaultDataHandler.java48
-rw-r--r--src/main/java/org/traccar/handler/DistanceHandler.java (renamed from src/org/traccar/DistanceHandler.java)30
-rw-r--r--src/main/java/org/traccar/handler/EngineHoursHandler.java50
-rw-r--r--src/main/java/org/traccar/handler/FilterHandler.java (renamed from src/org/traccar/FilterHandler.java)122
-rw-r--r--src/main/java/org/traccar/handler/GeocoderHandler.java94
-rw-r--r--src/main/java/org/traccar/handler/GeolocationHandler.java (renamed from src/org/traccar/GeolocationHandler.java)54
-rw-r--r--src/main/java/org/traccar/handler/HemisphereHandler.java (renamed from src/org/traccar/HemisphereHandler.java)40
-rw-r--r--src/main/java/org/traccar/handler/MotionHandler.java (renamed from src/org/traccar/MotionHandler.java)7
-rw-r--r--src/main/java/org/traccar/handler/NetworkMessageHandler.java57
-rw-r--r--src/main/java/org/traccar/handler/OpenChannelHandler.java42
-rw-r--r--src/main/java/org/traccar/handler/RemoteAddressHandler.java (renamed from src/org/traccar/RemoteAddressHandler.java)20
-rw-r--r--src/main/java/org/traccar/handler/StandardLoggingHandler.java87
-rw-r--r--src/main/java/org/traccar/handler/TimeHandler.java63
-rw-r--r--src/main/java/org/traccar/handler/events/AlertEventHandler.java59
-rw-r--r--src/main/java/org/traccar/handler/events/BaseEventHandler.java (renamed from src/org/traccar/BaseEventHandler.java)5
-rw-r--r--src/main/java/org/traccar/handler/events/CommandResultEventHandler.java (renamed from src/org/traccar/events/CommandResultEventHandler.java)5
-rw-r--r--src/main/java/org/traccar/handler/events/DriverEventHandler.java (renamed from src/org/traccar/events/DriverEventHandler.java)19
-rw-r--r--src/main/java/org/traccar/handler/events/FuelDropEventHandler.java (renamed from src/org/traccar/events/FuelDropEventHandler.java)25
-rw-r--r--src/main/java/org/traccar/handler/events/GeofenceEventHandler.java (renamed from src/org/traccar/events/GeofenceEventHandler.java)38
-rw-r--r--src/main/java/org/traccar/handler/events/IgnitionEventHandler.java (renamed from src/org/traccar/events/IgnitionEventHandler.java)21
-rw-r--r--src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java72
-rw-r--r--src/main/java/org/traccar/handler/events/MotionEventHandler.java (renamed from src/org/traccar/events/MotionEventHandler.java)26
-rw-r--r--src/main/java/org/traccar/handler/events/OverspeedEventHandler.java (renamed from src/org/traccar/events/OverspeedEventHandler.java)77
-rw-r--r--src/main/java/org/traccar/handler/protocol/Arnavi4FrameDecoder.java (renamed from src/org/traccar/protocol/Arnavi4FrameDecoder.java)0
-rw-r--r--src/main/java/org/traccar/handler/protocol/Arnavi4Protocol.java (renamed from src/org/traccar/protocol/Arnavi4Protocol.java)0
-rw-r--r--src/main/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoder.java (renamed from src/org/traccar/protocol/Arnavi4ProtocolDecoder.java)0
-rw-r--r--src/main/java/org/traccar/helper/BcdUtil.java (renamed from src/org/traccar/helper/BcdUtil.java)8
-rw-r--r--src/main/java/org/traccar/helper/BitBuffer.java (renamed from src/org/traccar/helper/BitBuffer.java)12
-rw-r--r--src/main/java/org/traccar/helper/BitUtil.java (renamed from src/org/traccar/helper/BitUtil.java)0
-rw-r--r--src/main/java/org/traccar/helper/BufferUtil.java47
-rw-r--r--src/main/java/org/traccar/helper/Checksum.java200
-rw-r--r--src/main/java/org/traccar/helper/DataConverter.java47
-rw-r--r--src/main/java/org/traccar/helper/DateBuilder.java (renamed from src/org/traccar/helper/DateBuilder.java)4
-rw-r--r--src/main/java/org/traccar/helper/DateUtil.java (renamed from src/org/traccar/helper/DateUtil.java)24
-rw-r--r--src/main/java/org/traccar/helper/DistanceCalculator.java (renamed from src/org/traccar/helper/DistanceCalculator.java)0
-rw-r--r--src/main/java/org/traccar/helper/Hashing.java (renamed from src/org/traccar/helper/Hashing.java)9
-rw-r--r--src/main/java/org/traccar/helper/LocationTree.java (renamed from src/org/traccar/helper/LocationTree.java)0
-rw-r--r--src/main/java/org/traccar/helper/Log.java271
-rw-r--r--src/main/java/org/traccar/helper/LogAction.java107
-rw-r--r--src/main/java/org/traccar/helper/ObdDecoder.java (renamed from src/org/traccar/helper/ObdDecoder.java)31
-rw-r--r--src/main/java/org/traccar/helper/Parser.java (renamed from src/org/traccar/helper/Parser.java)0
-rw-r--r--src/main/java/org/traccar/helper/PatternBuilder.java (renamed from src/org/traccar/helper/PatternBuilder.java)2
-rw-r--r--src/main/java/org/traccar/helper/PatternUtil.java (renamed from src/org/traccar/helper/PatternUtil.java)7
-rw-r--r--src/main/java/org/traccar/helper/SanitizerModule.java45
-rw-r--r--src/main/java/org/traccar/helper/ServletHelper.java45
-rw-r--r--src/main/java/org/traccar/helper/UnitsConverter.java (renamed from src/org/traccar/helper/UnitsConverter.java)14
-rw-r--r--src/main/java/org/traccar/model/Attribute.java (renamed from src/org/traccar/model/Attribute.java)0
-rw-r--r--src/main/java/org/traccar/model/BaseModel.java (renamed from src/org/traccar/model/BaseModel.java)0
-rw-r--r--src/main/java/org/traccar/model/Calendar.java (renamed from src/org/traccar/model/Calendar.java)0
-rw-r--r--src/main/java/org/traccar/model/CellTower.java (renamed from src/org/traccar/model/CellTower.java)0
-rw-r--r--src/main/java/org/traccar/model/Command.java (renamed from src/org/traccar/model/Command.java)3
-rw-r--r--src/main/java/org/traccar/model/Device.java (renamed from src/org/traccar/model/Device.java)24
-rw-r--r--src/main/java/org/traccar/model/DeviceAccumulators.java (renamed from src/org/traccar/model/DeviceTotalDistance.java)22
-rw-r--r--src/main/java/org/traccar/model/DeviceState.java (renamed from src/org/traccar/model/DeviceState.java)10
-rw-r--r--src/main/java/org/traccar/model/Driver.java (renamed from src/org/traccar/model/Driver.java)0
-rw-r--r--src/main/java/org/traccar/model/Event.java (renamed from src/org/traccar/model/Event.java)22
-rw-r--r--src/main/java/org/traccar/model/ExtendedModel.java (renamed from src/org/traccar/model/ExtendedModel.java)0
-rw-r--r--src/main/java/org/traccar/model/Geofence.java (renamed from src/org/traccar/model/Geofence.java)16
-rw-r--r--src/main/java/org/traccar/model/Group.java30
-rw-r--r--src/main/java/org/traccar/model/GroupedModel.java (renamed from src/org/traccar/model/Group.java)15
-rw-r--r--src/main/java/org/traccar/model/Maintenance.java (renamed from src/org/traccar/model/Notification.java)44
-rw-r--r--src/main/java/org/traccar/model/ManagedUser.java (renamed from src/org/traccar/model/ManagedUser.java)0
-rw-r--r--src/main/java/org/traccar/model/Message.java (renamed from src/org/traccar/model/Message.java)0
-rw-r--r--src/main/java/org/traccar/model/MiscFormatter.java (renamed from src/org/traccar/model/MiscFormatter.java)0
-rw-r--r--src/main/java/org/traccar/model/Network.java (renamed from src/org/traccar/model/Network.java)6
-rw-r--r--src/main/java/org/traccar/model/Notification.java72
-rw-r--r--src/main/java/org/traccar/model/Permission.java (renamed from src/org/traccar/model/Permission.java)0
-rw-r--r--src/main/java/org/traccar/model/Position.java (renamed from src/org/traccar/model/Position.java)60
-rw-r--r--src/main/java/org/traccar/model/ScheduledModel.java30
-rw-r--r--src/main/java/org/traccar/model/Server.java (renamed from src/org/traccar/model/Server.java)15
-rw-r--r--src/main/java/org/traccar/model/Statistics.java (renamed from src/org/traccar/model/Statistics.java)12
-rw-r--r--src/main/java/org/traccar/model/Typed.java (renamed from src/org/traccar/model/Typed.java)0
-rw-r--r--src/main/java/org/traccar/model/User.java (renamed from src/org/traccar/model/User.java)46
-rw-r--r--src/main/java/org/traccar/model/WifiAccessPoint.java (renamed from src/org/traccar/model/WifiAccessPoint.java)0
-rw-r--r--src/main/java/org/traccar/notification/EventForwarder.java91
-rw-r--r--src/main/java/org/traccar/notification/FullMessage.java (renamed from src/org/traccar/notification/MailMessage.java)4
-rw-r--r--src/main/java/org/traccar/notification/JsonTypeEventForwarder.java18
-rw-r--r--src/main/java/org/traccar/notification/MessageException.java29
-rw-r--r--src/main/java/org/traccar/notification/NotificationFormatter.java (renamed from src/org/traccar/notification/NotificationFormatter.java)54
-rw-r--r--src/main/java/org/traccar/notification/NotificatorManager.java94
-rw-r--r--src/main/java/org/traccar/notification/PropertiesProvider.java (renamed from src/org/traccar/notification/PropertiesProvider.java)19
-rw-r--r--src/main/java/org/traccar/notificators/Notificator.java44
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorFirebase.java87
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorMail.java40
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorNull.java38
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorSms.java62
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTelegram.java77
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorWeb.java (renamed from src/org/traccar/DefaultDataHandler.java)21
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocolDecoder.java (renamed from src/org/traccar/protocol/AdmProtocolDecoder.java)93
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocolEncoder.java (renamed from src/org/traccar/protocol/AdmProtocolEncoder.java)16
-rw-r--r--src/main/java/org/traccar/protocol/AisProtocol.java (renamed from src/org/traccar/protocol/AisProtocol.java)22
-rw-r--r--src/main/java/org/traccar/protocol/AisProtocolDecoder.java (renamed from src/org/traccar/protocol/AisProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/AlematicsFrameDecoder.java (renamed from src/org/traccar/protocol/AlematicsFrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/AlematicsProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java (renamed from src/org/traccar/protocol/AlematicsProtocolDecoder.java)14
-rw-r--r--src/main/java/org/traccar/protocol/AnytrekProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java120
-rw-r--r--src/main/java/org/traccar/protocol/ApelProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/ApelProtocolDecoder.java (renamed from src/org/traccar/protocol/ApelProtocolDecoder.java)92
-rw-r--r--src/main/java/org/traccar/protocol/AplicomFrameDecoder.java (renamed from src/org/traccar/protocol/AplicomFrameDecoder.java)124
-rw-r--r--src/main/java/org/traccar/protocol/AplicomProtocol.java (renamed from src/org/traccar/protocol/AplicomProtocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java (renamed from src/org/traccar/protocol/AplicomProtocolDecoder.java)172
-rw-r--r--src/main/java/org/traccar/protocol/AppelloProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java (renamed from src/org/traccar/protocol/AppelloProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/AppletProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/AppletProtocolDecoder.java48
-rw-r--r--src/main/java/org/traccar/protocol/AquilaProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java (renamed from src/org/traccar/protocol/AquilaProtocolDecoder.java)36
-rw-r--r--src/main/java/org/traccar/protocol/Ardi01Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java (renamed from src/org/traccar/protocol/Ardi01ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/ArknavProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java (renamed from src/org/traccar/protocol/ArknavProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/ArknavX8Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java (renamed from src/org/traccar/protocol/ArknavX8ProtocolDecoder.java)65
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java (renamed from src/org/traccar/protocol/ArnaviProtocolDecoder.java)36
-rw-r--r--src/main/java/org/traccar/protocol/AstraProtocol.java41
-rw-r--r--src/main/java/org/traccar/protocol/AstraProtocolDecoder.java (renamed from src/org/traccar/protocol/AstraProtocolDecoder.java)30
-rw-r--r--src/main/java/org/traccar/protocol/At2000FrameDecoder.java (renamed from src/org/traccar/protocol/At2000FrameDecoder.java)33
-rw-r--r--src/main/java/org/traccar/protocol/At2000Protocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java (renamed from src/org/traccar/protocol/At2000ProtocolDecoder.java)95
-rw-r--r--src/main/java/org/traccar/protocol/AtrackFrameDecoder.java80
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocol.java45
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java620
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolEncoder.java (renamed from src/org/traccar/protocol/AtrackProtocolEncoder.java)17
-rw-r--r--src/main/java/org/traccar/protocol/AuroProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/AuroProtocolDecoder.java (renamed from src/org/traccar/protocol/AuroProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/AustinNbProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java81
-rw-r--r--src/main/java/org/traccar/protocol/AutoFonFrameDecoder.java (renamed from src/org/traccar/protocol/AutoFonFrameDecoder.java)16
-rw-r--r--src/main/java/org/traccar/protocol/AutoFonProtocol.java (renamed from src/org/traccar/protocol/AutoFonProtocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java (renamed from src/org/traccar/protocol/AutoFonProtocolDecoder.java)27
-rw-r--r--src/main/java/org/traccar/protocol/AutoGradeProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java (renamed from src/org/traccar/protocol/AutoGradeProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/AutoTrackProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java138
-rw-r--r--src/main/java/org/traccar/protocol/AvemaProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java110
-rw-r--r--src/main/java/org/traccar/protocol/Avl301Protocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java (renamed from src/org/traccar/protocol/Avl301ProtocolDecoder.java)23
-rw-r--r--src/main/java/org/traccar/protocol/BceFrameDecoder.java (renamed from src/org/traccar/protocol/BceFrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java336
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolEncoder.java52
-rw-r--r--src/main/java/org/traccar/protocol/BlackKiteProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java (renamed from src/org/traccar/protocol/BlackKiteProtocolDecoder.java)62
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocolDecoder.java142
-rw-r--r--src/main/java/org/traccar/protocol/BoxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/BoxProtocolDecoder.java (renamed from src/org/traccar/protocol/BoxProtocolDecoder.java)43
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java121
-rw-r--r--src/main/java/org/traccar/protocol/CalAmpProtocol.java (renamed from src/org/traccar/protocol/CalAmpProtocol.java)18
-rw-r--r--src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java (renamed from src/org/traccar/protocol/CalAmpProtocolDecoder.java)26
-rw-r--r--src/main/java/org/traccar/protocol/CarTrackProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java (renamed from src/org/traccar/protocol/CarTrackProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocol.java (renamed from src/org/traccar/protocol/CarcellProtocol.java)30
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java (renamed from src/org/traccar/protocol/CarcellProtocolDecoder.java)12
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java (renamed from src/org/traccar/protocol/CarcellProtocolEncoder.java)17
-rw-r--r--src/main/java/org/traccar/protocol/CarscopProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java (renamed from src/org/traccar/protocol/CarscopProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocol.java48
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolDecoder.java609
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolEncoder.java75
-rw-r--r--src/main/java/org/traccar/protocol/CautelaProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java78
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorFrameDecoder.java (renamed from src/org/traccar/protocol/CellocatorFrameDecoder.java)37
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocol.java45
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java266
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocolEncoder.java (renamed from src/org/traccar/protocol/CellocatorProtocolEncoder.java)58
-rw-r--r--src/main/java/org/traccar/protocol/CguardProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/CguardProtocolDecoder.java (renamed from src/org/traccar/protocol/CguardProtocolDecoder.java)19
-rw-r--r--src/main/java/org/traccar/protocol/CityeasyProtocol.java (renamed from src/org/traccar/protocol/CityeasyProtocol.java)24
-rw-r--r--src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java (renamed from src/org/traccar/protocol/CityeasyProtocolDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/CityeasyProtocolEncoder.java (renamed from src/org/traccar/protocol/CityeasyProtocolEncoder.java)25
-rw-r--r--src/main/java/org/traccar/protocol/ContinentalProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java114
-rw-r--r--src/main/java/org/traccar/protocol/CradlepointProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java (renamed from src/org/traccar/protocol/CradlepointProtocolDecoder.java)16
-rw-r--r--src/main/java/org/traccar/protocol/DishaProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/DishaProtocolDecoder.java (renamed from src/org/traccar/protocol/DishaProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java (renamed from src/org/traccar/protocol/DmtHttpProtocolDecoder.java)34
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocolDecoder.java300
-rw-r--r--src/main/java/org/traccar/protocol/DwayProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/DwayProtocolDecoder.java (renamed from src/org/traccar/protocol/DwayProtocolDecoder.java)15
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java (renamed from src/org/traccar/protocol/EasyTrackProtocolDecoder.java)40
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocol.java (renamed from src/org/traccar/protocol/EelinkProtocol.java)32
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java (renamed from src/org/traccar/protocol/EelinkProtocolDecoder.java)235
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolEncoder.java109
-rw-r--r--src/main/java/org/traccar/protocol/EgtsFrameDecoder.java45
-rw-r--r--src/main/java/org/traccar/protocol/EgtsProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java283
-rw-r--r--src/main/java/org/traccar/protocol/EnforaProtocol.java (renamed from src/org/traccar/protocol/EnforaProtocol.java)31
-rw-r--r--src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java (renamed from src/org/traccar/protocol/EnforaProtocolDecoder.java)42
-rw-r--r--src/main/java/org/traccar/protocol/EnforaProtocolEncoder.java (renamed from src/org/traccar/protocol/EnforaProtocolEncoder.java)21
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocol.java45
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocolDecoder.java158
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocolEncoder.java46
-rw-r--r--src/main/java/org/traccar/protocol/EskyFrameDecoder.java (renamed from src/org/traccar/protocol/EskyFrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocolDecoder.java (renamed from src/org/traccar/protocol/EskyProtocolDecoder.java)43
-rw-r--r--src/main/java/org/traccar/protocol/ExtremTracProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java (renamed from src/org/traccar/protocol/ExtremTracProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocol.java (renamed from src/org/traccar/protocol/FifotrackProtocol.java)30
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java236
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolEncoder.java50
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java247
-rw-r--r--src/main/java/org/traccar/protocol/FlexCommProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java (renamed from src/org/traccar/protocol/FlexCommProtocolDecoder.java)13
-rw-r--r--src/main/java/org/traccar/protocol/FlextrackProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java (renamed from src/org/traccar/protocol/FlextrackProtocolDecoder.java)19
-rw-r--r--src/main/java/org/traccar/protocol/FoxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/FoxProtocolDecoder.java (renamed from src/org/traccar/protocol/FoxProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/FreedomProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java (renamed from src/org/traccar/protocol/FreedomProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java184
-rw-r--r--src/main/java/org/traccar/protocol/GalileoFrameDecoder.java (renamed from src/org/traccar/protocol/GalileoFrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocol.java (renamed from src/org/traccar/protocol/GalileoProtocol.java)27
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java (renamed from src/org/traccar/protocol/GalileoProtocolDecoder.java)241
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolEncoder.java (renamed from src/org/traccar/protocol/GalileoProtocolEncoder.java)30
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocol.java41
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolDecoder.java (renamed from src/org/traccar/protocol/GatorProtocolDecoder.java)31
-rw-r--r--src/main/java/org/traccar/protocol/GenxProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/GenxProtocolDecoder.java (renamed from src/org/traccar/protocol/GenxProtocolDecoder.java)11
-rw-r--r--src/main/java/org/traccar/protocol/Gl100Protocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java (renamed from src/org/traccar/protocol/Gl100ProtocolDecoder.java)13
-rw-r--r--src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java (renamed from src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java)37
-rw-r--r--src/main/java/org/traccar/protocol/Gl200FrameDecoder.java (renamed from src/org/traccar/protocol/Gl200FrameDecoder.java)21
-rw-r--r--src/main/java/org/traccar/protocol/Gl200Protocol.java53
-rw-r--r--src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java (renamed from src/org/traccar/protocol/Gl200ProtocolDecoder.java)12
-rw-r--r--src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java (renamed from src/org/traccar/protocol/Gl200ProtocolEncoder.java)23
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java (renamed from src/org/traccar/protocol/Gl200TextProtocolDecoder.java)541
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java (renamed from src/org/traccar/protocol/GlobalSatProtocolDecoder.java)106
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java163
-rw-r--r--src/main/java/org/traccar/protocol/GnxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GnxProtocolDecoder.java (renamed from src/org/traccar/protocol/GnxProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java261
-rw-r--r--src/main/java/org/traccar/protocol/GotopProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GotopProtocolDecoder.java (renamed from src/org/traccar/protocol/GotopProtocolDecoder.java)12
-rw-r--r--src/main/java/org/traccar/protocol/Gps056FrameDecoder.java (renamed from src/org/traccar/protocol/Gps056FrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/Gps056Protocol.java (renamed from src/org/traccar/protocol/Gps056Protocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java (renamed from src/org/traccar/protocol/Gps056ProtocolDecoder.java)46
-rw-r--r--src/main/java/org/traccar/protocol/Gps103Protocol.java60
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java422
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java (renamed from src/org/traccar/protocol/Gps103ProtocolEncoder.java)31
-rw-r--r--src/main/java/org/traccar/protocol/GpsGateProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java (renamed from src/org/traccar/protocol/GpsGateProtocolDecoder.java)30
-rw-r--r--src/main/java/org/traccar/protocol/GpsMarkerProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java (renamed from src/org/traccar/protocol/GpsMarkerProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/GpsmtaProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java (renamed from src/org/traccar/protocol/GpsmtaProtocolDecoder.java)39
-rw-r--r--src/main/java/org/traccar/protocol/GranitFrameDecoder.java (renamed from src/org/traccar/protocol/GranitFrameDecoder.java)26
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocol.java (renamed from src/org/traccar/protocol/GranitProtocol.java)31
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolDecoder.java (renamed from src/org/traccar/protocol/GranitProtocolDecoder.java)75
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolEncoder.java (renamed from src/org/traccar/protocol/GranitProtocolEncoder.java)42
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java (renamed from src/org/traccar/protocol/GranitProtocolSmsEncoder.java)11
-rw-r--r--src/main/java/org/traccar/protocol/Gt02Protocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java (renamed from src/org/traccar/protocol/Gt02ProtocolDecoder.java)24
-rw-r--r--src/main/java/org/traccar/protocol/Gt06FrameDecoder.java (renamed from src/org/traccar/protocol/Gt06FrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/Gt06Protocol.java (renamed from src/org/traccar/protocol/Gt06Protocol.java)22
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java1199
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java (renamed from src/org/traccar/protocol/Gt06ProtocolEncoder.java)45
-rw-r--r--src/main/java/org/traccar/protocol/Gt30Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java (renamed from src/org/traccar/protocol/Gt30ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/H02FrameDecoder.java (renamed from src/org/traccar/protocol/H02FrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/H02Protocol.java (renamed from src/org/traccar/protocol/H02Protocol.java)37
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java (renamed from src/org/traccar/protocol/H02ProtocolDecoder.java)200
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolEncoder.java (renamed from src/org/traccar/protocol/H02ProtocolEncoder.java)42
-rw-r--r--src/main/java/org/traccar/protocol/HaicomProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java (renamed from src/org/traccar/protocol/HaicomProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/HomtecsProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java (renamed from src/org/traccar/protocol/HomtecsProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengFrameDecoder.java (renamed from src/org/traccar/protocol/HuaShengFrameDecoder.java)20
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocol.java (renamed from src/org/traccar/protocol/HuaShengProtocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java (renamed from src/org/traccar/protocol/HuaShengProtocolDecoder.java)86
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoFrameDecoder.java (renamed from src/org/traccar/protocol/HuabaoFrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocol.java (renamed from src/org/traccar/protocol/HuabaoProtocol.java)22
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java299
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java78
-rw-r--r--src/main/java/org/traccar/protocol/HunterProProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java (renamed from src/org/traccar/protocol/HunterProProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/IdplProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/IdplProtocolDecoder.java (renamed from src/org/traccar/protocol/IdplProtocolDecoder.java)7
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracFrameDecoder.java (renamed from src/org/traccar/protocol/IntellitracFrameDecoder.java)20
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java (renamed from src/org/traccar/protocol/IntellitracProtocolDecoder.java)57
-rw-r--r--src/main/java/org/traccar/protocol/ItsFrameDecoder.java67
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocol.java42
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java239
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolEncoder.java41
-rw-r--r--src/main/java/org/traccar/protocol/Ivt401Protocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java183
-rw-r--r--src/main/java/org/traccar/protocol/JpKorjarFrameDecoder.java (renamed from src/org/traccar/protocol/JpKorjarFrameDecoder.java)15
-rw-r--r--src/main/java/org/traccar/protocol/JpKorjarProtocol.java (renamed from src/org/traccar/protocol/JpKorjarProtocol.java)24
-rw-r--r--src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java (renamed from src/org/traccar/protocol/JpKorjarProtocolDecoder.java)178
-rw-r--r--src/main/java/org/traccar/protocol/Jt600FrameDecoder.java (renamed from src/org/traccar/protocol/Jt600FrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/Jt600Protocol.java (renamed from src/org/traccar/protocol/Jt600Protocol.java)26
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java (renamed from src/org/traccar/protocol/Jt600ProtocolDecoder.java)107
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolEncoder.java (renamed from src/org/traccar/protocol/Jt600ProtocolEncoder.java)15
-rw-r--r--src/main/java/org/traccar/protocol/KenjiProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java (renamed from src/org/traccar/protocol/KenjiProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocol.java (renamed from src/org/traccar/protocol/KhdProtocol.java)24
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolDecoder.java (renamed from src/org/traccar/protocol/KhdProtocolDecoder.java)71
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolEncoder.java (renamed from src/org/traccar/protocol/KhdProtocolEncoder.java)36
-rw-r--r--src/main/java/org/traccar/protocol/L100FrameDecoder.java90
-rw-r--r--src/main/java/org/traccar/protocol/L100Protocol.java (renamed from src/org/traccar/protocol/L100Protocol.java)24
-rw-r--r--src/main/java/org/traccar/protocol/L100ProtocolDecoder.java341
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java283
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java57
-rw-r--r--src/main/java/org/traccar/protocol/LeafSpyProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java139
-rw-r--r--src/main/java/org/traccar/protocol/M2cProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/M2cProtocolDecoder.java131
-rw-r--r--src/main/java/org/traccar/protocol/M2mProtocol.java (renamed from src/org/traccar/protocol/M2mProtocol.java)22
-rw-r--r--src/main/java/org/traccar/protocol/M2mProtocolDecoder.java (renamed from src/org/traccar/protocol/M2mProtocolDecoder.java)14
-rw-r--r--src/main/java/org/traccar/protocol/MaestroProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java (renamed from src/org/traccar/protocol/MaestroProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/ManPowerProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java (renamed from src/org/traccar/protocol/ManPowerProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/MegastekFrameDecoder.java (renamed from src/org/traccar/protocol/MegastekFrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java (renamed from src/org/traccar/protocol/MegastekProtocolDecoder.java)102
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoFrameDecoder.java (renamed from src/org/traccar/protocol/MeiligaoFrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocol.java (renamed from src/org/traccar/protocol/MeiligaoProtocol.java)32
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java (renamed from src/org/traccar/protocol/MeiligaoProtocolDecoder.java)179
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java (renamed from src/org/traccar/protocol/MeiligaoProtocolEncoder.java)50
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackFrameDecoder.java (renamed from src/org/traccar/protocol/MeitrackFrameDecoder.java)16
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocol.java54
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java (renamed from src/org/traccar/protocol/MeitrackProtocolDecoder.java)181
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java (renamed from src/org/traccar/protocol/MeitrackProtocolEncoder.java)24
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java181
-rw-r--r--src/main/java/org/traccar/protocol/MilesmateProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java108
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocol.java (renamed from src/org/traccar/protocol/MiniFinderProtocol.java)30
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java (renamed from src/org/traccar/protocol/MiniFinderProtocolDecoder.java)24
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java87
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2Protocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java196
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocolDecoder.java85
-rw-r--r--src/main/java/org/traccar/protocol/Mta6Protocol.java (renamed from src/org/traccar/protocol/Mta6Protocol.java)28
-rw-r--r--src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java (renamed from src/org/traccar/protocol/Mta6ProtocolDecoder.java)69
-rw-r--r--src/main/java/org/traccar/protocol/MtxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/MtxProtocolDecoder.java (renamed from src/org/traccar/protocol/MtxProtocolDecoder.java)13
-rw-r--r--src/main/java/org/traccar/protocol/MxtFrameDecoder.java (renamed from src/org/traccar/protocol/MxtFrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/MxtProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/MxtProtocolDecoder.java (renamed from src/org/traccar/protocol/MxtProtocolDecoder.java)65
-rw-r--r--src/main/java/org/traccar/protocol/NavigilFrameDecoder.java (renamed from src/org/traccar/protocol/NavigilFrameDecoder.java)24
-rw-r--r--src/main/java/org/traccar/protocol/NavigilProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java (renamed from src/org/traccar/protocol/NavigilProtocolDecoder.java)185
-rw-r--r--src/main/java/org/traccar/protocol/NavisFrameDecoder.java109
-rw-r--r--src/main/java/org/traccar/protocol/NavisProtocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/NavisProtocolDecoder.java683
-rw-r--r--src/main/java/org/traccar/protocol/NavisetFrameDecoder.java39
-rw-r--r--src/main/java/org/traccar/protocol/NavisetProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java249
-rw-r--r--src/main/java/org/traccar/protocol/NeosProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/NeosProtocolDecoder.java98
-rw-r--r--src/main/java/org/traccar/protocol/NoranProtocol.java (renamed from src/org/traccar/protocol/NoranProtocol.java)25
-rw-r--r--src/main/java/org/traccar/protocol/NoranProtocolDecoder.java (renamed from src/org/traccar/protocol/NoranProtocolDecoder.java)64
-rw-r--r--src/main/java/org/traccar/protocol/NoranProtocolEncoder.java (renamed from src/org/traccar/protocol/NoranProtocolEncoder.java)34
-rw-r--r--src/main/java/org/traccar/protocol/NvsFrameDecoder.java (renamed from src/org/traccar/protocol/NvsFrameDecoder.java)18
-rw-r--r--src/main/java/org/traccar/protocol/NvsProtocol.java (renamed from src/org/traccar/protocol/NvsProtocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/NvsProtocolDecoder.java (renamed from src/org/traccar/protocol/NvsProtocolDecoder.java)26
-rw-r--r--src/main/java/org/traccar/protocol/NyitechProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java170
-rw-r--r--src/main/java/org/traccar/protocol/ObdDongleProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java (renamed from src/org/traccar/protocol/ObdDongleProtocolDecoder.java)30
-rw-r--r--src/main/java/org/traccar/protocol/OigoProtocol.java (renamed from src/org/traccar/protocol/OigoProtocol.java)18
-rw-r--r--src/main/java/org/traccar/protocol/OigoProtocolDecoder.java (renamed from src/org/traccar/protocol/OigoProtocolDecoder.java)35
-rw-r--r--src/main/java/org/traccar/protocol/OkoProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/OkoProtocolDecoder.java100
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java142
-rw-r--r--src/main/java/org/traccar/protocol/OpenGtsProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java115
-rw-r--r--src/main/java/org/traccar/protocol/OrionFrameDecoder.java (renamed from src/org/traccar/protocol/OrionFrameDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/OrionProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/OrionProtocolDecoder.java (renamed from src/org/traccar/protocol/OrionProtocolDecoder.java)35
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java184
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java69
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocol.java (renamed from src/org/traccar/protocol/OwnTracksProtocol.java)28
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java215
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java152
-rw-r--r--src/main/java/org/traccar/protocol/PathAwayProtocol.java (renamed from src/org/traccar/protocol/PathAwayProtocol.java)28
-rw-r--r--src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java (renamed from src/org/traccar/protocol/PathAwayProtocolDecoder.java)37
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java (renamed from src/org/traccar/protocol/PiligrimProtocolDecoder.java)47
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocolDecoder.java139
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java (renamed from src/org/traccar/protocol/PretraceProtocolDecoder.java)50
-rw-r--r--src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/PricolProtocol.java41
-rw-r--r--src/main/java/org/traccar/protocol/PricolProtocolDecoder.java (renamed from src/org/traccar/protocol/PricolProtocolDecoder.java)22
-rw-r--r--src/main/java/org/traccar/protocol/ProgressProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java (renamed from src/org/traccar/protocol/ProgressProtocolDecoder.java)84
-rw-r--r--src/main/java/org/traccar/protocol/PstFrameDecoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocolDecoder.java116
-rw-r--r--src/main/java/org/traccar/protocol/Pt215Protocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java118
-rw-r--r--src/main/java/org/traccar/protocol/Pt3000Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java (renamed from src/org/traccar/protocol/Pt3000ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/Pt502FrameDecoder.java73
-rw-r--r--src/main/java/org/traccar/protocol/Pt502Protocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java (renamed from src/org/traccar/protocol/Pt502ProtocolDecoder.java)360
-rw-r--r--src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java (renamed from src/org/traccar/protocol/Pt502ProtocolEncoder.java)21
-rw-r--r--src/main/java/org/traccar/protocol/Pt60Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java184
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java171
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocolDecoder.java197
-rw-r--r--src/main/java/org/traccar/protocol/RaveonProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java (renamed from src/org/traccar/protocol/RaveonProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/RecodaProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java (renamed from src/org/traccar/protocol/RecodaProtocolDecoder.java)34
-rw-r--r--src/main/java/org/traccar/protocol/RetranslatorFrameDecoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/RetranslatorProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java114
-rw-r--r--src/main/java/org/traccar/protocol/RitiProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/RitiProtocolDecoder.java (renamed from src/org/traccar/protocol/RitiProtocolDecoder.java)24
-rw-r--r--src/main/java/org/traccar/protocol/RoboTrackFrameDecoder.java57
-rw-r--r--src/main/java/org/traccar/protocol/RoboTrackProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java131
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java132
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocol.java (renamed from src/org/traccar/protocol/RuptelaProtocol.java)25
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java (renamed from src/org/traccar/protocol/RuptelaProtocolDecoder.java)88
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java (renamed from src/org/traccar/protocol/RuptelaProtocolEncoder.java)42
-rw-r--r--src/main/java/org/traccar/protocol/S168Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/S168ProtocolDecoder.java81
-rw-r--r--src/main/java/org/traccar/protocol/SabertekFrameDecoder.java44
-rw-r--r--src/main/java/org/traccar/protocol/SabertekProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java135
-rw-r--r--src/main/java/org/traccar/protocol/SanavProtocol.java45
-rw-r--r--src/main/java/org/traccar/protocol/SanavProtocolDecoder.java (renamed from src/org/traccar/protocol/SanavProtocolDecoder.java)41
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/SanulProtocolDecoder.java97
-rw-r--r--src/main/java/org/traccar/protocol/SatsolProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java104
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java219
-rw-r--r--src/main/java/org/traccar/protocol/SiwiProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java (renamed from src/org/traccar/protocol/SiwiProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/SkypatrolProtocol.java (renamed from src/org/traccar/protocol/SkypatrolProtocol.java)16
-rw-r--r--src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java (renamed from src/org/traccar/protocol/SkypatrolProtocolDecoder.java)21
-rw-r--r--src/main/java/org/traccar/protocol/SmartSoleProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java91
-rw-r--r--src/main/java/org/traccar/protocol/SmokeyProtocol.java (renamed from src/org/traccar/protocol/SmokeyProtocol.java)18
-rw-r--r--src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java (renamed from src/org/traccar/protocol/SmokeyProtocolDecoder.java)39
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java97
-rw-r--r--src/main/java/org/traccar/protocol/SpotProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/SpotProtocolDecoder.java102
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java (renamed from src/org/traccar/protocol/StarLinkProtocolDecoder.java)64
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java122
-rw-r--r--src/main/java/org/traccar/protocol/Stl060FrameDecoder.java (renamed from src/org/traccar/protocol/Stl060FrameDecoder.java)14
-rw-r--r--src/main/java/org/traccar/protocol/Stl060Protocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java (renamed from src/org/traccar/protocol/Stl060ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/SuntechFrameDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocol.java (renamed from src/org/traccar/protocol/SuntechProtocol.java)29
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java654
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java78
-rw-r--r--src/main/java/org/traccar/protocol/SupermateProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java (renamed from src/org/traccar/protocol/SupermateProtocolDecoder.java)16
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocol.java51
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocolDecoder.java105
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocolEncoder.java53
-rw-r--r--src/main/java/org/traccar/protocol/T55Protocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java (renamed from src/org/traccar/protocol/T55ProtocolDecoder.java)74
-rw-r--r--src/main/java/org/traccar/protocol/T57FrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/T57Protocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/T57ProtocolDecoder.java84
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocol.java (renamed from src/org/traccar/protocol/T800xProtocol.java)24
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java314
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolEncoder.java (renamed from src/org/traccar/protocol/T800xProtocolEncoder.java)38
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/TaipProtocolDecoder.java (renamed from src/org/traccar/protocol/TaipProtocolDecoder.java)103
-rw-r--r--src/main/java/org/traccar/protocol/TechTltProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java132
-rw-r--r--src/main/java/org/traccar/protocol/TekFrameDecoder.java42
-rw-r--r--src/main/java/org/traccar/protocol/TekProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/TekProtocolDecoder.java139
-rw-r--r--src/main/java/org/traccar/protocol/TelemaxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java112
-rw-r--r--src/main/java/org/traccar/protocol/TelicFrameDecoder.java (renamed from src/org/traccar/protocol/TelicFrameDecoder.java)23
-rw-r--r--src/main/java/org/traccar/protocol/TelicProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/TelicProtocolDecoder.java (renamed from src/org/traccar/protocol/TelicProtocolDecoder.java)42
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java (renamed from src/org/traccar/protocol/TeltonikaFrameDecoder.java)27
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocol.java45
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java (renamed from src/org/traccar/protocol/TeltonikaProtocolDecoder.java)281
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolEncoder.java (renamed from src/org/traccar/protocol/TeltonikaProtocolEncoder.java)45
-rw-r--r--src/main/java/org/traccar/protocol/ThinkRaceProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java (renamed from src/org/traccar/protocol/ThinkRaceProtocolDecoder.java)23
-rw-r--r--src/main/java/org/traccar/protocol/Tk102Protocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java (renamed from src/org/traccar/protocol/Tk102ProtocolDecoder.java)34
-rw-r--r--src/main/java/org/traccar/protocol/Tk103FrameDecoder.java75
-rw-r--r--src/main/java/org/traccar/protocol/Tk103Protocol.java69
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java (renamed from src/org/traccar/protocol/Tk103ProtocolDecoder.java)222
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java113
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java (renamed from src/org/traccar/protocol/Tlt2hProtocolDecoder.java)86
-rw-r--r--src/main/java/org/traccar/protocol/TlvProtocol.java (renamed from src/org/traccar/protocol/TlvProtocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/TlvProtocolDecoder.java (renamed from src/org/traccar/protocol/TlvProtocolDecoder.java)39
-rw-r--r--src/main/java/org/traccar/protocol/TmgFrameDecoder.java67
-rw-r--r--src/main/java/org/traccar/protocol/TmgProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/TmgProtocolDecoder.java (renamed from src/org/traccar/protocol/TmgProtocolDecoder.java)104
-rw-r--r--src/main/java/org/traccar/protocol/TopflytechProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java (renamed from src/org/traccar/protocol/TopflytechProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java182
-rw-r--r--src/main/java/org/traccar/protocol/TotemFrameDecoder.java (renamed from src/org/traccar/protocol/TotemFrameDecoder.java)24
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocol.java (renamed from src/org/traccar/protocol/TotemProtocol.java)30
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolDecoder.java (renamed from src/org/traccar/protocol/TotemProtocolDecoder.java)135
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolEncoder.java (renamed from src/org/traccar/protocol/TotemProtocolEncoder.java)17
-rw-r--r--src/main/java/org/traccar/protocol/Tr20Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java (renamed from src/org/traccar/protocol/Tr20ProtocolDecoder.java)25
-rw-r--r--src/main/java/org/traccar/protocol/Tr900Protocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java (renamed from src/org/traccar/protocol/Tr900ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/TrackboxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java (renamed from src/org/traccar/protocol/TrackboxProtocolDecoder.java)19
-rw-r--r--src/main/java/org/traccar/protocol/TrakMateProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java (renamed from src/org/traccar/protocol/TrakMateProtocolDecoder.java)95
-rw-r--r--src/main/java/org/traccar/protocol/TramigoFrameDecoder.java46
-rw-r--r--src/main/java/org/traccar/protocol/TramigoProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java (renamed from src/org/traccar/protocol/TramigoProtocolDecoder.java)58
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/TrvProtocolDecoder.java (renamed from src/org/traccar/protocol/TrvProtocolDecoder.java)87
-rw-r--r--src/main/java/org/traccar/protocol/Tt8850Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java (renamed from src/org/traccar/protocol/Tt8850ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/TytanProtocol.java (renamed from src/org/traccar/protocol/TytanProtocol.java)18
-rw-r--r--src/main/java/org/traccar/protocol/TytanProtocolDecoder.java (renamed from src/org/traccar/protocol/TytanProtocolDecoder.java)31
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocol.java36
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java (renamed from src/org/traccar/protocol/TzoneProtocolDecoder.java)182
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechFrameDecoder.java (renamed from src/org/traccar/protocol/UlbotechFrameDecoder.java)23
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocol.java (renamed from src/org/traccar/protocol/UlbotechProtocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java (renamed from src/org/traccar/protocol/UlbotechProtocolDecoder.java)70
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocolDecoder.java282
-rw-r--r--src/main/java/org/traccar/protocol/V680Protocol.java47
-rw-r--r--src/main/java/org/traccar/protocol/V680ProtocolDecoder.java (renamed from src/org/traccar/protocol/V680ProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/VisiontekProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java (renamed from src/org/traccar/protocol/VisiontekProtocolDecoder.java)10
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocolDecoder.java105
-rw-r--r--src/main/java/org/traccar/protocol/Vt200FrameDecoder.java (renamed from src/org/traccar/protocol/Vt200FrameDecoder.java)19
-rw-r--r--src/main/java/org/traccar/protocol/Vt200Protocol.java (renamed from src/org/traccar/protocol/Vt200Protocol.java)20
-rw-r--r--src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java (renamed from src/org/traccar/protocol/Vt200ProtocolDecoder.java)25
-rw-r--r--src/main/java/org/traccar/protocol/VtfmsFrameDecoder.java (renamed from src/org/traccar/protocol/VtfmsFrameDecoder.java)17
-rw-r--r--src/main/java/org/traccar/protocol/VtfmsProtocol.java44
-rw-r--r--src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java (renamed from src/org/traccar/protocol/VtfmsProtocolDecoder.java)35
-rw-r--r--src/main/java/org/traccar/protocol/WatchFrameDecoder.java67
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocol.java (renamed from src/org/traccar/protocol/WatchProtocol.java)26
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java (renamed from src/org/traccar/protocol/WatchProtocolDecoder.java)206
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolEncoder.java172
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocol.java71
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolDecoder.java (renamed from src/org/traccar/protocol/WialonProtocolDecoder.java)131
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolEncoder.java (renamed from src/org/traccar/protocol/WialonProtocolEncoder.java)17
-rw-r--r--src/main/java/org/traccar/protocol/WondexFrameDecoder.java (renamed from src/org/traccar/protocol/WondexFrameDecoder.java)27
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocol.java55
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolDecoder.java (renamed from src/org/traccar/protocol/WondexProtocolDecoder.java)21
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolEncoder.java (renamed from src/org/traccar/protocol/WondexProtocolEncoder.java)26
-rw-r--r--src/main/java/org/traccar/protocol/WristbandProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java207
-rw-r--r--src/main/java/org/traccar/protocol/XexunFrameDecoder.java (renamed from src/org/traccar/protocol/XexunFrameDecoder.java)25
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocol.java (renamed from src/org/traccar/protocol/XexunProtocol.java)33
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocolDecoder.java (renamed from src/org/traccar/protocol/XexunProtocolDecoder.java)11
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocolEncoder.java (renamed from src/org/traccar/protocol/XexunProtocolEncoder.java)17
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocol.java53
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java371
-rw-r--r--src/main/java/org/traccar/protocol/XirgoProtocolEncoder.java39
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28Protocol.java49
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java187
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28ProtocolEncoder.java57
-rw-r--r--src/main/java/org/traccar/protocol/Xt013Protocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java (renamed from src/org/traccar/protocol/Xt013ProtocolDecoder.java)8
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400Protocol.java (renamed from src/org/traccar/protocol/Xt2400Protocol.java)18
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java (renamed from src/org/traccar/protocol/Xt2400ProtocolDecoder.java)42
-rw-r--r--src/main/java/org/traccar/protocol/YwtProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/YwtProtocolDecoder.java (renamed from src/org/traccar/protocol/YwtProtocolDecoder.java)24
-rw-r--r--src/main/java/org/traccar/reports/Events.java (renamed from src/org/traccar/reports/Events.java)24
-rw-r--r--src/main/java/org/traccar/reports/ReportUtils.java (renamed from src/org/traccar/reports/ReportUtils.java)90
-rw-r--r--src/main/java/org/traccar/reports/Route.java (renamed from src/org/traccar/reports/Route.java)0
-rw-r--r--src/main/java/org/traccar/reports/Stops.java (renamed from src/org/traccar/reports/Stops.java)14
-rw-r--r--src/main/java/org/traccar/reports/Summary.java (renamed from src/org/traccar/reports/Summary.java)25
-rw-r--r--src/main/java/org/traccar/reports/Trips.java (renamed from src/org/traccar/reports/Trips.java)14
-rw-r--r--src/main/java/org/traccar/reports/model/BaseReport.java (renamed from src/org/traccar/reports/model/BaseReport.java)19
-rw-r--r--src/main/java/org/traccar/reports/model/DeviceReport.java (renamed from src/org/traccar/reports/model/DeviceReport.java)0
-rw-r--r--src/main/java/org/traccar/reports/model/StopReport.java (renamed from src/org/traccar/reports/model/StopReport.java)24
-rw-r--r--src/main/java/org/traccar/reports/model/SummaryReport.java (renamed from src/org/traccar/reports/model/SummaryReport.java)0
-rw-r--r--src/main/java/org/traccar/reports/model/TripReport.java (renamed from src/org/traccar/reports/model/TripReport.java)24
-rw-r--r--src/main/java/org/traccar/reports/model/TripsConfig.java (renamed from src/org/traccar/reports/model/TripsConfig.java)0
-rw-r--r--src/main/java/org/traccar/sms/HttpSmsClient.java110
-rw-r--r--src/main/java/org/traccar/sms/SmsManager.java29
-rw-r--r--src/main/java/org/traccar/sms/smpp/ClientSmppSessionHandler.java (renamed from src/org/traccar/smpp/ClientSmppSessionHandler.java)41
-rw-r--r--src/main/java/org/traccar/sms/smpp/EnquireLinkTask.java (renamed from src/org/traccar/smpp/EnquireLinkTask.java)13
-rw-r--r--src/main/java/org/traccar/sms/smpp/ReconnectionTask.java (renamed from src/org/traccar/smpp/ReconnectionTask.java)3
-rw-r--r--src/main/java/org/traccar/sms/smpp/SmppClient.java (renamed from src/org/traccar/smpp/SmppClient.java)88
-rw-r--r--src/main/java/org/traccar/sms/smpp/TextMessageEventHandler.java (renamed from src/org/traccar/events/TextMessageEventHandler.java)2
-rw-r--r--src/main/java/org/traccar/web/ConsoleServlet.java (renamed from src/org/traccar/web/ConsoleServlet.java)7
-rw-r--r--src/main/java/org/traccar/web/CsvBuilder.java (renamed from src/org/traccar/web/CsvBuilder.java)27
-rw-r--r--src/main/java/org/traccar/web/GpxBuilder.java (renamed from src/org/traccar/web/GpxBuilder.java)8
-rw-r--r--src/main/java/org/traccar/web/WebServer.java (renamed from src/org/traccar/web/WebServer.java)132
-rw-r--r--src/main/proto/OmnicommMessage.proto464
-rw-r--r--src/org/traccar/BasePipelineFactory.java296
-rw-r--r--src/org/traccar/BaseProtocolEncoder.java70
-rw-r--r--src/org/traccar/Config.java103
-rw-r--r--src/org/traccar/GeocoderHandler.java95
-rw-r--r--src/org/traccar/GlobalChannelFactory.java56
-rw-r--r--src/org/traccar/Main.java67
-rw-r--r--src/org/traccar/MainEventHandler.java119
-rw-r--r--src/org/traccar/Protocol.java23
-rw-r--r--src/org/traccar/TrackerServer.java132
-rw-r--r--src/org/traccar/database/IdentityManager.java39
-rw-r--r--src/org/traccar/events/AlertEventHandler.java38
-rw-r--r--src/org/traccar/events/MaintenanceEventHandler.java69
-rw-r--r--src/org/traccar/geocoder/JsonGeocoder.java89
-rw-r--r--src/org/traccar/geolocation/UniversalGeolocationProvider.java74
-rw-r--r--src/org/traccar/helper/Checksum.java254
-rw-r--r--src/org/traccar/helper/Log.java268
-rw-r--r--src/org/traccar/helper/StringFinder.java41
-rw-r--r--src/org/traccar/notification/EventForwarder.java91
-rw-r--r--src/org/traccar/notification/NotificationSms.java52
-rw-r--r--src/org/traccar/protocol/AdmProtocol.java53
-rw-r--r--src/org/traccar/protocol/AlematicsProtocol.java46
-rw-r--r--src/org/traccar/protocol/ApelProtocol.java46
-rw-r--r--src/org/traccar/protocol/AppelloProtocol.java47
-rw-r--r--src/org/traccar/protocol/AquilaProtocol.java47
-rw-r--r--src/org/traccar/protocol/Ardi01Protocol.java47
-rw-r--r--src/org/traccar/protocol/ArknavProtocol.java47
-rw-r--r--src/org/traccar/protocol/ArknavX8Protocol.java47
-rw-r--r--src/org/traccar/protocol/ArnaviProtocol.java47
-rw-r--r--src/org/traccar/protocol/AstraProtocol.java50
-rw-r--r--src/org/traccar/protocol/At2000Protocol.java45
-rw-r--r--src/org/traccar/protocol/AtrackFrameDecoder.java63
-rw-r--r--src/org/traccar/protocol/AtrackProtocol.java54
-rw-r--r--src/org/traccar/protocol/AtrackProtocolDecoder.java352
-rw-r--r--src/org/traccar/protocol/AuroProtocol.java47
-rw-r--r--src/org/traccar/protocol/AutoGradeProtocol.java47
-rw-r--r--src/org/traccar/protocol/Avl301Protocol.java43
-rw-r--r--src/org/traccar/protocol/BceProtocol.java45
-rw-r--r--src/org/traccar/protocol/BceProtocolDecoder.java169
-rw-r--r--src/org/traccar/protocol/BlackKiteProtocol.java46
-rw-r--r--src/org/traccar/protocol/BoxProtocol.java47
-rw-r--r--src/org/traccar/protocol/CarTrackProtocol.java47
-rw-r--r--src/org/traccar/protocol/CarscopProtocol.java47
-rw-r--r--src/org/traccar/protocol/CastelProtocol.java56
-rw-r--r--src/org/traccar/protocol/CastelProtocolDecoder.java456
-rw-r--r--src/org/traccar/protocol/CellocatorProtocol.java60
-rw-r--r--src/org/traccar/protocol/CellocatorProtocolDecoder.java160
-rw-r--r--src/org/traccar/protocol/CguardProtocol.java47
-rw-r--r--src/org/traccar/protocol/CradlepointProtocol.java47
-rw-r--r--src/org/traccar/protocol/DishaProtocol.java47
-rw-r--r--src/org/traccar/protocol/DmtHttpProtocol.java47
-rw-r--r--src/org/traccar/protocol/DmtProtocol.java46
-rw-r--r--src/org/traccar/protocol/DmtProtocolDecoder.java189
-rw-r--r--src/org/traccar/protocol/DwayProtocol.java47
-rw-r--r--src/org/traccar/protocol/EasyTrackProtocol.java47
-rw-r--r--src/org/traccar/protocol/EelinkProtocolEncoder.java65
-rw-r--r--src/org/traccar/protocol/EskyProtocol.java46
-rw-r--r--src/org/traccar/protocol/ExtremTracProtocol.java47
-rw-r--r--src/org/traccar/protocol/FifotrackProtocolDecoder.java125
-rw-r--r--src/org/traccar/protocol/FlespiProtocol.java46
-rw-r--r--src/org/traccar/protocol/FlespiProtocolDecoder.java116
-rw-r--r--src/org/traccar/protocol/FlexCommProtocol.java47
-rw-r--r--src/org/traccar/protocol/FlextrackProtocol.java47
-rw-r--r--src/org/traccar/protocol/FoxProtocol.java47
-rw-r--r--src/org/traccar/protocol/FreedomProtocol.java47
-rw-r--r--src/org/traccar/protocol/GatorProtocol.java50
-rw-r--r--src/org/traccar/protocol/GenxProtocol.java45
-rw-r--r--src/org/traccar/protocol/Gl100Protocol.java56
-rw-r--r--src/org/traccar/protocol/Gl200Protocol.java61
-rw-r--r--src/org/traccar/protocol/GlobalSatProtocol.java47
-rw-r--r--src/org/traccar/protocol/GnxProtocol.java47
-rw-r--r--src/org/traccar/protocol/GoSafeProtocol.java47
-rw-r--r--src/org/traccar/protocol/GoSafeProtocolDecoder.java259
-rw-r--r--src/org/traccar/protocol/GotopProtocol.java47
-rw-r--r--src/org/traccar/protocol/Gps103Protocol.java69
-rw-r--r--src/org/traccar/protocol/Gps103ProtocolDecoder.java298
-rw-r--r--src/org/traccar/protocol/GpsGateProtocol.java47
-rw-r--r--src/org/traccar/protocol/GpsMarkerProtocol.java47
-rw-r--r--src/org/traccar/protocol/GpsmtaProtocol.java45
-rw-r--r--src/org/traccar/protocol/Gt02Protocol.java43
-rw-r--r--src/org/traccar/protocol/Gt06ProtocolDecoder.java666
-rw-r--r--src/org/traccar/protocol/Gt30Protocol.java47
-rw-r--r--src/org/traccar/protocol/HaicomProtocol.java47
-rw-r--r--src/org/traccar/protocol/HomtecsProtocol.java45
-rw-r--r--src/org/traccar/protocol/HuabaoProtocolDecoder.java181
-rw-r--r--src/org/traccar/protocol/HuabaoProtocolEncoder.java54
-rw-r--r--src/org/traccar/protocol/HunterProProtocol.java47
-rw-r--r--src/org/traccar/protocol/IdplProtocol.java47
-rw-r--r--src/org/traccar/protocol/IntellitracProtocol.java46
-rw-r--r--src/org/traccar/protocol/KenjiProtocol.java48
-rw-r--r--src/org/traccar/protocol/L100FrameDecoder.java50
-rw-r--r--src/org/traccar/protocol/L100ProtocolDecoder.java120
-rw-r--r--src/org/traccar/protocol/LaipacProtocol.java47
-rw-r--r--src/org/traccar/protocol/LaipacProtocolDecoder.java116
-rw-r--r--src/org/traccar/protocol/MaestroProtocol.java47
-rw-r--r--src/org/traccar/protocol/ManPowerProtocol.java47
-rw-r--r--src/org/traccar/protocol/MegastekProtocol.java46
-rw-r--r--src/org/traccar/protocol/MeitrackProtocol.java68
-rw-r--r--src/org/traccar/protocol/MiniFinderProtocolEncoder.java84
-rw-r--r--src/org/traccar/protocol/MtxProtocol.java47
-rw-r--r--src/org/traccar/protocol/MxtProtocol.java45
-rw-r--r--src/org/traccar/protocol/NavigilProtocol.java45
-rw-r--r--src/org/traccar/protocol/NavisProtocol.java46
-rw-r--r--src/org/traccar/protocol/NavisProtocolDecoder.java287
-rw-r--r--src/org/traccar/protocol/ObdDongleProtocol.java43
-rw-r--r--src/org/traccar/protocol/OrionProtocol.java45
-rw-r--r--src/org/traccar/protocol/OsmAndProtocol.java45
-rw-r--r--src/org/traccar/protocol/OsmAndProtocolDecoder.java167
-rw-r--r--src/org/traccar/protocol/OwnTracksProtocolDecoder.java115
-rw-r--r--src/org/traccar/protocol/PiligrimProtocol.java47
-rw-r--r--src/org/traccar/protocol/PretraceProtocol.java47
-rw-r--r--src/org/traccar/protocol/PricolProtocol.java50
-rw-r--r--src/org/traccar/protocol/ProgressProtocol.java46
-rw-r--r--src/org/traccar/protocol/Pt3000Protocol.java47
-rw-r--r--src/org/traccar/protocol/Pt502FrameDecoder.java56
-rw-r--r--src/org/traccar/protocol/Pt502Protocol.java57
-rw-r--r--src/org/traccar/protocol/RaveonProtocol.java47
-rw-r--r--src/org/traccar/protocol/RecodaProtocol.java46
-rw-r--r--src/org/traccar/protocol/RitiProtocol.java46
-rw-r--r--src/org/traccar/protocol/SanavProtocol.java47
-rw-r--r--src/org/traccar/protocol/SiwiProtocol.java45
-rw-r--r--src/org/traccar/protocol/StarLinkProtocol.java47
-rw-r--r--src/org/traccar/protocol/Stl060Protocol.java46
-rw-r--r--src/org/traccar/protocol/SuntechProtocolDecoder.java379
-rw-r--r--src/org/traccar/protocol/SuntechProtocolEncoder.java58
-rw-r--r--src/org/traccar/protocol/SupermateProtocol.java46
-rw-r--r--src/org/traccar/protocol/T55Protocol.java56
-rw-r--r--src/org/traccar/protocol/T800xProtocolDecoder.java189
-rw-r--r--src/org/traccar/protocol/TaipProtocol.java56
-rw-r--r--src/org/traccar/protocol/TelicProtocol.java49
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocol.java54
-rw-r--r--src/org/traccar/protocol/ThinkRaceProtocol.java43
-rw-r--r--src/org/traccar/protocol/Tk102Protocol.java43
-rw-r--r--src/org/traccar/protocol/Tk103Protocol.java69
-rw-r--r--src/org/traccar/protocol/Tk103ProtocolEncoder.java54
-rw-r--r--src/org/traccar/protocol/Tlt2hProtocol.java47
-rw-r--r--src/org/traccar/protocol/TmgProtocol.java47
-rw-r--r--src/org/traccar/protocol/TopflytechProtocol.java47
-rw-r--r--src/org/traccar/protocol/Tr20Protocol.java47
-rw-r--r--src/org/traccar/protocol/Tr900Protocol.java56
-rw-r--r--src/org/traccar/protocol/TrackboxProtocol.java47
-rw-r--r--src/org/traccar/protocol/TrakMateProtocol.java47
-rw-r--r--src/org/traccar/protocol/TramigoFrameDecoder.java59
-rw-r--r--src/org/traccar/protocol/TramigoProtocol.java45
-rw-r--r--src/org/traccar/protocol/TrvProtocol.java47
-rw-r--r--src/org/traccar/protocol/Tt8850Protocol.java47
-rw-r--r--src/org/traccar/protocol/TzoneProtocol.java43
-rw-r--r--src/org/traccar/protocol/UproProtocol.java45
-rw-r--r--src/org/traccar/protocol/UproProtocolDecoder.java187
-rw-r--r--src/org/traccar/protocol/V680Protocol.java56
-rw-r--r--src/org/traccar/protocol/VisiontekProtocol.java47
-rw-r--r--src/org/traccar/protocol/VtfmsProtocol.java52
-rw-r--r--src/org/traccar/protocol/WatchFrameDecoder.java74
-rw-r--r--src/org/traccar/protocol/WatchProtocolEncoder.java138
-rw-r--r--src/org/traccar/protocol/WialonProtocol.java61
-rw-r--r--src/org/traccar/protocol/WondexProtocol.java63
-rw-r--r--src/org/traccar/protocol/XirgoProtocol.java56
-rw-r--r--src/org/traccar/protocol/XirgoProtocolDecoder.java183
-rw-r--r--src/org/traccar/protocol/Xt013Protocol.java47
-rw-r--r--src/org/traccar/protocol/YwtProtocol.java47
-rw-r--r--src/test/java/org/traccar/BaseTest.java34
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java331
-rw-r--r--src/test/java/org/traccar/TestIdentityManager.java77
-rw-r--r--src/test/java/org/traccar/WebDataHandlerTest.java28
-rw-r--r--src/test/java/org/traccar/calendar/CalendarTest.java59
-rw-r--r--src/test/java/org/traccar/config/ConfigTest.java19
-rw-r--r--src/test/java/org/traccar/database/DataManagerTest.java81
-rw-r--r--src/test/java/org/traccar/database/GroupTreeTest.java56
-rw-r--r--src/test/java/org/traccar/geocoder/AddressFormatTest.java33
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java88
-rw-r--r--src/test/java/org/traccar/geofence/GeofenceCircleTest.java28
-rw-r--r--src/test/java/org/traccar/geofence/GeofencePolygonTest.java52
-rw-r--r--src/test/java/org/traccar/geofence/GeofencePolylineTest.java47
-rw-r--r--src/test/java/org/traccar/geolocation/GeolocationProviderTest.java41
-rw-r--r--src/test/java/org/traccar/handler/ComputedAttributesTest.java71
-rw-r--r--src/test/java/org/traccar/handler/DistanceHandlerTest.java30
-rw-r--r--src/test/java/org/traccar/handler/FilterHandlerTest.java88
-rw-r--r--src/test/java/org/traccar/handler/MotionHandlerTest.java21
-rw-r--r--src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java30
-rw-r--r--src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java28
-rw-r--r--src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java27
-rw-r--r--src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java119
-rw-r--r--src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java128
-rw-r--r--src/test/java/org/traccar/handler/protocol/Arnavi4FrameDecoderTest.java50
-rw-r--r--src/test/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoderTest.java40
-rw-r--r--src/test/java/org/traccar/helper/BcdUtilTest.java24
-rw-r--r--src/test/java/org/traccar/helper/BitBufferTest.java23
-rw-r--r--src/test/java/org/traccar/helper/BitUtilTest.java38
-rw-r--r--src/test/java/org/traccar/helper/ChecksumTest.java39
-rw-r--r--src/test/java/org/traccar/helper/DateBuilderTest.java27
-rw-r--r--src/test/java/org/traccar/helper/DateUtilTest.java30
-rw-r--r--src/test/java/org/traccar/helper/DistanceCalculatorTest.java24
-rw-r--r--src/test/java/org/traccar/helper/LocationTreeTest.java30
-rw-r--r--src/test/java/org/traccar/helper/LogTest.java23
-rw-r--r--src/test/java/org/traccar/helper/ObdDecoderTest.java26
-rw-r--r--src/test/java/org/traccar/helper/PatternBuilderTest.java20
-rw-r--r--src/test/java/org/traccar/helper/PatternUtilTest.java18
-rw-r--r--src/test/java/org/traccar/helper/ServletHelperTest.java65
-rw-r--r--src/test/java/org/traccar/model/MiscFormatterTest.java20
-rw-r--r--src/test/java/org/traccar/notification/NotificiationMailTest.java59
-rw-r--r--src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java38
-rw-r--r--src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java44
-rw-r--r--src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java93
-rw-r--r--src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java59
-rw-r--r--src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java121
-rw-r--r--src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java41
-rw-r--r--src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java73
-rw-r--r--src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java57
-rw-r--r--src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java46
-rw-r--r--src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java152
-rw-r--r--src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java78
-rw-r--r--src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java41
-rw-r--r--src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java55
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java118
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java60
-rw-r--r--src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java53
-rw-r--r--src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java56
-rw-r--r--src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java404
-rw-r--r--src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java61
-rw-r--r--src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java100
-rw-r--r--src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java40
-rw-r--r--src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java264
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java52
-rw-r--r--src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java49
-rw-r--r--src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java43
-rw-r--r--src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java330
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/H02FrameDecoderTest.java71
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java279
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java72
-rw-r--r--src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java81
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java64
-rw-r--r--src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java108
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java60
-rw-r--r--src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java43
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java123
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java37
-rwxr-xr-xsrc/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/L100FrameDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java132
-rw-r--r--src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java100
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java143
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java41
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java110
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java37
-rw-r--r--src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java73
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java79
-rw-r--r--src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java45
-rw-r--r--src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java33
-rw-r--r--src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java48
-rw-r--r--src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java20
-rw-r--r--src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java35
-rw-r--r--src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/PstFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java86
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java69
-rw-r--r--src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java37
-rw-r--r--src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java34
-rw-r--r--src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java37
-rw-r--r--src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java59
-rw-r--r--src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java211
-rwxr-xr-xsrc/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java131
-rw-r--r--src/test/java/org/traccar/protocol/T57FrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java66
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java89
-rw-r--r--src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/TekFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java94
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java140
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java44
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java205
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java282
-rw-r--r--src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java74
-rw-r--r--src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java54
-rw-r--r--src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java35
-rw-r--r--src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java116
-rw-r--r--src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java63
-rw-r--r--src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java70
-rw-r--r--src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java56
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java94
-rw-r--r--src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java85
-rw-r--r--src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java77
-rw-r--r--src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java40
-rw-r--r--src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java60
-rw-r--r--src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java35
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java137
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java87
-rw-r--r--src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java74
-rw-r--r--src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java28
-rw-r--r--src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java65
-rw-r--r--src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java118
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java79
-rw-r--r--src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java26
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java30
-rw-r--r--src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java31
-rw-r--r--src/test/java/org/traccar/reports/ReportUtilsTest.java390
-rw-r--r--src/test/java/org/traccar/web/WebServerTest.java29
1224 files changed, 54611 insertions, 18412 deletions
diff --git a/src/org/traccar/BaseDataHandler.java b/src/main/java/org/traccar/BaseDataHandler.java
index 0c71a6a4d..48794b0d7 100644
--- a/src/org/traccar/BaseDataHandler.java
+++ b/src/main/java/org/traccar/BaseDataHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,21 +15,22 @@
*/
package org.traccar;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import org.traccar.model.Position;
-public abstract class BaseDataHandler extends OneToOneDecoder {
+public abstract class BaseDataHandler extends ChannelInboundHandlerAdapter {
@Override
- protected final Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
-
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof Position) {
- return handlePosition((Position) msg);
+ Position position = handlePosition((Position) msg);
+ if (position != null) {
+ ctx.fireChannelRead(position);
+ }
+ } else {
+ super.channelRead(ctx, msg);
}
-
- return msg;
}
protected abstract Position handlePosition(Position position);
diff --git a/src/main/java/org/traccar/BaseFrameDecoder.java b/src/main/java/org/traccar/BaseFrameDecoder.java
new file mode 100644
index 000000000..f90f90e4b
--- /dev/null
+++ b/src/main/java/org/traccar/BaseFrameDecoder.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+import java.util.List;
+
+public abstract class BaseFrameDecoder extends ByteToMessageDecoder {
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+ Object decoded = decode(ctx, ctx != null ? ctx.channel() : null, in);
+ if (decoded != null) {
+ out.add(decoded);
+ }
+ }
+
+ protected abstract Object decode(ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception;
+
+}
diff --git a/src/main/java/org/traccar/BaseHttpProtocolDecoder.java b/src/main/java/org/traccar/BaseHttpProtocolDecoder.java
new file mode 100644
index 000000000..57a68acac
--- /dev/null
+++ b/src/main/java/org/traccar/BaseHttpProtocolDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+
+public abstract class BaseHttpProtocolDecoder extends BaseProtocolDecoder {
+
+ public BaseHttpProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public void sendResponse(Channel channel, HttpResponseStatus status) {
+ if (channel != null) {
+ HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
+ response.headers().add(HttpHeaderNames.CONTENT_LENGTH, 0);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
new file mode 100644
index 000000000..5a5850a86
--- /dev/null
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2012 - 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;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelInboundHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOutboundHandler;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.timeout.IdleStateHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Keys;
+import org.traccar.handler.DefaultDataHandler;
+import org.traccar.handler.TimeHandler;
+import org.traccar.handler.events.AlertEventHandler;
+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.handler.ComputedAttributesHandler;
+import org.traccar.handler.CopyAttributesHandler;
+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.NetworkMessageHandler;
+import org.traccar.handler.OpenChannelHandler;
+import org.traccar.handler.RemoteAddressHandler;
+import org.traccar.handler.StandardLoggingHandler;
+
+import java.util.Map;
+
+public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BasePipelineFactory.class);
+
+ private final TrackerServer server;
+ private final String protocol;
+ private boolean eventsEnabled;
+ private int timeout;
+
+ public BasePipelineFactory(TrackerServer server, String protocol) {
+ this.server = server;
+ this.protocol = protocol;
+ eventsEnabled = Context.getConfig().getBoolean(Keys.EVENT_ENABLE);
+ timeout = Context.getConfig().getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
+ if (timeout == 0) {
+ timeout = Context.getConfig().getInteger(Keys.SERVER_TIMEOUT);
+ }
+ }
+
+ protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
+
+ @SafeVarargs
+ private final void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) {
+ for (Class<? extends ChannelHandler> handlerClass : handlerClasses) {
+ if (handlerClass != null) {
+ pipeline.addLast(Main.getInjector().getInstance(handlerClass));
+ }
+ }
+ }
+
+ public static <T extends ChannelHandler> T getHandler(ChannelPipeline pipeline, Class<T> clazz) {
+ for (Map.Entry<String, ChannelHandler> handlerEntry : pipeline) {
+ ChannelHandler handler = handlerEntry.getValue();
+ if (handler instanceof WrapperInboundHandler) {
+ handler = ((WrapperInboundHandler) handler).getWrappedHandler();
+ } else if (handler instanceof WrapperOutboundHandler) {
+ handler = ((WrapperOutboundHandler) handler).getWrappedHandler();
+ }
+ if (clazz.isAssignableFrom(handler.getClass())) {
+ return (T) handler;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void initChannel(Channel channel) {
+ final ChannelPipeline pipeline = channel.pipeline();
+
+ if (timeout > 0 && !server.isDatagram()) {
+ pipeline.addLast(new IdleStateHandler(timeout, 0, 0));
+ }
+ pipeline.addLast(new OpenChannelHandler(server));
+ pipeline.addLast(new NetworkMessageHandler());
+ pipeline.addLast(new StandardLoggingHandler(protocol));
+
+ addProtocolHandlers(handler -> {
+ if (!(handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder)) {
+ if (handler instanceof ChannelInboundHandler) {
+ handler = new WrapperInboundHandler((ChannelInboundHandler) handler);
+ } else {
+ handler = new WrapperOutboundHandler((ChannelOutboundHandler) handler);
+ }
+ }
+ pipeline.addLast(handler);
+ });
+
+ addHandlers(
+ pipeline,
+ TimeHandler.class,
+ GeolocationHandler.class,
+ HemisphereHandler.class,
+ DistanceHandler.class,
+ RemoteAddressHandler.class);
+
+ addDynamicHandlers(pipeline);
+
+ addHandlers(
+ pipeline,
+ FilterHandler.class,
+ GeocoderHandler.class,
+ MotionHandler.class,
+ CopyAttributesHandler.class,
+ EngineHoursHandler.class,
+ ComputedAttributesHandler.class,
+ WebDataHandler.class,
+ DefaultDataHandler.class);
+
+ if (eventsEnabled) {
+ addHandlers(
+ pipeline,
+ CommandResultEventHandler.class,
+ OverspeedEventHandler.class,
+ FuelDropEventHandler.class,
+ MotionEventHandler.class,
+ GeofenceEventHandler.class,
+ AlertEventHandler.class,
+ IgnitionEventHandler.class,
+ MaintenanceEventHandler.class,
+ DriverEventHandler.class);
+ }
+
+ pipeline.addLast(new MainEventHandler());
+ }
+
+ private void addDynamicHandlers(ChannelPipeline pipeline) {
+ String handlers = Context.getConfig().getString(Keys.EXTRA_HANDLERS);
+ if (handlers != null) {
+ for (String handler : handlers.split(",")) {
+ try {
+ pipeline.addLast((ChannelHandler) Class.forName(handler).getDeclaredConstructor().newInstance());
+ } catch (ReflectiveOperationException error) {
+ LOGGER.warn("Dynamic handler error", error);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java
index 90b9f21f2..c0fd1e27f 100644
--- a/src/org/traccar/BaseProtocol.java
+++ b/src/main/java/org/traccar/BaseProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,17 @@
*/
package org.traccar;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.database.ActiveDevice;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
-import javax.xml.bind.DatatypeConverter;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Set;
public abstract class BaseProtocol implements Protocol {
@@ -31,11 +33,17 @@ 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 StringProtocolEncoder textCommandEncoder = null;
- public BaseProtocol(String name) {
- this.name = name;
+ public static String nameFromClass(Class<?> clazz) {
+ String className = clazz.getSimpleName();
+ return className.substring(0, className.length() - 8).toLowerCase();
+ }
+
+ public BaseProtocol() {
+ name = nameFromClass(getClass());
}
@Override
@@ -43,6 +51,15 @@ public abstract class BaseProtocol implements Protocol {
return name;
}
+ protected void addServer(TrackerServer server) {
+ serverList.add(server);
+ }
+
+ @Override
+ public Collection<TrackerServer> getServerList() {
+ return serverList;
+ }
+
public void setSupportedDataCommands(String... commands) {
supportedDataCommands.addAll(Arrays.asList(commands));
}
@@ -76,10 +93,10 @@ public abstract class BaseProtocol implements Protocol {
activeDevice.write(command);
} else if (command.getType().equals(Command.TYPE_CUSTOM)) {
String data = command.getString(Command.KEY_DATA);
- if (activeDevice.getChannel().getPipeline().get(StringEncoder.class) != null) {
+ if (BasePipelineFactory.getHandler(activeDevice.getChannel().pipeline(), StringEncoder.class) != null) {
activeDevice.write(data);
} else {
- activeDevice.write(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary(data)));
+ activeDevice.write(Unpooled.wrappedBuffer(DataConverter.parseHex(data)));
}
} else {
throw new RuntimeException("Command " + command.getType() + " is not supported in protocol " + getName());
@@ -92,18 +109,22 @@ public abstract class BaseProtocol implements Protocol {
@Override
public void sendTextCommand(String destAddress, Command command) throws Exception {
- if (Context.getSmppManager() != null) {
+ if (Context.getSmsManager() != null) {
if (command.getType().equals(Command.TYPE_CUSTOM)) {
- Context.getSmppManager().sendMessageSync(destAddress, command.getString(Command.KEY_DATA), true);
+ Context.getSmsManager().sendMessageSync(destAddress, command.getString(Command.KEY_DATA), true);
} else if (supportedTextCommands.contains(command.getType()) && textCommandEncoder != null) {
- Context.getSmppManager().sendMessageSync(destAddress,
- (String) textCommandEncoder.encodeCommand(command), true);
+ String encodedCommand = (String) textCommandEncoder.encodeCommand(command);
+ if (encodedCommand != null) {
+ Context.getSmsManager().sendMessageSync(destAddress, encodedCommand, true);
+ } else {
+ throw new RuntimeException("Failed to encode command");
+ }
} else {
throw new RuntimeException(
"Command " + command.getType() + " is not supported in protocol " + getName());
}
} else {
- throw new RuntimeException("SMPP client is not enabled");
+ throw new RuntimeException("SMS is not enabled");
}
}
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java
index 2d6286bf8..e6e02c2d6 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,10 +15,15 @@
*/
package org.traccar;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.socket.DatagramChannel;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.traccar.helper.Log;
+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.database.ConnectionManager;
+import org.traccar.database.IdentityManager;
+import org.traccar.database.StatisticsManager;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Device;
import org.traccar.model.Position;
@@ -29,46 +34,40 @@ import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
-import java.sql.SQLException;
+import java.util.TimeZone;
public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
- private final Protocol protocol;
-
- public long addUnknownDevice(String uniqueId) {
- Device device = new Device();
- device.setName(uniqueId);
- device.setUniqueId(uniqueId);
- device.setCategory(Context.getConfig().getString("database.registerUnknown.defaultCategory"));
-
- long defaultGroupId = Context.getConfig().getLong("database.registerUnknown.defaultGroupId");
- if (defaultGroupId != 0) {
- device.setGroupId(defaultGroupId);
- }
-
- try {
- Context.getDeviceManager().addItem(device);
+ private static final Logger LOGGER = LoggerFactory.getLogger(BaseProtocolDecoder.class);
- Log.info("Automatically registered device " + uniqueId);
+ private static final String PROTOCOL_UNKNOWN = "unknown";
- if (defaultGroupId != 0) {
- Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
- Context.getPermissionsManager().refreshAllExtendedPermissions();
- }
+ 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;
- return device.getId();
- } catch (SQLException e) {
- Log.warning(e);
- return 0;
- }
+ public BaseProtocolDecoder(Protocol protocol) {
+ this.protocol = protocol;
+ statisticsManager = Main.getInjector() != null ? Main.getInjector().getInstance(StatisticsManager.class) : null;
}
public String getProtocolName() {
- return protocol.getName();
+ return protocol != null ? protocol.getName() : PROTOCOL_UNKNOWN;
+ }
+
+ public String getServer(Channel channel, char delimiter) {
+ String server = config.getString(getProtocolName() + ".server");
+ if (server == null && channel != null) {
+ InetSocketAddress address = (InetSocketAddress) channel.localAddress();
+ server = address.getAddress().getHostAddress() + ":" + address.getPort();
+ }
+ return server != null ? server.replace(':', delimiter) : null;
}
protected double convertSpeed(double value, String defaultUnits) {
- switch (Context.getConfig().getString(getProtocolName() + ".speed", defaultUnits)) {
+ switch (config.getString(getProtocolName() + ".speed", defaultUnits)) {
case "kmh":
return UnitsConverter.knotsFromKph(value);
case "mps":
@@ -81,16 +80,36 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
}
+ protected TimeZone getTimeZone(long deviceId) {
+ return getTimeZone(deviceId, "UTC");
+ }
+
+ protected TimeZone getTimeZone(long deviceId, String defaultTimeZone) {
+ TimeZone result = TimeZone.getTimeZone(defaultTimeZone);
+ String timeZoneName = identityManager.lookupAttributeString(deviceId, "decoder.timezone", null, false, true);
+ if (timeZoneName != null) {
+ result = TimeZone.getTimeZone(timeZoneName);
+ } else {
+ int timeZoneOffset = config.getInteger(getProtocolName() + ".timezone", 0);
+ if (timeZoneOffset != 0) {
+ result.setRawOffset(timeZoneOffset * 1000);
+ LOGGER.warn("Config parameter " + getProtocolName() + ".timezone is deprecated");
+ }
+ }
+ return result;
+ }
+
private DeviceSession channelDeviceSession; // connection-based protocols
private Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols
private long findDeviceId(SocketAddress remoteAddress, String... uniqueIds) {
- long deviceId = 0;
if (uniqueIds.length > 0) {
+ long deviceId = 0;
+ Device device = null;
try {
for (String uniqueId : uniqueIds) {
if (uniqueId != null) {
- Device device = Context.getIdentityManager().getByUniqueId(uniqueId);
+ device = identityManager.getByUniqueId(uniqueId);
if (device != null) {
deviceId = device.getId();
break;
@@ -98,33 +117,44 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
}
} catch (Exception e) {
- Log.warning(e);
+ LOGGER.warn("Find device error", e);
+ }
+ if (deviceId == 0 && config.getBoolean("database.registerUnknown")) {
+ return identityManager.addUnknownDevice(uniqueIds[0]);
}
+ if (device != null && !device.getDisabled() || config.getBoolean("database.storeDisabled")) {
+ return deviceId;
+ }
+ StringBuilder message = new StringBuilder();
if (deviceId == 0) {
- if (Context.getConfig().getBoolean("database.registerUnknown")) {
- return addUnknownDevice(uniqueIds[0]);
- }
-
- StringBuilder message = new StringBuilder("Unknown device -");
- for (String uniqueId : uniqueIds) {
- message.append(" ").append(uniqueId);
- }
- if (remoteAddress != null) {
- message.append(" (").append(((InetSocketAddress) remoteAddress).getHostString()).append(")");
- }
- Log.warning(message.toString());
+ 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 deviceId;
+ return 0;
}
public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) {
- if (channel != null && channel.getPipeline().get(HttpRequestDecoder.class) != null
- || Context.getConfig().getBoolean("decoder.ignoreSessionCache")) {
+ 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(getProtocolName() + ".ignoreSessionCache")
+ || config.getBoolean("decoder.ignoreSessionCache")) {
long deviceId = findDeviceId(remoteAddress, uniqueIds);
if (deviceId != 0) {
- if (Context.getConnectionManager() != null) {
- Context.getConnectionManager().addActiveDevice(deviceId, protocol, channel, remoteAddress);
+ if (connectionManager != null) {
+ connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress);
}
return new DeviceSession(deviceId);
} else {
@@ -139,8 +169,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
} else if (deviceId != 0) {
deviceSession = new DeviceSession(deviceId);
addressDeviceSessions.put(remoteAddress, deviceSession);
- if (Context.getConnectionManager() != null) {
- Context.getConnectionManager().addActiveDevice(deviceId, protocol, channel, remoteAddress);
+ if (connectionManager != null) {
+ connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress);
}
return deviceSession;
} else {
@@ -151,8 +181,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
long deviceId = findDeviceId(remoteAddress, uniqueIds);
if (deviceId != 0) {
channelDeviceSession = new DeviceSession(deviceId);
- if (Context.getConnectionManager() != null) {
- Context.getConnectionManager().addActiveDevice(deviceId, protocol, channel, remoteAddress);
+ if (connectionManager != null) {
+ connectionManager.addActiveDevice(deviceId, protocol, channel, remoteAddress);
}
}
}
@@ -160,15 +190,11 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
}
- public BaseProtocolDecoder(Protocol protocol) {
- this.protocol = protocol;
- }
-
public void getLastLocation(Position position, Date deviceTime) {
if (position.getDeviceId() != 0) {
position.setOutdated(true);
- Position last = Context.getIdentityManager().getLastPosition(position.getDeviceId());
+ Position last = identityManager.getLastPosition(position.getDeviceId());
if (last != null) {
position.setFixTime(last.getFixTime());
position.setValid(last.getValid());
@@ -193,8 +219,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
@Override
protected void onMessageEvent(
Channel channel, SocketAddress remoteAddress, Object originalMessage, Object decodedMessage) {
- if (Context.getStatisticsManager() != null) {
- Context.getStatisticsManager().registerMessageReceived();
+ if (statisticsManager != null) {
+ statisticsManager.registerMessageReceived();
}
Position position = null;
if (decodedMessage != null) {
@@ -208,12 +234,12 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
}
if (position != null) {
- Context.getConnectionManager().updateDevice(
+ connectionManager.updateDevice(
position.getDeviceId(), Device.STATUS_ONLINE, new Date());
} else {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession != null) {
- Context.getConnectionManager().updateDevice(
+ connectionManager.updateDevice(
deviceSession.getDeviceId(), Device.STATUS_ONLINE, new Date());
}
}
@@ -222,9 +248,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
@Override
protected Object handleEmptyMessage(Channel channel, SocketAddress remoteAddress, Object msg) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (Context.getConfig().getBoolean("database.saveEmpty") && deviceSession != null) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ if (config.getBoolean("database.saveEmpty") && deviceSession != null) {
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
return position;
diff --git a/src/main/java/org/traccar/BaseProtocolEncoder.java b/src/main/java/org/traccar/BaseProtocolEncoder.java
new file mode 100644
index 000000000..b6df07b98
--- /dev/null
+++ b/src/main/java/org/traccar/BaseProtocolEncoder.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.Command;
+
+public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BaseProtocolEncoder.class);
+
+ private static final String PROTOCOL_UNKNOWN = "unknown";
+
+ private final Protocol protocol;
+
+ public BaseProtocolEncoder(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getProtocolName() {
+ return protocol != null ? protocol.getName() : PROTOCOL_UNKNOWN;
+ }
+
+ protected String getUniqueId(long deviceId) {
+ return Context.getIdentityManager().getById(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);
+ command.set(Command.KEY_DEVICE_PASSWORD, password);
+ }
+ }
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+
+ NetworkMessage networkMessage = (NetworkMessage) msg;
+
+ if (networkMessage.getMessage() instanceof Command) {
+
+ Command command = (Command) networkMessage.getMessage();
+ Object encodedCommand = encodeCommand(ctx.channel(), command);
+
+ StringBuilder s = new StringBuilder();
+ s.append("[").append(ctx.channel().id().asShortText()).append("] ");
+ s.append("id: ").append(getUniqueId(command.getDeviceId())).append(", ");
+ s.append("command type: ").append(command.getType()).append(" ");
+ if (encodedCommand != null) {
+ s.append("sent");
+ } else {
+ s.append("not sent");
+ }
+ LOGGER.info(s.toString());
+
+ ctx.write(new NetworkMessage(encodedCommand, networkMessage.getRemoteAddress()), promise);
+
+ } else {
+
+ super.write(ctx, msg, promise);
+
+ }
+ }
+
+ protected Object encodeCommand(Channel channel, Command command) {
+ return encodeCommand(command);
+ }
+
+ protected Object encodeCommand(Command command) {
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/CharacterDelimiterFrameDecoder.java b/src/main/java/org/traccar/CharacterDelimiterFrameDecoder.java
index d71974e78..eeb8834dc 100644
--- a/src/org/traccar/CharacterDelimiterFrameDecoder.java
+++ b/src/main/java/org/traccar/CharacterDelimiterFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,27 +15,27 @@
*/
package org.traccar;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
public class CharacterDelimiterFrameDecoder extends DelimiterBasedFrameDecoder {
- private static ChannelBuffer createDelimiter(char delimiter) {
+ private static ByteBuf createDelimiter(char delimiter) {
byte[] buf = {(byte) delimiter};
- return ChannelBuffers.wrappedBuffer(buf);
+ return Unpooled.wrappedBuffer(buf);
}
- private static ChannelBuffer createDelimiter(String delimiter) {
+ private static ByteBuf createDelimiter(String delimiter) {
byte[] buf = new byte[delimiter.length()];
for (int i = 0; i < delimiter.length(); i++) {
buf[i] = (byte) delimiter.charAt(i);
}
- return ChannelBuffers.wrappedBuffer(buf);
+ return Unpooled.wrappedBuffer(buf);
}
- private static ChannelBuffer[] convertDelimiters(String[] delimiters) {
- ChannelBuffer[] result = new ChannelBuffer[delimiters.length];
+ private static ByteBuf[] convertDelimiters(String[] delimiters) {
+ ByteBuf[] result = new ByteBuf[delimiters.length];
for (int i = 0; i < delimiters.length; i++) {
result[i] = createDelimiter(delimiters[i]);
}
@@ -50,8 +50,16 @@ public class CharacterDelimiterFrameDecoder extends DelimiterBasedFrameDecoder {
super(maxFrameLength, createDelimiter(delimiter));
}
+ public CharacterDelimiterFrameDecoder(int maxFrameLength, boolean stripDelimiter, String delimiter) {
+ super(maxFrameLength, stripDelimiter, createDelimiter(delimiter));
+ }
+
public CharacterDelimiterFrameDecoder(int maxFrameLength, String... delimiters) {
super(maxFrameLength, convertDelimiters(delimiters));
}
+ public CharacterDelimiterFrameDecoder(int maxFrameLength, boolean stripDelimiter, String... delimiters) {
+ super(maxFrameLength, stripDelimiter, convertDelimiters(delimiters));
+ }
+
}
diff --git a/src/org/traccar/Context.java b/src/main/java/org/traccar/Context.java
index 3b24c6460..9c20db9e4 100644
--- a/src/org/traccar/Context.java
+++ b/src/main/java/org/traccar/Context.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,43 +17,33 @@ package org.traccar;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-import com.ning.http.client.AsyncHttpClient;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Properties;
-
+import com.fasterxml.jackson.datatype.jsr353.JSR353Module;
import org.apache.velocity.app.VelocityEngine;
import org.eclipse.jetty.util.URIUtil;
-import org.traccar.database.CalendarManager;
-import org.traccar.database.CommandsManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
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.PermissionsManager;
-import org.traccar.database.GeofenceManager;
-import org.traccar.database.GroupsManager;
-import org.traccar.database.StatisticsManager;
import org.traccar.database.UsersManager;
-import org.traccar.events.MotionEventHandler;
-import org.traccar.events.OverspeedEventHandler;
-import org.traccar.geocoder.BingMapsGeocoder;
-import org.traccar.geocoder.FactualGeocoder;
-import org.traccar.geocoder.GeocodeFarmGeocoder;
-import org.traccar.geocoder.GisgraphyGeocoder;
-import org.traccar.geocoder.GoogleGeocoder;
-import org.traccar.geocoder.MapQuestGeocoder;
-import org.traccar.geocoder.NominatimGeocoder;
-import org.traccar.geocoder.OpenCageGeocoder;
import org.traccar.geocoder.Geocoder;
-import org.traccar.geolocation.UnwiredGeolocationProvider;
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;
@@ -62,19 +52,28 @@ 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.User;
-import org.traccar.geolocation.GoogleGeolocationProvider;
-import org.traccar.geolocation.GeolocationProvider;
-import org.traccar.geolocation.MozillaGeolocationProvider;
-import org.traccar.geolocation.OpenCellIdGeolocationProvider;
import org.traccar.notification.EventForwarder;
+import org.traccar.notification.JsonTypeEventForwarder;
+import org.traccar.notification.NotificatorManager;
import org.traccar.reports.model.TripsConfig;
-import org.traccar.smpp.SmppClient;
+import org.traccar.sms.SmsManager;
+import org.traccar.sms.smpp.SmppClient;
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 static final Logger LOGGER = LoggerFactory.getLogger(Context.class);
+
private Context() {
}
@@ -84,12 +83,6 @@ public final class Context {
return config;
}
- private static boolean loggerEnabled;
-
- public static boolean isLoggerEnabled() {
- return loggerEnabled;
- }
-
private static ObjectMapper objectMapper;
public static ObjectMapper getObjectMapper() {
@@ -108,6 +101,18 @@ public final class Context {
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() {
@@ -144,16 +149,8 @@ public final class Context {
return permissionsManager;
}
- private static Geocoder geocoder;
-
public static Geocoder getGeocoder() {
- return geocoder;
- }
-
- private static GeolocationProvider geolocationProvider;
-
- public static GeolocationProvider getGeolocationProvider() {
- return geolocationProvider;
+ return Main.getInjector() != null ? Main.getInjector().getInstance(Geocoder.class) : null;
}
private static WebServer webServer;
@@ -186,16 +183,22 @@ public final class Context {
return notificationManager;
}
+ private static NotificatorManager notificatorManager;
+
+ public static NotificatorManager getNotificatorManager() {
+ return notificatorManager;
+ }
+
private static VelocityEngine velocityEngine;
public static VelocityEngine getVelocityEngine() {
return velocityEngine;
}
- private static final AsyncHttpClient ASYNC_HTTP_CLIENT = new AsyncHttpClient();
+ private static Client client = ClientBuilder.newClient();
- public static AsyncHttpClient getAsyncHttpClient() {
- return ASYNC_HTTP_CLIENT;
+ public static Client getClient() {
+ return client;
}
private static EventForwarder eventForwarder;
@@ -222,28 +225,16 @@ public final class Context {
return commandsManager;
}
- private static StatisticsManager statisticsManager;
-
- public static StatisticsManager getStatisticsManager() {
- return statisticsManager;
- }
-
- private static SmppClient smppClient;
-
- public static SmppClient getSmppManager() {
- return smppClient;
- }
-
- private static MotionEventHandler motionEventHandler;
+ private static MaintenancesManager maintenancesManager;
- public static MotionEventHandler getMotionEventHandler() {
- return motionEventHandler;
+ public static MaintenancesManager getMaintenancesManager() {
+ return maintenancesManager;
}
- private static OverspeedEventHandler overspeedEventHandler;
+ private static SmsManager smsManager;
- public static OverspeedEventHandler getOverspeedEventHandler() {
- return overspeedEventHandler;
+ public static SmsManager getSmsManager() {
+ return smsManager;
}
private static TripsConfig tripsConfig;
@@ -263,32 +254,52 @@ public final class Context {
config.getDouble("event.motion.speedThreshold", 0.01));
}
- public static void init(String[] arguments) throws Exception {
+ private static class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
- config = new Config();
- if (arguments.length <= 0) {
- throw new RuntimeException("Configuration file is not provided");
+ @Override
+ public ObjectMapper getContext(Class<?> clazz) {
+ return objectMapper;
}
- config.load(arguments[0]);
+ }
+
+ public static void init(String configFile) throws Exception {
+
+ try {
+ config = new Config(configFile);
+ } catch (Exception e) {
+ config = new Config();
+ Log.setupDefaultLogger();
+ throw e;
+ }
- loggerEnabled = config.getBoolean("logger.enable");
- if (loggerEnabled) {
+ if (config.getBoolean("logger.enable")) {
Log.setupLogger(config);
}
objectMapper = new ObjectMapper();
+ objectMapper.registerModule(new SanitizerModule());
+ objectMapper.registerModule(new JSR353Module());
objectMapper.setConfig(
objectMapper.getSerializationConfig().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
+ if (Context.getConfig().getBoolean("mapper.prettyPrintedJson")) {
+ objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
+ }
+
+ client = ClientBuilder.newClient().register(new ObjectMapperContextResolver());
if (config.hasKey("database.url")) {
dataManager = new DataManager(config);
}
- if (config.hasKey("media.path")) {
- mediaManager = new MediaManager(config);
+ if (config.getBoolean("ldap.enable")) {
+ ldapProvider = new LdapProvider(config);
}
+ mailManager = new MailManager();
+
+ mediaManager = new MediaManager(config.getString("media.path"));
+
if (dataManager != null) {
usersManager = new UsersManager(dataManager);
groupsManager = new GroupsManager(dataManager);
@@ -297,64 +308,8 @@ public final class Context {
identityManager = deviceManager;
- if (config.getBoolean("geocoder.enable")) {
- String type = config.getString("geocoder.type", "google");
- String url = config.getString("geocoder.url");
- String key = config.getString("geocoder.key");
- String language = config.getString("geocoder.language");
-
- int cacheSize = config.getInteger("geocoder.cacheSize");
- switch (type) {
- case "nominatim":
- geocoder = new NominatimGeocoder(url, key, language, cacheSize);
- break;
- case "gisgraphy":
- geocoder = new GisgraphyGeocoder(url, cacheSize);
- break;
- case "mapquest":
- geocoder = new MapQuestGeocoder(url, key, cacheSize);
- break;
- case "opencage":
- geocoder = new OpenCageGeocoder(url, key, cacheSize);
- break;
- case "bingmaps":
- geocoder = new BingMapsGeocoder(url, key, cacheSize);
- break;
- case "factual":
- geocoder = new FactualGeocoder(url, key, cacheSize);
- break;
- case "geocodefarm":
- geocoder = new GeocodeFarmGeocoder(key, language, cacheSize);
- break;
- default:
- geocoder = new GoogleGeocoder(key, language, cacheSize);
- break;
- }
- }
-
- if (config.getBoolean("geolocation.enable")) {
- String type = config.getString("geolocation.type", "mozilla");
- String url = config.getString("geolocation.url");
- String key = config.getString("geolocation.key");
-
- switch (type) {
- case "google":
- geolocationProvider = new GoogleGeolocationProvider(key);
- break;
- case "opencellid":
- geolocationProvider = new OpenCellIdGeolocationProvider(key);
- break;
- case "unwired":
- geolocationProvider = new UnwiredGeolocationProvider(url, key);
- break;
- default:
- geolocationProvider = new MozillaGeolocationProvider(key);
- break;
- }
- }
-
if (config.getBoolean("web.enable")) {
- webServer = new WebServer(config, dataManager.getDataSource());
+ webServer = new WebServer(config);
}
permissionsManager = new PermissionsManager(dataManager, usersManager);
@@ -363,60 +318,68 @@ public final class Context {
tripsConfig = initTripsConfig();
- if (config.getBoolean("event.enable")) {
- geofenceManager = new GeofenceManager(dataManager);
- calendarManager = new CalendarManager(dataManager);
- notificationManager = new NotificationManager(dataManager);
- 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;
+ if (config.getBoolean("sms.enable")) {
+ final String smsManagerClass = config.getString("sms.manager.class", SmppClient.class.getCanonicalName());
try {
- address = config.getString("web.address", InetAddress.getLocalHost().getHostAddress());
- } catch (UnknownHostException e) {
- address = "localhost";
+ smsManager = (SmsManager) Class.forName(smsManagerClass).newInstance();
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ LOGGER.warn("Error loading SMS Manager class : " + smsManagerClass, e);
}
+ }
- String webUrl = URIUtil.newURI("http", address, config.getInteger("web.port", 8082), "", "");
- webUrl = Context.getConfig().getString("web.url", webUrl);
- velocityProperties.setProperty("web.url", webUrl);
-
- velocityEngine = new VelocityEngine();
- velocityEngine.init(velocityProperties);
-
- motionEventHandler = new MotionEventHandler(tripsConfig);
- overspeedEventHandler = new OverspeedEventHandler(
- Context.getConfig().getLong("event.overspeed.minimalDuration") * 1000,
- Context.getConfig().getBoolean("event.overspeed.notRepeat"));
+ if (config.getBoolean("event.enable")) {
+ initEventsModule();
}
serverManager = new ServerManager();
if (config.getBoolean("event.forward.enable")) {
- eventForwarder = new EventForwarder();
+ eventForwarder = new JsonTypeEventForwarder();
}
attributesManager = new AttributesManager(dataManager);
driversManager = new DriversManager(dataManager);
- commandsManager = new CommandsManager(dataManager);
+ commandsManager = new CommandsManager(dataManager, config.getBoolean("commands.queueing"));
- statisticsManager = new StatisticsManager();
+ }
+
+ private static void initEventsModule() {
- if (config.getBoolean("sms.smpp.enable")) {
- smppClient = new SmppClient();
+ 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("web.address", InetAddress.getLocalHost().getHostAddress());
+ } catch (UnknownHostException e) {
+ address = "localhost";
}
+ String webUrl = URIUtil.newURI("http", address, config.getInteger("web.port", 8082), "", "");
+ webUrl = Context.getConfig().getString("web.url", webUrl);
+ velocityProperties.setProperty("web.url", webUrl);
+
+ velocityEngine = new VelocityEngine();
+ velocityEngine.init(velocityProperties);
}
- public static void init(IdentityManager testIdentityManager) {
+ 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) {
@@ -436,6 +399,8 @@ public final class Context {
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;
}
diff --git a/src/org/traccar/DeviceSession.java b/src/main/java/org/traccar/DeviceSession.java
index 36958287d..322381807 100644
--- a/src/org/traccar/DeviceSession.java
+++ b/src/main/java/org/traccar/DeviceSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,6 +15,8 @@
*/
package org.traccar;
+import java.util.TimeZone;
+
public class DeviceSession {
private final long deviceId;
@@ -27,4 +29,14 @@ public class DeviceSession {
return deviceId;
}
+ private TimeZone timeZone;
+
+ public void setTimeZone(TimeZone timeZone) {
+ this.timeZone = timeZone;
+ }
+
+ public TimeZone getTimeZone() {
+ return timeZone;
+ }
+
}
diff --git a/src/main/java/org/traccar/EventLoopGroupFactory.java b/src/main/java/org/traccar/EventLoopGroupFactory.java
new file mode 100644
index 000000000..482559253
--- /dev/null
+++ b/src/main/java/org/traccar/EventLoopGroupFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+
+public final class EventLoopGroupFactory {
+
+ private static EventLoopGroup bossGroup = new NioEventLoopGroup();
+ private static EventLoopGroup workerGroup = new NioEventLoopGroup();
+
+ private EventLoopGroupFactory() {
+ }
+
+ public static EventLoopGroup getBossGroup() {
+ return bossGroup;
+ }
+
+ public static EventLoopGroup getWorkerGroup() {
+ return workerGroup;
+ }
+
+}
diff --git a/src/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java
index 268e6f688..681924e87 100644
--- a/src/org/traccar/ExtendedObjectDecoder.java
+++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,65 +15,57 @@
*/
package org.traccar;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelEvent;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelUpstreamHandler;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.MessageEvent;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
-import javax.xml.bind.DatatypeConverter;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
-public abstract class ExtendedObjectDecoder implements ChannelUpstreamHandler {
+public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter {
private void saveOriginal(Object decodedMessage, Object originalMessage) {
if (Context.getConfig().getBoolean("database.saveOriginal") && decodedMessage instanceof Position) {
Position position = (Position) decodedMessage;
- if (originalMessage instanceof ChannelBuffer) {
- ChannelBuffer buf = (ChannelBuffer) originalMessage;
- position.set(Position.KEY_ORIGINAL, ChannelBuffers.hexDump(buf, 0, buf.writerIndex()));
+ if (originalMessage instanceof ByteBuf) {
+ ByteBuf buf = (ByteBuf) originalMessage;
+ position.set(Position.KEY_ORIGINAL, ByteBufUtil.hexDump(buf));
} else if (originalMessage instanceof String) {
- position.set(Position.KEY_ORIGINAL, DatatypeConverter.printHexBinary(
+ position.set(Position.KEY_ORIGINAL, DataConverter.printHex(
((String) originalMessage).getBytes(StandardCharsets.US_ASCII)));
}
}
}
@Override
- public void handleUpstream(
- ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
- if (!(evt instanceof MessageEvent)) {
- ctx.sendUpstream(evt);
- return;
- }
-
- MessageEvent e = (MessageEvent) evt;
- Object originalMessage = e.getMessage();
- Object decodedMessage = decode(e.getChannel(), e.getRemoteAddress(), originalMessage);
- onMessageEvent(e.getChannel(), e.getRemoteAddress(), originalMessage, decodedMessage);
- if (originalMessage == decodedMessage) {
- ctx.sendUpstream(evt);
- } else {
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ NetworkMessage networkMessage = (NetworkMessage) msg;
+ Object originalMessage = networkMessage.getMessage();
+ try {
+ Object decodedMessage = decode(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage);
+ onMessageEvent(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage, decodedMessage);
if (decodedMessage == null) {
- decodedMessage = handleEmptyMessage(e.getChannel(), e.getRemoteAddress(), originalMessage);
+ decodedMessage = handleEmptyMessage(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage);
}
if (decodedMessage != null) {
if (decodedMessage instanceof Collection) {
for (Object o : (Collection) decodedMessage) {
saveOriginal(o, originalMessage);
- Channels.fireMessageReceived(ctx, o, e.getRemoteAddress());
+ ctx.fireChannelRead(o);
}
} else {
saveOriginal(decodedMessage, originalMessage);
- Channels.fireMessageReceived(ctx, decodedMessage, e.getRemoteAddress());
+ ctx.fireChannelRead(decodedMessage);
}
}
+ } finally {
+ ReferenceCountUtil.release(originalMessage);
}
}
diff --git a/src/org/traccar/GlobalTimer.java b/src/main/java/org/traccar/GlobalTimer.java
index 70e6e2e45..a97321ba2 100644
--- a/src/org/traccar/GlobalTimer.java
+++ b/src/main/java/org/traccar/GlobalTimer.java
@@ -15,8 +15,8 @@
*/
package org.traccar;
-import org.jboss.netty.util.HashedWheelTimer;
-import org.jboss.netty.util.Timer;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
public final class GlobalTimer {
diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java
new file mode 100644
index 000000000..6ebd1d399
--- /dev/null
+++ b/src/main/java/org/traccar/Main.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2012 - 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;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.nio.charset.Charset;
+import java.sql.SQLException;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.Locale;
+
+public final class Main {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
+
+ private static final long CLEAN_PERIOD = 24 * 60 * 60 * 1000;
+
+ private static Injector injector;
+
+ public static Injector getInjector() {
+ return injector;
+ }
+
+ private Main() {
+ }
+
+ public static void logSystemInfo() {
+ try {
+ OperatingSystemMXBean operatingSystemBean = ManagementFactory.getOperatingSystemMXBean();
+ LOGGER.info("Operating system"
+ + " name: " + operatingSystemBean.getName()
+ + " version: " + operatingSystemBean.getVersion()
+ + " architecture: " + operatingSystemBean.getArch());
+
+ RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
+ LOGGER.info("Java runtime"
+ + " name: " + runtimeBean.getVmName()
+ + " vendor: " + runtimeBean.getVmVendor()
+ + " version: " + runtimeBean.getVmVersion());
+
+ MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
+ LOGGER.info("Memory limit"
+ + " heap: " + memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024) + "mb"
+ + " non-heap: " + memoryBean.getNonHeapMemoryUsage().getMax() / (1024 * 1024) + "mb");
+
+ LOGGER.info("Character encoding: "
+ + System.getProperty("file.encoding") + " charset: " + Charset.defaultCharset());
+
+ } catch (Exception error) {
+ LOGGER.warn("Failed to get system info");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Locale.setDefault(Locale.ENGLISH);
+
+ if (args.length <= 0) {
+ throw new RuntimeException("Configuration file is not provided");
+ }
+
+ final String configFile = args[args.length - 1];
+
+ if (args[0].startsWith("--")) {
+ WindowsService windowsService = new WindowsService("traccar") {
+ @Override
+ public void run() {
+ Main.run(configFile);
+ }
+ };
+ switch (args[0]) {
+ case "--install":
+ windowsService.install("traccar", null, null, null, null, configFile);
+ return;
+ case "--uninstall":
+ windowsService.uninstall();
+ return;
+ case "--service":
+ default:
+ windowsService.init();
+ break;
+ }
+ } else {
+ run(configFile);
+ }
+ }
+
+ public static void run(String configFile) {
+ try {
+ Context.init(configFile);
+ injector = Guice.createInjector(new MainModule());
+ logSystemInfo();
+ LOGGER.info("Version: " + Main.class.getPackage().getImplementationVersion());
+ LOGGER.info("Starting server...");
+
+ Context.getServerManager().start();
+ if (Context.getWebServer() != null) {
+ Context.getWebServer().start();
+ }
+
+ new Timer().scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Context.getDataManager().clearHistory();
+ } catch (SQLException error) {
+ LOGGER.warn("Clear history error", error);
+ }
+ }
+ }, 0, CLEAN_PERIOD);
+
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ LOGGER.error("Thread exception", e);
+ }
+ });
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ LOGGER.info("Shutting down server...");
+
+ if (Context.getWebServer() != null) {
+ Context.getWebServer().stop();
+ }
+ Context.getServerManager().stop();
+ }
+ });
+ } catch (Exception e) {
+ LOGGER.error("Main method error", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java
new file mode 100644
index 000000000..2309b1e70
--- /dev/null
+++ b/src/main/java/org/traccar/MainEventHandler.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2012 - 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;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.timeout.IdleStateEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.database.StatisticsManager;
+import org.traccar.helper.DateUtil;
+import org.traccar.model.Position;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class MainEventHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MainEventHandler.class);
+
+ private static final String DEFAULT_LOGGER_ATTRIBUTES = "time,position,speed,course,accuracy,result";
+
+ private final Set<String> connectionlessProtocols = new HashSet<>();
+ private final Set<String> logAttributes = new LinkedHashSet<>();
+
+ public MainEventHandler() {
+ String connectionlessProtocolList = Context.getConfig().getString("status.ignoreOffline");
+ if (connectionlessProtocolList != null) {
+ connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split("[, ]")));
+ }
+ logAttributes.addAll(Arrays.asList(
+ Context.getConfig().getString("logger.attributes", DEFAULT_LOGGER_ATTRIBUTES).split("[, ]")));
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ if (msg instanceof Position) {
+
+ Position position = (Position) msg;
+ try {
+ Context.getDeviceManager().updateLatestPosition(position);
+ } catch (SQLException 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);
+ for (String attribute : logAttributes) {
+ switch (attribute) {
+ case "time":
+ builder.append(", time: ").append(DateUtil.formatDate(position.getFixTime(), false));
+ break;
+ case "position":
+ builder.append(", lat: ").append(String.format("%.5f", position.getLatitude()));
+ builder.append(", lon: ").append(String.format("%.5f", position.getLongitude()));
+ break;
+ case "speed":
+ if (position.getSpeed() > 0) {
+ builder.append(", speed: ").append(String.format("%.1f", position.getSpeed()));
+ }
+ break;
+ case "course":
+ builder.append(", course: ").append(String.format("%.1f", position.getCourse()));
+ break;
+ case "accuracy":
+ if (position.getAccuracy() > 0) {
+ builder.append(", accuracy: ").append(String.format("%.1f", position.getAccuracy()));
+ }
+ break;
+ case "outdated":
+ if (position.getOutdated()) {
+ builder.append(", outdated");
+ }
+ break;
+ case "invalid":
+ if (!position.getValid()) {
+ builder.append(", invalid");
+ }
+ break;
+ default:
+ Object value = position.getAttributes().get(attribute);
+ if (value != null) {
+ builder.append(", ").append(attribute).append(": ").append(value);
+ }
+ break;
+ }
+ }
+ LOGGER.info(builder.toString());
+
+ Main.getInjector().getInstance(StatisticsManager.class).registerMessageStored(position.getDeviceId());
+ }
+ }
+
+ 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");
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+ LOGGER.info(formatChannel(ctx.channel()) + " disconnected");
+ closeChannel(ctx.channel());
+
+ if (BasePipelineFactory.getHandler(ctx.pipeline(), HttpRequestDecoder.class) == null
+ && !connectionlessProtocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName())) {
+ Context.getConnectionManager().removeActiveDevice(ctx.channel());
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ while (cause.getCause() != null && cause.getCause() != cause) {
+ cause = cause.getCause();
+ }
+ LOGGER.warn(formatChannel(ctx.channel()) + " error", cause);
+ closeChannel(ctx.channel());
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
+ if (evt instanceof IdleStateEvent) {
+ LOGGER.info(formatChannel(ctx.channel()) + " timed out");
+ closeChannel(ctx.channel());
+ }
+ }
+
+ private void closeChannel(Channel channel) {
+ if (!(channel instanceof DatagramChannel)) {
+ channel.close();
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
new file mode 100644
index 000000000..0957d9fe3
--- /dev/null
+++ b/src/main/java/org/traccar/MainModule.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2018 - 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;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.AttributesManager;
+import org.traccar.database.CalendarManager;
+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.StatisticsManager;
+import org.traccar.geocoder.AddressFormat;
+import org.traccar.geocoder.BanGeocoder;
+import org.traccar.geocoder.BingMapsGeocoder;
+import org.traccar.geocoder.FactualGeocoder;
+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.MapQuestGeocoder;
+import org.traccar.geocoder.MapmyIndiaGeocoder;
+import org.traccar.geocoder.NominatimGeocoder;
+import org.traccar.geocoder.OpenCageGeocoder;
+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.TimeHandler;
+import org.traccar.handler.events.AlertEventHandler;
+import org.traccar.handler.events.CommandResultEventHandler;
+import org.traccar.handler.events.DriverEventHandler;
+import org.traccar.handler.events.FuelDropEventHandler;
+import org.traccar.handler.events.GeofenceEventHandler;
+import org.traccar.handler.events.IgnitionEventHandler;
+import org.traccar.handler.events.MaintenanceEventHandler;
+import org.traccar.handler.events.MotionEventHandler;
+import org.traccar.handler.events.OverspeedEventHandler;
+import org.traccar.reports.model.TripsConfig;
+
+import javax.annotation.Nullable;
+import javax.ws.rs.client.Client;
+
+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();
+ }
+
+ @Provides
+ public static Client provideClient() {
+ return Context.getClient();
+ }
+
+ @Provides
+ public static TripsConfig provideTripsConfig() {
+ return Context.getTripsConfig();
+ }
+
+ @Provides
+ public static DeviceManager provideDeviceManager() {
+ return Context.getDeviceManager();
+ }
+
+ @Provides
+ public static GeofenceManager provideGeofenceManager() {
+ return Context.getGeofenceManager();
+ }
+
+ @Provides
+ public static CalendarManager provideCalendarManager() {
+ return Context.getCalendarManager();
+ }
+
+ @Provides
+ public static AttributesManager provideAttributesManager() {
+ return Context.getAttributesManager();
+ }
+
+ @Provides
+ public static MaintenancesManager provideMaintenancesManager() {
+ return Context.getMaintenancesManager();
+ }
+
+ @Singleton
+ @Provides
+ public static StatisticsManager provideStatisticsManager(Config config, DataManager dataManager, Client client) {
+ return new StatisticsManager(config, dataManager, client);
+ }
+
+ @Singleton
+ @Provides
+ public static Geocoder provideGeocoder(Config config) {
+ if (config.getBoolean(Keys.GEOCODER_ENABLE)) {
+ String type = config.getString(Keys.GEOCODER_TYPE, "google");
+ String url = config.getString(Keys.GEOCODER_URL);
+ String id = config.getString(Keys.GEOCODER_ID);
+ String key = config.getString(Keys.GEOCODER_KEY);
+ String language = config.getString(Keys.GEOCODER_LANGUAGE);
+ String formatString = config.getString(Keys.GEOCODER_FORMAT);
+ AddressFormat addressFormat = formatString != null ? new AddressFormat(formatString) : new AddressFormat();
+
+ int cacheSize = config.getInteger(Keys.GEOCODER_CACHE_SIZE);
+ switch (type) {
+ case "nominatim":
+ return new NominatimGeocoder(url, key, language, cacheSize, addressFormat);
+ case "gisgraphy":
+ return new GisgraphyGeocoder(url, cacheSize, addressFormat);
+ case "mapquest":
+ return new MapQuestGeocoder(url, key, cacheSize, addressFormat);
+ case "opencage":
+ return new OpenCageGeocoder(url, key, cacheSize, addressFormat);
+ case "bingmaps":
+ return new BingMapsGeocoder(url, key, cacheSize, addressFormat);
+ case "factual":
+ return new FactualGeocoder(url, key, cacheSize, addressFormat);
+ case "geocodefarm":
+ return new GeocodeFarmGeocoder(key, language, cacheSize, addressFormat);
+ case "geocodexyz":
+ return new GeocodeXyzGeocoder(key, cacheSize, addressFormat);
+ case "ban":
+ return new BanGeocoder(cacheSize, addressFormat);
+ case "here":
+ return new HereGeocoder(url, id, key, language, cacheSize, addressFormat);
+ case "mapmyindia":
+ return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat);
+ default:
+ return new GoogleGeocoder(key, language, cacheSize, addressFormat);
+ }
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static GeolocationProvider provideGeolocationProvider(Config config) {
+ 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);
+ case "opencellid":
+ return new OpenCellIdGeolocationProvider(url, key);
+ case "unwired":
+ return new UnwiredGeolocationProvider(url, key);
+ default:
+ return new MozillaGeolocationProvider(key);
+ }
+ }
+ return null;
+ }
+
+ @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.getBoolean(Keys.FORWARD_ENABLE)) {
+ return new WebDataHandler(config, identityManager, objectMapper, client);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static GeolocationHandler provideGeolocationHandler(
+ Config config, @Nullable GeolocationProvider geolocationProvider, StatisticsManager statisticsManager) {
+ if (geolocationProvider != null) {
+ return new GeolocationHandler(config, geolocationProvider, statisticsManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static GeocoderHandler provideGeocoderHandler(
+ Config config, @Nullable Geocoder geocoder, IdentityManager identityManager,
+ StatisticsManager statisticsManager) {
+ if (geocoder != null) {
+ return new GeocoderHandler(config, geocoder, identityManager, statisticsManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static MotionHandler provideMotionHandler(TripsConfig tripsConfig) {
+ return new MotionHandler(tripsConfig.getSpeedThreshold());
+ }
+
+ @Singleton
+ @Provides
+ public static EngineHoursHandler provideEngineHoursHandler(Config config, IdentityManager identityManager) {
+ if (config.getBoolean(Keys.PROCESSING_ENGINE_HOURS_ENABLE)) {
+ return new EngineHoursHandler(identityManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static CopyAttributesHandler provideCopyAttributesHandler(Config config, IdentityManager identityManager) {
+ if (config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE)) {
+ return new CopyAttributesHandler(identityManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static ComputedAttributesHandler provideComputedAttributesHandler(
+ Config config, IdentityManager identityManager, AttributesManager attributesManager) {
+ if (config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_ENABLE)) {
+ return new ComputedAttributesHandler(config, identityManager, attributesManager);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static TimeHandler provideTimeHandler(Config config) {
+ if (config.hasKey(Keys.TIME_OVERRIDE)) {
+ return new TimeHandler(config);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
+ public static DefaultDataHandler provideDefaultDataHandler(@Nullable DataManager dataManager) {
+ if (dataManager != null) {
+ return new DefaultDataHandler(dataManager);
+ }
+ 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 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) {
+ return new GeofenceEventHandler(identityManager, geofenceManager, calendarManager);
+ }
+
+ @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);
+ }
+
+ @Override
+ protected void configure() {
+ binder().requireExplicitBindings();
+ }
+
+}
diff --git a/src/main/java/org/traccar/NetworkMessage.java b/src/main/java/org/traccar/NetworkMessage.java
new file mode 100644
index 000000000..14a397e69
--- /dev/null
+++ b/src/main/java/org/traccar/NetworkMessage.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.net.SocketAddress;
+
+public class NetworkMessage {
+
+ private final SocketAddress remoteAddress;
+ private final Object message;
+
+ public NetworkMessage(Object message, SocketAddress remoteAddress) {
+ this.message = message;
+ this.remoteAddress = remoteAddress;
+ }
+
+ public SocketAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ public Object getMessage() {
+ return message;
+ }
+
+}
diff --git a/src/main/java/org/traccar/PipelineBuilder.java b/src/main/java/org/traccar/PipelineBuilder.java
new file mode 100644
index 000000000..3334040b1
--- /dev/null
+++ b/src/main/java/org/traccar/PipelineBuilder.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public interface PipelineBuilder {
+
+ void addLast(ChannelHandler handler);
+
+}
diff --git a/src/main/java/org/traccar/Protocol.java b/src/main/java/org/traccar/Protocol.java
new file mode 100644
index 000000000..3b66f2598
--- /dev/null
+++ b/src/main/java/org/traccar/Protocol.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.traccar.database.ActiveDevice;
+import org.traccar.model.Command;
+
+import java.util.Collection;
+
+public interface Protocol {
+
+ String getName();
+
+ Collection<TrackerServer> getServerList();
+
+ Collection<String> getSupportedDataCommands();
+
+ void sendDataCommand(ActiveDevice activeDevice, Command command);
+
+ Collection<String> getSupportedTextCommands();
+
+ void sendTextCommand(String destAddress, Command command) throws Exception;
+
+}
diff --git a/src/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java
index 9b1e2650d..6a3273402 100644
--- a/src/org/traccar/ServerManager.java
+++ b/src/main/java/org/traccar/ServerManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,7 +15,11 @@
*/
package org.traccar;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.File;
+import java.net.BindException;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
@@ -30,6 +34,8 @@ import java.util.jar.JarFile;
public class ServerManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class);
+
private final List<TrackerServer> serverList = new LinkedList<>();
private final Map<String, BaseProtocol> protocolList = new ConcurrentHashMap<>();
@@ -38,7 +44,7 @@ public class ServerManager {
List<String> names = new LinkedList<>();
String packageName = "org.traccar.protocol";
String packagePath = packageName.replace('.', '/');
- URL packageUrl = Thread.currentThread().getContextClassLoader().getResource(packagePath);
+ URL packageUrl = getClass().getClassLoader().getResource(packagePath);
if (packageUrl.getProtocol().equals("jar")) {
String jarFileName = URLDecoder.decode(packageUrl.getFile(), StandardCharsets.UTF_8.name());
@@ -64,10 +70,11 @@ public class ServerManager {
for (String name : names) {
Class protocolClass = Class.forName(packageName + '.' + name);
- if (BaseProtocol.class.isAssignableFrom(protocolClass)) {
- BaseProtocol baseProtocol = (BaseProtocol) protocolClass.newInstance();
- initProtocolServer(baseProtocol);
- protocolList.put(baseProtocol.getName(), baseProtocol);
+ if (BaseProtocol.class.isAssignableFrom(protocolClass)
+ && Context.getConfig().hasKey(BaseProtocol.nameFromClass(protocolClass) + ".port")) {
+ BaseProtocol protocol = (BaseProtocol) protocolClass.newInstance();
+ serverList.addAll(protocol.getServerList());
+ protocolList.put(protocol.getName(), protocol);
}
}
}
@@ -76,9 +83,13 @@ public class ServerManager {
return protocolList.get(name);
}
- public void start() {
+ public void start() throws Exception {
for (TrackerServer server: serverList) {
- server.start();
+ try {
+ server.start();
+ } catch (BindException e) {
+ LOGGER.warn("Port {} is disabled due to conflict", server.getPort());
+ }
}
}
@@ -86,16 +97,7 @@ public class ServerManager {
for (TrackerServer server: serverList) {
server.stop();
}
-
- // Release resources
- GlobalChannelFactory.release();
GlobalTimer.release();
}
- private void initProtocolServer(final Protocol protocol) {
- if (Context.getConfig().hasKey(protocol.getName() + ".port")) {
- protocol.initTrackerServers(serverList);
- }
- }
-
}
diff --git a/src/org/traccar/StringProtocolEncoder.java b/src/main/java/org/traccar/StringProtocolEncoder.java
index 1945ae174..e9fb65500 100644
--- a/src/org/traccar/StringProtocolEncoder.java
+++ b/src/main/java/org/traccar/StringProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,31 +17,39 @@ package org.traccar;
import org.traccar.model.Command;
-import java.util.Map;
-
public abstract class StringProtocolEncoder extends BaseProtocolEncoder {
+ public StringProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
public interface ValueFormatter {
String formatValue(String key, Object value);
}
protected String formatCommand(Command command, String format, ValueFormatter valueFormatter, String... keys) {
- String result = String.format(format, (Object[]) keys);
-
- result = result.replaceAll("\\{" + Command.KEY_UNIQUE_ID + "}", getUniqueId(command.getDeviceId()));
- for (Map.Entry<String, Object> entry : command.getAttributes().entrySet()) {
+ Object[] values = new String[keys.length];
+ for (int i = 0; i < keys.length; i++) {
String value = null;
- if (valueFormatter != null) {
- value = valueFormatter.formatValue(entry.getKey(), entry.getValue());
- }
- if (value == null) {
- value = entry.getValue().toString();
+ if (keys[i].equals(Command.KEY_UNIQUE_ID)) {
+ value = getUniqueId(command.getDeviceId());
+ } else {
+ Object object = command.getAttributes().get(keys[i]);
+ if (valueFormatter != null) {
+ value = valueFormatter.formatValue(keys[i], object);
+ }
+ if (value == null && object != null) {
+ value = object.toString();
+ }
+ if (value == null) {
+ value = "";
+ }
}
- result = result.replaceAll("\\{" + entry.getKey() + "}", value);
+ values[i] = value;
}
- return result;
+ return String.format(format, values);
}
protected String formatCommand(Command command, String format, String... keys) {
diff --git a/src/main/java/org/traccar/TrackerServer.java b/src/main/java/org/traccar/TrackerServer.java
new file mode 100644
index 000000000..3a1e1c4e8
--- /dev/null
+++ b/src/main/java/org/traccar/TrackerServer.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+import io.netty.bootstrap.AbstractBootstrap;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+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.util.concurrent.GlobalEventExecutor;
+
+import java.net.InetSocketAddress;
+
+public abstract class TrackerServer {
+
+ private final boolean datagram;
+ private final AbstractBootstrap bootstrap;
+
+ public boolean isDatagram() {
+ return datagram;
+ }
+
+ public TrackerServer(boolean datagram, String protocol) {
+ this.datagram = datagram;
+
+ address = Context.getConfig().getString(protocol + ".address");
+ port = Context.getConfig().getInteger(protocol + ".port");
+
+ BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, protocol) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ TrackerServer.this.addProtocolHandlers(pipeline);
+ }
+ };
+
+ if (datagram) {
+
+ this.bootstrap = new Bootstrap()
+ .group(EventLoopGroupFactory.getWorkerGroup())
+ .channel(NioDatagramChannel.class)
+ .handler(pipelineFactory);
+
+ } else {
+
+ this.bootstrap = new ServerBootstrap()
+ .group(EventLoopGroupFactory.getBossGroup(), EventLoopGroupFactory.getWorkerGroup())
+ .channel(NioServerSocketChannel.class)
+ .childHandler(pipelineFactory);
+
+ }
+ }
+
+ protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
+
+ private int port;
+
+ 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);
+
+ public ChannelGroup getChannelGroup() {
+ return channelGroup;
+ }
+
+ public void start() throws Exception {
+ InetSocketAddress endpoint;
+ if (address == null) {
+ endpoint = new InetSocketAddress(port);
+ } else {
+ endpoint = new InetSocketAddress(address, port);
+ }
+
+ Channel channel = bootstrap.bind(endpoint).sync().channel();
+ if (channel != null) {
+ getChannelGroup().add(channel);
+ }
+ }
+
+ public void stop() {
+ channelGroup.close().awaitUninterruptibly();
+ }
+
+}
diff --git a/src/org/traccar/WebDataHandler.java b/src/main/java/org/traccar/WebDataHandler.java
index c64dcc81b..64396de03 100644
--- a/src/org/traccar/WebDataHandler.java
+++ b/src/main/java/org/traccar/WebDataHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -16,11 +16,22 @@
package org.traccar;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.netty.channel.ChannelHandler;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.IdentityManager;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.model.Device;
import org.traccar.model.Position;
-
+import org.traccar.model.Group;
+
+import javax.inject.Inject;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import java.util.HashMap;
+import java.util.Map;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@@ -29,12 +40,29 @@ import java.util.Formatter;
import java.util.Locale;
import java.util.TimeZone;
+@ChannelHandler.Sharable
public class WebDataHandler extends BaseDataHandler {
- private final String url;
+ private static final String KEY_POSITION = "position";
+ private static final String KEY_DEVICE = "device";
- public WebDataHandler(String url) {
- this.url = url;
+ private final IdentityManager identityManager;
+ private final ObjectMapper objectMapper;
+ private final Client client;
+
+ private final String url;
+ private final String header;
+ private final boolean json;
+
+ @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);
}
private static String formatSentence(Position position) {
@@ -73,13 +101,14 @@ public class WebDataHandler extends BaseDataHandler {
}
}
- public String formatRequest(Position position) {
+ public String formatRequest(Position position) throws UnsupportedEncodingException, JsonProcessingException {
- Device device = Context.getIdentityManager().getById(position.getDeviceId());
+ Device device = identityManager.getById(position.getDeviceId());
String request = url
- .replace("{name}", device.getName())
+ .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()))
@@ -90,40 +119,83 @@ public class WebDataHandler extends BaseDataHandler {
.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) {
- try {
- request = request.replace(
- "{address}", URLEncoder.encode(position.getAddress(), StandardCharsets.UTF_8.name()));
- } catch (UnsupportedEncodingException error) {
- Log.warning(error);
- }
+ request = request.replace(
+ "{address}", URLEncoder.encode(position.getAddress(), StandardCharsets.UTF_8.name()));
}
if (request.contains("{attributes}")) {
- try {
- String attributes = Context.getObjectMapper().writeValueAsString(position.getAttributes());
- request = request.replace(
- "{attributes}", URLEncoder.encode(attributes, StandardCharsets.UTF_8.name()));
- } catch (UnsupportedEncodingException | JsonProcessingException error) {
- Log.warning(error);
- }
+ 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;
}
@Override
protected Position handlePosition(Position position) {
- Context.getAsyncHttpClient().prepareGet(formatRequest(position)).execute();
+ String url;
+ if (json) {
+ url = this.url;
+ } else {
+ try {
+ url = formatRequest(position);
+ } catch (UnsupportedEncodingException | JsonProcessingException e) {
+ throw new RuntimeException("Forwarding formatting error", e);
+ }
+ }
+
+ Invocation.Builder 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());
+ }
+ }
+
+ if (json) {
+ requestBuilder.async().post(Entity.json(prepareJsonPayload(position)));
+ } else {
+ requestBuilder.async().get();
+ }
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/WindowsService.java b/src/main/java/org/traccar/WindowsService.java
new file mode 100644
index 000000000..4a8955608
--- /dev/null
+++ b/src/main/java/org/traccar/WindowsService.java
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.sun.jna.Pointer;
+import com.sun.jna.platform.win32.Advapi32;
+import com.sun.jna.platform.win32.WinError;
+import com.sun.jna.platform.win32.WinNT;
+import com.sun.jna.platform.win32.Winsvc;
+import com.sun.jna.platform.win32.Winsvc.HandlerEx;
+import com.sun.jna.platform.win32.Winsvc.SC_HANDLE;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_DESCRIPTION;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_MAIN_FUNCTION;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_HANDLE;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_TABLE_ENTRY;
+import jnr.posix.POSIXFactory;
+
+import java.io.File;
+import java.net.URISyntaxException;
+
+public abstract class WindowsService {
+
+ private static final Advapi32 ADVAPI_32 = Advapi32.INSTANCE;
+
+ private final Object waitObject = new Object();
+
+ private String serviceName;
+ private ServiceMain serviceMain;
+ private ServiceControl serviceControl;
+ private SERVICE_STATUS_HANDLE serviceStatusHandle;
+
+ public WindowsService(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public boolean install(
+ String displayName, String description, String[] dependencies,
+ String account, String password, String config) throws URISyntaxException {
+
+ String javaHome = System.getProperty("java.home");
+ String javaBinary = javaHome + "\\bin\\java.exe";
+
+ File jar = new File(WindowsService.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+ String command = javaBinary
+ + " -Duser.dir=\"" + jar.getParentFile().getAbsolutePath() + "\""
+ + " -jar \"" + jar.getAbsolutePath() + "\""
+ + " --service \"" + config + "\"";
+
+ boolean success = false;
+ StringBuilder dep = new StringBuilder();
+
+ if (dependencies != null) {
+ for (String s : dependencies) {
+ dep.append(s);
+ dep.append("\0");
+ }
+ }
+ dep.append("\0");
+
+ SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION();
+ desc.lpDescription = description;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = ADVAPI_32.CreateService(serviceManager, serviceName, displayName,
+ Winsvc.SERVICE_ALL_ACCESS, WinNT.SERVICE_WIN32_OWN_PROCESS, WinNT.SERVICE_AUTO_START,
+ WinNT.SERVICE_ERROR_NORMAL,
+ command,
+ null, null, dep.toString(), account, password);
+
+ if (service != null) {
+ success = ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc);
+ ADVAPI_32.CloseServiceHandle(service);
+ }
+ ADVAPI_32.CloseServiceHandle(serviceManager);
+ }
+ return success;
+ }
+
+ public boolean uninstall() {
+ boolean success = false;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS);
+
+ if (service != null) {
+ success = ADVAPI_32.DeleteService(service);
+ ADVAPI_32.CloseServiceHandle(service);
+ }
+ ADVAPI_32.CloseServiceHandle(serviceManager);
+ }
+ return success;
+ }
+
+ public boolean start() {
+ boolean success = false;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE);
+
+ if (service != null) {
+ success = ADVAPI_32.StartService(service, 0, null);
+ ADVAPI_32.CloseServiceHandle(service);
+ }
+ ADVAPI_32.CloseServiceHandle(serviceManager);
+ }
+
+ return success;
+ }
+
+ public boolean stop() {
+ boolean success = false;
+
+ SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE);
+
+ if (serviceManager != null) {
+ SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE);
+
+ if (service != null) {
+ SERVICE_STATUS serviceStatus = new SERVICE_STATUS();
+ success = Advapi32.INSTANCE.ControlService(service, Winsvc.SERVICE_CONTROL_STOP, serviceStatus);
+ Advapi32.INSTANCE.CloseServiceHandle(service);
+ }
+ Advapi32.INSTANCE.CloseServiceHandle(serviceManager);
+ }
+
+ return success;
+ }
+
+ public void init() throws URISyntaxException {
+ String path = new File(
+ WindowsService.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent();
+
+ POSIXFactory.getPOSIX().chdir(path);
+
+ serviceMain = new ServiceMain();
+ SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY();
+ entry.lpServiceName = serviceName;
+ entry.lpServiceProc = serviceMain;
+
+ Advapi32.INSTANCE.StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY[]) entry.toArray(2));
+ }
+
+ private SC_HANDLE openServiceControlManager(String machine, int access) {
+ return ADVAPI_32.OpenSCManager(machine, null, access);
+ }
+
+ private void reportStatus(int status, int win32ExitCode, int waitHint) {
+ SERVICE_STATUS serviceStatus = new SERVICE_STATUS();
+ serviceStatus.dwServiceType = WinNT.SERVICE_WIN32_OWN_PROCESS;
+ serviceStatus.dwControlsAccepted = Winsvc.SERVICE_ACCEPT_STOP | Winsvc.SERVICE_ACCEPT_SHUTDOWN;
+ serviceStatus.dwWin32ExitCode = win32ExitCode;
+ serviceStatus.dwWaitHint = waitHint;
+ serviceStatus.dwCurrentState = status;
+
+ ADVAPI_32.SetServiceStatus(serviceStatusHandle, serviceStatus);
+ }
+
+ public abstract void run();
+
+ private class ServiceMain implements SERVICE_MAIN_FUNCTION {
+
+ public void callback(int dwArgc, Pointer lpszArgv) {
+ serviceControl = new ServiceControl();
+ serviceStatusHandle = ADVAPI_32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null);
+
+ reportStatus(Winsvc.SERVICE_START_PENDING, WinError.NO_ERROR, 3000);
+ reportStatus(Winsvc.SERVICE_RUNNING, WinError.NO_ERROR, 0);
+
+ Thread.currentThread().setContextClassLoader(WindowsService.class.getClassLoader());
+
+ run();
+
+ try {
+ synchronized (waitObject) {
+ waitObject.wait();
+ }
+ } catch (InterruptedException ex) {
+ }
+
+ reportStatus(Winsvc.SERVICE_STOPPED, WinError.NO_ERROR, 0);
+
+ // Avoid returning from ServiceMain, which will cause a crash
+ // See http://support.microsoft.com/kb/201349, which recommends
+ // having init() wait for this thread.
+ // Waiting on this thread in init() won't fix the crash, though.
+
+ System.exit(0);
+ }
+
+ }
+
+ private class ServiceControl implements HandlerEx {
+
+ public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) {
+ switch (dwControl) {
+ case Winsvc.SERVICE_CONTROL_STOP:
+ case Winsvc.SERVICE_CONTROL_SHUTDOWN:
+ reportStatus(Winsvc.SERVICE_STOP_PENDING, WinError.NO_ERROR, 5000);
+ synchronized (waitObject) {
+ waitObject.notifyAll();
+ }
+ break;
+ default:
+ break;
+ }
+ return WinError.NO_ERROR;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/traccar/WrapperContext.java b/src/main/java/org/traccar/WrapperContext.java
new file mode 100644
index 000000000..372d3c60d
--- /dev/null
+++ b/src/main/java/org/traccar/WrapperContext.java
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.buffer.ByteBufAllocator;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.ChannelProgressivePromise;
+import io.netty.channel.ChannelPromise;
+import io.netty.util.Attribute;
+import io.netty.util.AttributeKey;
+import io.netty.util.concurrent.EventExecutor;
+
+import java.net.SocketAddress;
+
+public class WrapperContext implements ChannelHandlerContext {
+
+ private ChannelHandlerContext context;
+ private SocketAddress remoteAddress;
+
+ public WrapperContext(ChannelHandlerContext context, SocketAddress remoteAddress) {
+ this.context = context;
+ this.remoteAddress = remoteAddress;
+ }
+
+ @Override
+ public Channel channel() {
+ return context.channel();
+ }
+
+ @Override
+ public EventExecutor executor() {
+ return context.executor();
+ }
+
+ @Override
+ public String name() {
+ return context.name();
+ }
+
+ @Override
+ public ChannelHandler handler() {
+ return context.handler();
+ }
+
+ @Override
+ public boolean isRemoved() {
+ return context.isRemoved();
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelRegistered() {
+ return context.fireChannelRegistered();
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelUnregistered() {
+ return context.fireChannelUnregistered();
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelActive() {
+ return context.fireChannelActive();
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelInactive() {
+ return context.fireChannelInactive();
+ }
+
+ @Override
+ public ChannelHandlerContext fireExceptionCaught(Throwable cause) {
+ return context.fireExceptionCaught(cause);
+ }
+
+ @Override
+ public ChannelHandlerContext fireUserEventTriggered(Object evt) {
+ return context.fireUserEventTriggered(evt);
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelRead(Object msg) {
+ if (!(msg instanceof NetworkMessage)) {
+ msg = new NetworkMessage(msg, remoteAddress);
+ }
+ return context.fireChannelRead(msg);
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelReadComplete() {
+ return context.fireChannelReadComplete();
+ }
+
+ @Override
+ public ChannelHandlerContext fireChannelWritabilityChanged() {
+ return context.fireChannelWritabilityChanged();
+ }
+
+ @Override
+ public ChannelFuture bind(SocketAddress localAddress) {
+ return context.bind(localAddress);
+ }
+
+ @Override
+ public ChannelFuture connect(SocketAddress remoteAddress) {
+ return context.connect(remoteAddress);
+ }
+
+ @Override
+ public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
+ return context.connect(remoteAddress, localAddress);
+ }
+
+ @Override
+ public ChannelFuture disconnect() {
+ return context.disconnect();
+ }
+
+ @Override
+ public ChannelFuture close() {
+ return context.close();
+ }
+
+ @Override
+ public ChannelFuture deregister() {
+ return context.deregister();
+ }
+
+ @Override
+ public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
+ return context.bind(localAddress, promise);
+ }
+
+ @Override
+ public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
+ return context.connect(remoteAddress, promise);
+ }
+
+ @Override
+ public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
+ return context.connect(remoteAddress, localAddress, promise);
+ }
+
+ @Override
+ public ChannelFuture disconnect(ChannelPromise promise) {
+ return context.disconnect(promise);
+ }
+
+ @Override
+ public ChannelFuture close(ChannelPromise promise) {
+ return context.close(promise);
+ }
+
+ @Override
+ public ChannelFuture deregister(ChannelPromise promise) {
+ return context.deregister(promise);
+ }
+
+ @Override
+ public ChannelHandlerContext read() {
+ return context.read();
+ }
+
+ @Override
+ public ChannelFuture write(Object msg) {
+ return context.write(msg);
+ }
+
+ @Override
+ public ChannelFuture write(Object msg, ChannelPromise promise) {
+ if (!(msg instanceof NetworkMessage)) {
+ msg = new NetworkMessage(msg, remoteAddress);
+ }
+ return context.write(msg, promise);
+ }
+
+ @Override
+ public ChannelHandlerContext flush() {
+ return context.flush();
+ }
+
+ @Override
+ public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
+ return context.writeAndFlush(msg, promise);
+ }
+
+ @Override
+ public ChannelFuture writeAndFlush(Object msg) {
+ return context.writeAndFlush(msg);
+ }
+
+ @Override
+ public ChannelPromise newPromise() {
+ return context.newPromise();
+ }
+
+ @Override
+ public ChannelProgressivePromise newProgressivePromise() {
+ return context.newProgressivePromise();
+ }
+
+ @Override
+ public ChannelFuture newSucceededFuture() {
+ return context.newSucceededFuture();
+ }
+
+ @Override
+ public ChannelFuture newFailedFuture(Throwable cause) {
+ return context.newFailedFuture(cause);
+ }
+
+ @Override
+ public ChannelPromise voidPromise() {
+ return context.voidPromise();
+ }
+
+ @Override
+ public ChannelPipeline pipeline() {
+ return context.pipeline();
+ }
+
+ @Override
+ public ByteBufAllocator alloc() {
+ return context.alloc();
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public <T> Attribute<T> attr(AttributeKey<T> key) {
+ return context.attr(key);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public <T> boolean hasAttr(AttributeKey<T> key) {
+ return context.hasAttr(key);
+ }
+
+}
diff --git a/src/main/java/org/traccar/WrapperInboundHandler.java b/src/main/java/org/traccar/WrapperInboundHandler.java
new file mode 100644
index 000000000..ca33d021f
--- /dev/null
+++ b/src/main/java/org/traccar/WrapperInboundHandler.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandler;
+
+public class WrapperInboundHandler implements ChannelInboundHandler {
+
+ private ChannelInboundHandler handler;
+
+ public ChannelInboundHandler getWrappedHandler() {
+ return handler;
+ }
+
+ public WrapperInboundHandler(ChannelInboundHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
+ handler.channelRegistered(ctx);
+ }
+
+ @Override
+ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
+ handler.channelUnregistered(ctx);
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ handler.channelActive(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ handler.channelInactive(ctx);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof NetworkMessage) {
+ NetworkMessage nm = (NetworkMessage) msg;
+ handler.channelRead(new WrapperContext(ctx, nm.getRemoteAddress()), nm.getMessage());
+ } else {
+ handler.channelRead(ctx, msg);
+ }
+ }
+
+ @Override
+ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+ handler.channelReadComplete(ctx);
+ }
+
+ @Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+ handler.userEventTriggered(ctx, evt);
+ }
+
+ @Override
+ public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
+ handler.channelWritabilityChanged(ctx);
+ }
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ handler.handlerAdded(ctx);
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ handler.handlerRemoved(ctx);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ handler.exceptionCaught(ctx, cause);
+ }
+
+}
diff --git a/src/main/java/org/traccar/WrapperOutboundHandler.java b/src/main/java/org/traccar/WrapperOutboundHandler.java
new file mode 100644
index 000000000..0136c5b22
--- /dev/null
+++ b/src/main/java/org/traccar/WrapperOutboundHandler.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandler;
+import io.netty.channel.ChannelPromise;
+
+import java.net.SocketAddress;
+
+public class WrapperOutboundHandler implements ChannelOutboundHandler {
+
+ private ChannelOutboundHandler handler;
+
+ public ChannelOutboundHandler getWrappedHandler() {
+ return handler;
+ }
+
+ public WrapperOutboundHandler(ChannelOutboundHandler handler) {
+ this.handler = handler;
+ }
+
+ @Override
+ public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
+ handler.bind(ctx, localAddress, promise);
+ }
+
+ @Override
+ public void connect(
+ ChannelHandlerContext ctx, SocketAddress remoteAddress,
+ SocketAddress localAddress, ChannelPromise promise) throws Exception {
+ handler.connect(ctx, remoteAddress, localAddress, promise);
+ }
+
+ @Override
+ public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
+ handler.disconnect(ctx, promise);
+ }
+
+ @Override
+ public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
+ handler.close(ctx, promise);
+ }
+
+ @Override
+ public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
+ handler.deregister(ctx, promise);
+ }
+
+ @Override
+ public void read(ChannelHandlerContext ctx) throws Exception {
+ handler.read(ctx);
+ }
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ if (msg instanceof NetworkMessage) {
+ NetworkMessage nm = (NetworkMessage) msg;
+ handler.write(new WrapperContext(ctx, nm.getRemoteAddress()), nm.getMessage(), promise);
+ } else {
+ handler.write(ctx, msg, promise);
+ }
+ }
+
+ @Override
+ public void flush(ChannelHandlerContext ctx) throws Exception {
+ handler.flush(ctx);
+ }
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ handler.handlerAdded(ctx);
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ handler.handlerRemoved(ctx);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ handler.exceptionCaught(ctx, cause);
+ }
+
+}
diff --git a/src/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java
index 70523d253..906d16b5b 100644
--- a/src/org/traccar/api/AsyncSocket.java
+++ b/src/main/java/org/traccar/api/AsyncSocket.java
@@ -18,9 +18,10 @@ package org.traccar.api;
import com.fasterxml.jackson.core.JsonProcessingException;
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.Log;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -32,6 +33,8 @@ import java.util.Map;
public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.UpdateListener {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AsyncSocket.class);
+
private static final String KEY_DEVICES = "devices";
private static final String KEY_POSITIONS = "positions";
private static final String KEY_EVENTS = "events";
@@ -86,7 +89,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
try {
getRemote().sendString(Context.getObjectMapper().writeValueAsString(data), null);
} catch (JsonProcessingException e) {
- Log.warning(e);
+ LOGGER.warn("Socket JSON formatting error", e);
}
}
}
diff --git a/src/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java
index 9318b6fc6..9318b6fc6 100644
--- a/src/org/traccar/api/AsyncSocketServlet.java
+++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java
diff --git a/src/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java
index 634957a49..7de6a3877 100644
--- a/src/org/traccar/api/BaseObjectResource.java
+++ b/src/main/java/org/traccar/api/BaseObjectResource.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2017 - 2018 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.
@@ -31,10 +31,14 @@ 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 {
@@ -76,12 +80,20 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
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());
}
BaseObjectManager<T> manager = Context.getManager(baseClass);
manager.addItem(entity);
+ LogAction.create(getUserId(), entity);
Context.getDataManager().linkObject(User.class, getUserId(), baseClass, entity.getId(), true);
+ LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId());
if (manager instanceof SimpleObjectManager) {
((SimpleObjectManager<T>) manager).refreshUserItems();
@@ -103,10 +115,17 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
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());
}
Context.getPermissionsManager().checkPermission(baseClass, getUserId(), entity.getId());
Context.getManager(baseClass).updateItem(entity);
+ LogAction.edit(getUserId(), entity);
if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) {
Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
@@ -128,6 +147,7 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
BaseObjectManager<T> manager = Context.getManager(baseClass);
manager.removeItem(id);
+ LogAction.remove(getUserId(), baseClass, id);
if (manager instanceof SimpleObjectManager) {
((SimpleObjectManager<T>) manager).refreshUserItems();
@@ -136,12 +156,19 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour
}
}
if (baseClass.equals(Group.class) || baseClass.equals(Device.class) || baseClass.equals(User.class)) {
+ if (baseClass.equals(Group.class)) {
+ Context.getGroupsManager().updateGroupCache(true);
+ 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/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java
index cc272df9c..cc272df9c 100644
--- a/src/org/traccar/api/BaseResource.java
+++ b/src/main/java/org/traccar/api/BaseResource.java
diff --git a/src/org/traccar/api/CorsResponseFilter.java b/src/main/java/org/traccar/api/CorsResponseFilter.java
index 70ea7e3e1..227f80609 100644
--- a/src/org/traccar/api/CorsResponseFilter.java
+++ b/src/main/java/org/traccar/api/CorsResponseFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
package org.traccar.api;
-import org.jboss.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpHeaderNames;
import org.traccar.Context;
import javax.ws.rs.container.ContainerRequestContext;
@@ -31,26 +31,26 @@ public class CorsResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
- if (!response.getHeaders().containsKey(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS)) {
- response.getHeaders().add(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS, HEADERS_ALL);
+ if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString())) {
+ response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString(), HEADERS_ALL);
}
- if (!response.getHeaders().containsKey(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_CREDENTIALS)) {
- response.getHeaders().add(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_CREDENTIALS, true);
+ if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS.toString())) {
+ response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS.toString(), true);
}
- if (!response.getHeaders().containsKey(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_METHODS)) {
- response.getHeaders().add(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_METHODS, METHODS_ALL);
+ if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString())) {
+ response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString(), METHODS_ALL);
}
- if (!response.getHeaders().containsKey(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN)) {
- String origin = request.getHeaderString(HttpHeaders.Names.ORIGIN);
+ if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString())) {
+ String origin = request.getHeaderString(HttpHeaderNames.ORIGIN.toString());
String allowed = Context.getConfig().getString("web.origin");
if (origin == null) {
- response.getHeaders().add(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, ORIGIN_ALL);
+ response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), ORIGIN_ALL);
} else if (allowed == null || allowed.equals(ORIGIN_ALL) || allowed.contains(origin)) {
- response.getHeaders().add(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
+ response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), origin);
}
}
}
diff --git a/src/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java
index 007a7b1bd..007a7b1bd 100644
--- a/src/org/traccar/api/ExtendedObjectResource.java
+++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java
diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java
new file mode 100644
index 000000000..53539770f
--- /dev/null
+++ b/src/main/java/org/traccar/api/MediaFilter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api;
+
+import java.io.IOException;
+import java.sql.SQLException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.traccar.Context;
+import org.traccar.Main;
+import org.traccar.api.resource.SessionResource;
+import org.traccar.database.StatisticsManager;
+import org.traccar.helper.Log;
+import org.traccar.model.Device;
+
+public class MediaFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ try {
+ HttpSession session = ((HttpServletRequest) request).getSession(false);
+ Long userId = null;
+ 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);
+ }
+ }
+ if (userId == null) {
+ httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ }
+
+ String path = ((HttpServletRequest) request).getPathInfo();
+ String[] parts = path.split("/");
+ if (parts.length < 2 || parts.length == 2 && !path.endsWith("/")) {
+ Context.getPermissionsManager().checkAdmin(userId);
+ } else {
+ Device device = Context.getDeviceManager().getByUniqueId(parts[1]);
+ if (device != null) {
+ Context.getPermissionsManager().checkDevice(userId, device.getId());
+ } else {
+ httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ }
+
+ chain.doFilter(request, response);
+ } catch (SecurityException e) {
+ httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ httpResponse.getWriter().println(Log.exceptionStack(e));
+ } catch (SQLException e) {
+ httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ httpResponse.getWriter().println(Log.exceptionStack(e));
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+}
diff --git a/src/org/traccar/api/ObjectMapperProvider.java b/src/main/java/org/traccar/api/ObjectMapperProvider.java
index f81b20917..f81b20917 100644
--- a/src/org/traccar/api/ObjectMapperProvider.java
+++ b/src/main/java/org/traccar/api/ObjectMapperProvider.java
diff --git a/src/org/traccar/api/ResourceErrorHandler.java b/src/main/java/org/traccar/api/ResourceErrorHandler.java
index 1d618b08d..108a8e8cc 100644
--- a/src/org/traccar/api/ResourceErrorHandler.java
+++ b/src/main/java/org/traccar/api/ResourceErrorHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -26,14 +26,8 @@ public class ResourceErrorHandler implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception e) {
if (e instanceof WebApplicationException) {
- WebApplicationException exception = (WebApplicationException) e;
- String message;
- if (exception.getCause() != null) {
- message = Log.exceptionStack(exception.getCause());
- } else {
- message = Log.exceptionStack(exception);
- }
- return Response.fromResponse(exception.getResponse()).entity(message).build();
+ WebApplicationException webException = (WebApplicationException) e;
+ return Response.fromResponse(webException.getResponse()).entity(Log.exceptionStack(webException)).build();
} else {
return Response.status(Response.Status.BAD_REQUEST).entity(Log.exceptionStack(e)).build();
}
diff --git a/src/org/traccar/api/SecurityRequestFilter.java b/src/main/java/org/traccar/api/SecurityRequestFilter.java
index 7024bdbc9..33b6b37df 100644
--- a/src/org/traccar/api/SecurityRequestFilter.java
+++ b/src/main/java/org/traccar/api/SecurityRequestFilter.java
@@ -15,9 +15,13 @@
*/
package org.traccar.api;
+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.helper.Log;
+import org.traccar.database.StatisticsManager;
+import org.traccar.helper.DataConverter;
import org.traccar.model.User;
import javax.annotation.security.PermitAll;
@@ -28,13 +32,14 @@ import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
-import javax.xml.bind.DatatypeConverter;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
public class SecurityRequestFilter implements ContainerRequestFilter {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SecurityRequestFilter.class);
+
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
public static final String BASIC_REALM = "Basic realm=\"api\"";
@@ -43,7 +48,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
public static String[] decodeBasicAuth(String auth) {
auth = auth.replaceFirst("[B|b]asic ", "");
- byte[] decodedBytes = DatatypeConverter.parseBase64Binary(auth);
+ byte[] decodedBytes = DataConverter.parseBase64(auth);
if (decodedBytes != null && decodedBytes.length > 0) {
return new String(decodedBytes, StandardCharsets.US_ASCII).split(":", 2);
}
@@ -74,7 +79,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
String[] auth = decodeBasicAuth(authHeader);
User user = Context.getPermissionsManager().login(auth[0], auth[1]);
if (user != null) {
- Context.getStatisticsManager().registerRequest(user.getId());
+ Main.getInjector().getInstance(StatisticsManager.class).registerRequest(user.getId());
securityContext = new UserSecurityContext(new UserPrincipal(user.getId()));
}
} catch (SQLException e) {
@@ -86,14 +91,14 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY);
if (userId != null) {
Context.getPermissionsManager().checkUserEnabled(userId);
- Context.getStatisticsManager().registerRequest(userId);
+ Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId);
securityContext = new UserSecurityContext(new UserPrincipal(userId));
}
}
} catch (SecurityException e) {
- Log.warning(e);
+ LOGGER.warn("Authentication error", e);
}
if (securityContext != null) {
diff --git a/src/org/traccar/api/SimpleObjectResource.java b/src/main/java/org/traccar/api/SimpleObjectResource.java
index a7fcae0e7..a7fcae0e7 100644
--- a/src/org/traccar/api/SimpleObjectResource.java
+++ b/src/main/java/org/traccar/api/SimpleObjectResource.java
diff --git a/src/org/traccar/api/UserPrincipal.java b/src/main/java/org/traccar/api/UserPrincipal.java
index 80e92c2dd..80e92c2dd 100644
--- a/src/org/traccar/api/UserPrincipal.java
+++ b/src/main/java/org/traccar/api/UserPrincipal.java
diff --git a/src/org/traccar/api/UserSecurityContext.java b/src/main/java/org/traccar/api/UserSecurityContext.java
index 55c0621bc..55c0621bc 100644
--- a/src/org/traccar/api/UserSecurityContext.java
+++ b/src/main/java/org/traccar/api/UserSecurityContext.java
diff --git a/src/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java
index 26a1f6931..de69d871c 100644
--- a/src/org/traccar/api/resource/AttributeResource.java
+++ b/src/main/java/org/traccar/api/resource/AttributeResource.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2017 - 2019 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.
@@ -19,8 +19,11 @@ package org.traccar.api.resource;
import java.sql.SQLException;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
@@ -30,7 +33,7 @@ import org.traccar.Context;
import org.traccar.api.ExtendedObjectResource;
import org.traccar.model.Attribute;
import org.traccar.model.Position;
-import org.traccar.processing.ComputedAttributesHandler;
+import org.traccar.handler.ComputedAttributesHandler;
@Path("attributes/computed")
@Produces(MediaType.APPLICATION_JSON)
@@ -43,18 +46,23 @@ public class AttributeResource extends ExtendedObjectResource<Attribute> {
@POST
@Path("test")
- public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
+ 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().computeAttribute(entity, last);
+ Object result = new ComputedAttributesHandler(
+ Context.getConfig(),
+ Context.getIdentityManager(),
+ Context.getAttributesManager()).computeAttribute(entity, last);
if (result != null) {
switch (entity.getType()) {
case "number":
- return Response.ok((Number) result).build();
+ Number numberValue = (Number) result;
+ return Response.ok(numberValue).build();
case "boolean":
- return Response.ok((Boolean) result).build();
+ Boolean booleanValue = (Boolean) result;
+ return Response.ok(booleanValue).build();
default:
return Response.ok(result.toString()).build();
}
@@ -66,4 +74,24 @@ public class AttributeResource extends ExtendedObjectResource<Attribute> {
}
}
+ @POST
+ public Response add(Attribute entity) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ return super.add(entity);
+ }
+
+ @Path("{id}")
+ @PUT
+ public Response update(Attribute entity) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ return super.update(entity);
+ }
+
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ return super.remove(id);
+ }
+
}
diff --git a/src/org/traccar/api/resource/CalendarResource.java b/src/main/java/org/traccar/api/resource/CalendarResource.java
index 9399c34a5..9399c34a5 100644
--- a/src/org/traccar/api/resource/CalendarResource.java
+++ b/src/main/java/org/traccar/api/resource/CalendarResource.java
diff --git a/src/org/traccar/api/resource/CommandResource.java b/src/main/java/org/traccar/api/resource/CommandResource.java
index 703638701..a31345246 100644
--- a/src/org/traccar/api/resource/CommandResource.java
+++ b/src/main/java/org/traccar/api/resource/CommandResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
@@ -23,7 +23,6 @@ import org.traccar.database.CommandsManager;
import org.traccar.model.Command;
import org.traccar.model.Typed;
-import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -48,7 +47,7 @@ public class CommandResource extends ExtendedObjectResource<Command> {
@GET
@Path("send")
- public Collection<Command> get(@QueryParam("deviceId") long deviceId) throws SQLException {
+ 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()));
@@ -77,11 +76,15 @@ public class CommandResource extends ExtendedObjectResource<Command> {
@GET
@Path("types")
- public Collection<Typed> get(@QueryParam("deviceId") long deviceId,
+ public Collection<Typed> get(
+ @QueryParam("deviceId") long deviceId,
+ @QueryParam("protocol") String protocol,
@QueryParam("textChannel") boolean textChannel) {
if (deviceId != 0) {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
return Context.getCommandsManager().getCommandTypes(deviceId, textChannel);
+ } else if (protocol != null) {
+ return Context.getCommandsManager().getCommandTypes(protocol, textChannel);
} else {
return Context.getCommandsManager().getAllCommandTypes();
}
diff --git a/src/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java
index 1fae92dc7..f9c9a139d 100644
--- a/src/org/traccar/api/resource/DeviceResource.java
+++ b/src/main/java/org/traccar/api/resource/DeviceResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,9 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseObjectResource;
import org.traccar.database.DeviceManager;
+import org.traccar.helper.LogAction;
import org.traccar.model.Device;
-import org.traccar.model.DeviceTotalDistance;
+import org.traccar.model.DeviceAccumulators;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -64,9 +65,13 @@ public class DeviceResource extends BaseObjectResource<Device> {
userId = getUserId();
}
Context.getPermissionsManager().checkUser(getUserId(), userId);
- result = deviceManager.getUserItems(userId);
+ if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
+ result = deviceManager.getAllUserItems(userId);
+ } else {
+ result = deviceManager.getUserItems(userId);
+ }
} else {
- result = new HashSet<Long>();
+ result = new HashSet<>();
for (String uniqueId : uniqueIds) {
Device device = deviceManager.getByUniqueId(uniqueId);
Context.getPermissionsManager().checkDevice(getUserId(), device.getId());
@@ -80,11 +85,15 @@ public class DeviceResource extends BaseObjectResource<Device> {
return deviceManager.getItems(result);
}
- @Path("{id}/distance")
+ @Path("{id}/accumulators")
@PUT
- public Response updateTotalDistance(DeviceTotalDistance entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
- Context.getDeviceManager().resetTotalDistance(entity);
+ public Response updateAccumulators(DeviceAccumulators entity) throws SQLException {
+ if (!Context.getPermissionsManager().getUserAdmin(getUserId())) {
+ Context.getPermissionsManager().checkManager(getUserId());
+ Context.getPermissionsManager().checkPermission(Device.class, getUserId(), entity.getDeviceId());
+ }
+ Context.getDeviceManager().resetDeviceAccumulators(entity);
+ LogAction.resetDeviceAccumulators(getUserId(), entity.getDeviceId());
return Response.noContent().build();
}
diff --git a/src/org/traccar/api/resource/DriverResource.java b/src/main/java/org/traccar/api/resource/DriverResource.java
index 91aa54c5e..91aa54c5e 100644
--- a/src/org/traccar/api/resource/DriverResource.java
+++ b/src/main/java/org/traccar/api/resource/DriverResource.java
diff --git a/src/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java
index a7cf9edbd..e0ccf7020 100644
--- a/src/org/traccar/api/resource/EventResource.java
+++ b/src/main/java/org/traccar/api/resource/EventResource.java
@@ -13,6 +13,7 @@ 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)
@@ -28,6 +29,9 @@ public class EventResource extends BaseResource {
if (event.getGeofenceId() != 0) {
Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId());
}
+ if (event.getMaintenanceId() != 0) {
+ Context.getPermissionsManager().checkPermission(Maintenance.class, getUserId(), event.getMaintenanceId());
+ }
return event;
}
diff --git a/src/org/traccar/api/resource/GeofenceResource.java b/src/main/java/org/traccar/api/resource/GeofenceResource.java
index 58f2c188c..58f2c188c 100644
--- a/src/org/traccar/api/resource/GeofenceResource.java
+++ b/src/main/java/org/traccar/api/resource/GeofenceResource.java
diff --git a/src/org/traccar/api/resource/GroupResource.java b/src/main/java/org/traccar/api/resource/GroupResource.java
index fcea15d0a..fcea15d0a 100644
--- a/src/org/traccar/api/resource/GroupResource.java
+++ b/src/main/java/org/traccar/api/resource/GroupResource.java
diff --git a/src/main/java/org/traccar/api/resource/MaintenanceResource.java b/src/main/java/org/traccar/api/resource/MaintenanceResource.java
new file mode 100644
index 000000000..fa1b359ce
--- /dev/null
+++ b/src/main/java/org/traccar/api/resource/MaintenanceResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.traccar.api.ExtendedObjectResource;
+import org.traccar.model.Maintenance;
+
+@Path("maintenance")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class MaintenanceResource extends ExtendedObjectResource<Maintenance> {
+
+ public MaintenanceResource() {
+ super(Maintenance.class);
+ }
+
+}
diff --git a/src/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java
index 540f02926..9631a52b7 100644
--- a/src/org/traccar/api/resource/NotificationResource.java
+++ b/src/main/java/org/traccar/api/resource/NotificationResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -17,11 +17,11 @@ package org.traccar.api.resource;
import java.util.Collection;
-import javax.mail.MessagingException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -31,13 +31,8 @@ import org.traccar.api.ExtendedObjectResource;
import org.traccar.model.Event;
import org.traccar.model.Notification;
import org.traccar.model.Typed;
-import org.traccar.notification.NotificationMail;
-import org.traccar.notification.NotificationSms;
+import org.traccar.notification.MessageException;
-import com.cloudhopper.smpp.type.RecoverablePduException;
-import com.cloudhopper.smpp.type.SmppChannelException;
-import com.cloudhopper.smpp.type.SmppTimeoutException;
-import com.cloudhopper.smpp.type.UnrecoverablePduException;
@Path("notifications")
@Produces(MediaType.APPLICATION_JSON)
@@ -54,12 +49,27 @@ public class NotificationResource extends ExtendedObjectResource<Notification> {
return Context.getNotificationManager().getAllNotificationTypes();
}
+ @GET
+ @Path("notificators")
+ public Collection<Typed> getNotificators() {
+ return Context.getNotificatorManager().getAllNotificatorTypes();
+ }
+
@POST
@Path("test")
- public Response testMessage() throws MessagingException, RecoverablePduException,
- UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException {
- NotificationMail.sendMailSync(getUserId(), new Event("test", 0), null);
- NotificationSms.sendSmsSync(getUserId(), new Event("test", 0), null);
+ public Response testMessage() throws MessageException, InterruptedException {
+ for (Typed method : Context.getNotificatorManager().getAllNotificatorTypes()) {
+ Context.getNotificatorManager()
+ .getNotificator(method.getType()).sendSync(getUserId(), new Event("test", 0), null);
+ }
+ return Response.noContent().build();
+ }
+
+ @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);
return Response.noContent().build();
}
diff --git a/src/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java
index 9b9f65ad1..b89d9d376 100644
--- a/src/org/traccar/api/resource/PermissionsResource.java
+++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java
@@ -29,6 +29,7 @@ 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;
@@ -61,6 +62,8 @@ public class PermissionsResource extends BaseResource {
checkPermission(permission, true);
Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
permission.getPropertyClass(), permission.getPropertyId(), true);
+ LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId());
Context.getPermissionsManager().refreshPermissions(permission);
return Response.noContent().build();
}
@@ -72,6 +75,8 @@ public class PermissionsResource extends BaseResource {
checkPermission(permission, false);
Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
permission.getPropertyClass(), permission.getPropertyId(), false);
+ LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId());
Context.getPermissionsManager().refreshPermissions(permission);
return Response.noContent().build();
}
diff --git a/src/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java
index c031b842f..c031b842f 100644
--- a/src/org/traccar/api/resource/PositionResource.java
+++ b/src/main/java/org/traccar/api/resource/PositionResource.java
diff --git a/src/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java
index 7c472e84d..d371cf987 100644
--- a/src/org/traccar/api/resource/ReportResource.java
+++ b/src/main/java/org/traccar/api/resource/ReportResource.java
@@ -1,3 +1,19 @@
+/*
+ * 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.api.resource;
import java.io.ByteArrayOutputStream;
@@ -6,6 +22,10 @@ import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
+import javax.activation.DataHandler;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.util.ByteArrayDataSource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -15,6 +35,9 @@ 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.DateUtil;
import org.traccar.model.Event;
@@ -33,9 +56,43 @@ import org.traccar.reports.Stops;
@Consumes(MediaType.APPLICATION_JSON)
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 interface ReportExecutor {
+ void execute(ByteArrayOutputStream stream) throws SQLException, IOException;
+ }
+
+ private Response executeReport(
+ long userId, boolean mail, ReportExecutor executor) throws SQLException, IOException {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ if (mail) {
+ new Thread(() -> {
+ try {
+ 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) {
+ 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();
+ }
+ }
+
@Path("route")
@GET
public Collection<Position> getRoute(
@@ -50,13 +107,12 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getRouteExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- Route.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
-
- return Response.ok(stream.toByteArray())
- .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ throws SQLException, IOException {
+ return executeReport(getUserId(), mail, stream -> {
+ Route.getExcel(stream, getUserId(), deviceIds, groupIds,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+ });
}
@Path("events")
@@ -75,13 +131,12 @@ public class ReportResource extends BaseResource {
public Response getEventsExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("type") final List<String> types,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- Events.getExcel(stream, getUserId(), deviceIds, groupIds, types,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
-
- return Response.ok(stream.toByteArray())
- .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ throws SQLException, IOException {
+ return executeReport(getUserId(), mail, stream -> {
+ Events.getExcel(stream, getUserId(), deviceIds, groupIds, types,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+ });
}
@Path("summary")
@@ -98,13 +153,12 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getSummaryExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- Summary.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
-
- return Response.ok(stream.toByteArray())
- .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ throws SQLException, IOException {
+ return executeReport(getUserId(), mail, stream -> {
+ Summary.getExcel(stream, getUserId(), deviceIds, groupIds,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+ });
}
@Path("trips")
@@ -122,13 +176,12 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getTripsExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- Trips.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
-
- return Response.ok(stream.toByteArray())
- .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ throws SQLException, IOException {
+ return executeReport(getUserId(), mail, stream -> {
+ Trips.getExcel(stream, getUserId(), deviceIds, groupIds,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+ });
}
@Path("stops")
@@ -146,14 +199,12 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getStopsExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- Stops.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
-
- return Response.ok(stream.toByteArray())
- .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
+ @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ throws SQLException, IOException {
+ return executeReport(getUserId(), mail, stream -> {
+ Stops.getExcel(stream, getUserId(), deviceIds, groupIds,
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
+ });
}
-
}
diff --git a/src/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java
index 034a5c492..e7cad2a0c 100644
--- a/src/org/traccar/api/resource/ServerResource.java
+++ b/src/main/java/org/traccar/api/resource/ServerResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -17,6 +17,7 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseResource;
+import org.traccar.helper.LogAction;
import org.traccar.model.Server;
import javax.annotation.security.PermitAll;
@@ -25,6 +26,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.sql.SQLException;
@@ -44,7 +46,18 @@ public class ServerResource extends BaseResource {
public Response update(Server entity) throws SQLException {
Context.getPermissionsManager().checkAdmin(getUserId());
Context.getPermissionsManager().updateServer(entity);
+ LogAction.edit(getUserId(), entity);
return Response.ok(entity).build();
}
+ @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);
+ } else {
+ throw new RuntimeException("Reverse geocoding is not enabled");
+ }
+ }
+
}
diff --git a/src/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java
index fa2a14c6f..e3c5d457f 100644
--- a/src/org/traccar/api/resource/SessionResource.java
+++ b/src/main/java/org/traccar/api/resource/SessionResource.java
@@ -17,6 +17,9 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseResource;
+import org.traccar.helper.DataConverter;
+import org.traccar.helper.ServletHelper;
+import org.traccar.helper.LogAction;
import org.traccar.model.User;
import javax.annotation.security.PermitAll;
@@ -33,7 +36,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
@@ -60,15 +62,14 @@ public class SessionResource extends BaseResource {
Cookie[] cookies = request.getCookies();
String email = null, password = null;
if (cookies != null) {
- for (int i = 0; i < cookies.length; i++) {
- if (cookies[i].getName().equals(USER_COOKIE_KEY)) {
- byte[] emailBytes = DatatypeConverter.parseBase64Binary(
- URLDecoder.decode(cookies[i].getValue(), StandardCharsets.US_ASCII.name()));
+ for (Cookie cookie : cookies) {
+ if (cookie.getName().equals(USER_COOKIE_KEY)) {
+ byte[] emailBytes = DataConverter.parseBase64(
+ URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name()));
email = new String(emailBytes, StandardCharsets.UTF_8);
- }
- if (cookies[i].getName().equals(PASS_COOKIE_KEY)) {
- byte[] passwordBytes = DatatypeConverter.parseBase64Binary(
- URLDecoder.decode(cookies[i].getValue(), StandardCharsets.US_ASCII.name()));
+ } else if (cookie.getName().equals(PASS_COOKIE_KEY)) {
+ byte[] passwordBytes = DataConverter.parseBase64(
+ URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name()));
password = new String(passwordBytes, StandardCharsets.UTF_8);
}
}
@@ -103,14 +104,17 @@ public class SessionResource extends BaseResource {
User user = Context.getPermissionsManager().login(email, password);
if (user != null) {
request.getSession().setAttribute(USER_ID_KEY, user.getId());
+ LogAction.login(user.getId());
return user;
} else {
+ LogAction.failedLogin(ServletHelper.retrieveRemoteAddress(request));
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
@DELETE
public Response remove() {
+ LogAction.logout(getUserId());
request.getSession().removeAttribute(USER_ID_KEY);
return Response.noContent().build();
}
diff --git a/src/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java
index e801d4ff3..e801d4ff3 100644
--- a/src/org/traccar/api/resource/StatisticsResource.java
+++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java
diff --git a/src/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java
index 0f6f6edba..0b42d8d92 100644
--- a/src/org/traccar/api/resource/UserResource.java
+++ b/src/main/java/org/traccar/api/resource/UserResource.java
@@ -18,6 +18,7 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseObjectResource;
import org.traccar.database.UsersManager;
+import org.traccar.helper.LogAction;
import org.traccar.model.ManagedUser;
import org.traccar.model.User;
@@ -81,8 +82,10 @@ public class UserResource extends BaseObjectResource<User> {
}
}
Context.getUsersManager().addItem(entity);
+ LogAction.create(getUserId(), entity);
if (Context.getPermissionsManager().getUserManager(getUserId())) {
Context.getDataManager().linkObject(User.class, getUserId(), ManagedUser.class, entity.getId(), true);
+ 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/config/Config.java b/src/main/java/org/traccar/config/Config.java
new file mode 100644
index 000000000..d8f2a0e99
--- /dev/null
+++ b/src/main/java/org/traccar/config/Config.java
@@ -0,0 +1,166 @@
+/*
+ * 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.config;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.InvalidPropertiesFormatException;
+import java.util.Properties;
+
+public class Config {
+
+ private final Properties properties = new Properties();
+
+ private boolean useEnvironmentVariables;
+
+ public Config() {
+ }
+
+ public Config(String file) throws IOException {
+ try {
+ Properties mainProperties = new Properties();
+ try (InputStream inputStream = new FileInputStream(file)) {
+ mainProperties.loadFromXML(inputStream);
+ }
+
+ String defaultConfigFile = mainProperties.getProperty("config.default");
+ if (defaultConfigFile != null) {
+ try (InputStream inputStream = new FileInputStream(defaultConfigFile)) {
+ properties.loadFromXML(inputStream);
+ }
+ }
+
+ properties.putAll(mainProperties); // override defaults
+
+ useEnvironmentVariables = Boolean.parseBoolean(System.getenv("CONFIG_USE_ENVIRONMENT_VARIABLES"))
+ || Boolean.parseBoolean(properties.getProperty("config.useEnvironmentVariables"));
+ } catch (InvalidPropertiesFormatException e) {
+ throw new RuntimeException("Configuration file is not a valid XML document", e);
+ }
+ }
+
+ public boolean hasKey(ConfigKey key) {
+ return hasKey(key.getKey());
+ }
+
+ @Deprecated
+ public boolean hasKey(String key) {
+ return useEnvironmentVariables && System.getenv().containsKey(getEnvironmentVariableName(key))
+ || properties.containsKey(key);
+ }
+
+ public String getString(ConfigKey key) {
+ return getString(key.getKey());
+ }
+
+ @Deprecated
+ public String getString(String key) {
+ if (useEnvironmentVariables) {
+ String value = System.getenv(getEnvironmentVariableName(key));
+ if (value != null && !value.isEmpty()) {
+ return value;
+ }
+ }
+ return properties.getProperty(key);
+ }
+
+ public String getString(ConfigKey key, String defaultValue) {
+ return getString(key.getKey(), defaultValue);
+ }
+
+ @Deprecated
+ public String getString(String key, String defaultValue) {
+ return hasKey(key) ? getString(key) : defaultValue;
+ }
+
+ public boolean getBoolean(ConfigKey key) {
+ return getBoolean(key.getKey());
+ }
+
+ @Deprecated
+ public boolean getBoolean(String key) {
+ return Boolean.parseBoolean(getString(key));
+ }
+
+ public int getInteger(ConfigKey key) {
+ return getInteger(key.getKey());
+ }
+
+ @Deprecated
+ public int getInteger(String key) {
+ return getInteger(key, 0);
+ }
+
+ public int getInteger(ConfigKey key, int defaultValue) {
+ return getInteger(key.getKey(), defaultValue);
+ }
+
+ @Deprecated
+ public int getInteger(String key, int defaultValue) {
+ return hasKey(key) ? Integer.parseInt(getString(key)) : defaultValue;
+ }
+
+ public long getLong(ConfigKey key) {
+ return getLong(key.getKey());
+ }
+
+ @Deprecated
+ public long getLong(String key) {
+ return getLong(key, 0);
+ }
+
+ public long getLong(ConfigKey key, long defaultValue) {
+ return getLong(key.getKey(), defaultValue);
+ }
+
+ @Deprecated
+ public long getLong(String key, long defaultValue) {
+ return hasKey(key) ? Long.parseLong(getString(key)) : defaultValue;
+ }
+
+ public double getDouble(ConfigKey key) {
+ return getDouble(key.getKey());
+ }
+
+ @Deprecated
+ public double getDouble(String key) {
+ return getDouble(key, 0.0);
+ }
+
+ public double getDouble(ConfigKey key, double defaultValue) {
+ return getDouble(key.getKey(), defaultValue);
+ }
+
+ @Deprecated
+ public double getDouble(String key, double defaultValue) {
+ return hasKey(key) ? Double.parseDouble(getString(key)) : defaultValue;
+ }
+
+ public void setString(ConfigKey key, String value) {
+ setString(key.getKey(), value);
+ }
+
+ @Deprecated
+ public void setString(String key, String value) {
+ properties.put(key, value);
+ }
+
+ static String getEnvironmentVariableName(String key) {
+ return key.replaceAll("\\.", "_").replaceAll("(\\p{Lu})", "_$1").toUpperCase();
+ }
+
+}
diff --git a/src/main/java/org/traccar/config/ConfigKey.java b/src/main/java/org/traccar/config/ConfigKey.java
new file mode 100644
index 000000000..2e54ad392
--- /dev/null
+++ b/src/main/java/org/traccar/config/ConfigKey.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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.config;
+
+public class ConfigKey {
+
+ private final String key;
+ private final Class clazz;
+
+ ConfigKey(String key, Class clazz) {
+ this.key = key;
+ this.clazz = clazz;
+ }
+
+ String getKey() {
+ return key;
+ }
+
+ Class getValueClass() {
+ return clazz;
+ }
+
+}
diff --git a/src/main/java/org/traccar/config/ConfigSuffix.java b/src/main/java/org/traccar/config/ConfigSuffix.java
new file mode 100644
index 000000000..149b2cd00
--- /dev/null
+++ b/src/main/java/org/traccar/config/ConfigSuffix.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.config;
+
+public class ConfigSuffix extends ConfigKey {
+
+ ConfigSuffix(String key, Class clazz) {
+ super(key, clazz);
+ }
+
+ public ConfigKey withPrefix(String prefix) {
+ return new ConfigKey(prefix + getKey(), getValueClass());
+ }
+
+}
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
new file mode 100644
index 000000000..2c5dcefd5
--- /dev/null
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 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.config;
+
+public final class Keys {
+
+ /**
+ * Connection timeout value in seconds. Because sometimes there is no way to detect lost TCP connection old
+ * connections stay in open state. On most systems there is a limit on number of open connection, so this leads to
+ * problems with establishing new connections when number of devices is high or devices data connections are
+ * unstable.
+ */
+ public static final ConfigSuffix PROTOCOL_TIMEOUT = new ConfigSuffix(
+ ".timeout", Integer.class);
+
+ /**
+ * Server wide connection timeout value in seconds. See protocol timeout for more information.
+ */
+ public static final ConfigKey SERVER_TIMEOUT = new ConfigKey(
+ "server.timeout", Integer.class);
+
+ /**
+ * 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 SERVER_STATISTICS = new ConfigKey(
+ "server.statistics", Boolean.class);
+
+ /**
+ * Enable events subsystem. Flag to enable all events handlers.
+ */
+ public static final ConfigKey EVENT_ENABLE = new ConfigKey(
+ "event.enable", Boolean.class);
+
+ /**
+ * If true, the event is generated once at the beginning of overspeeding period.
+ */
+ public static final ConfigKey EVENT_OVERSPEED_NOT_REPEAT = new ConfigKey(
+ "event.overspeed.notRepeat", Boolean.class);
+
+ /**
+ * Minimal over speed duration to trigger the event. Value in seconds.
+ */
+ public static final ConfigKey EVENT_OVERSPEED_MINIMAL_DURATION = new ConfigKey(
+ "event.overspeed.minimalDuration", Long.class);
+
+ /**
+ * Relevant only for geofence speed limits. Use lowest speed limits from all geofences.
+ */
+ public static final ConfigKey EVENT_OVERSPEED_PREFER_LOWEST = new ConfigKey(
+ "event.overspeed.preferLowest", Boolean.class);
+
+ /**
+ * Do not generate alert event if same alert was present in last known location.
+ */
+ public static final ConfigKey EVENT_IGNORE_DUPLICATE_ALERTS = new ConfigKey(
+ "event.ignoreDuplicateAlerts", Boolean.class);
+
+ /**
+ * List of external handler classes to use in Netty pipeline.
+ */
+ public static final ConfigKey EXTRA_HANDLERS = new ConfigKey(
+ "extra.handlers", String.class);
+
+ /**
+ * Enable positions forwarding to other web server.
+ */
+ public static final ConfigKey FORWARD_ENABLE = new ConfigKey(
+ "forward.enable", Boolean.class);
+
+ /**
+ * 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 FORWARD_URL = new ConfigKey(
+ "forward.url", String.class);
+
+ /**
+ * Additional HTTP header, can be used for authorization.
+ */
+ public static final ConfigKey FORWARD_HEADER = new ConfigKey(
+ "forward.header", String.class);
+
+ /**
+ * Boolean value to enable forwarding in JSON format.
+ */
+ public static final ConfigKey FORWARD_JSON = new ConfigKey(
+ "forward.json", Boolean.class);
+
+ /**
+ * Boolean flag to enable or disable position filtering.
+ */
+ public static final ConfigKey FILTER_ENABLE = new ConfigKey(
+ "filter.enable", Boolean.class);
+
+ /**
+ * Filter invalid (valid field is set to false) positions.
+ */
+ public static final ConfigKey FILTER_INVALID = new ConfigKey(
+ "filter.invalid", Boolean.class);
+
+ /**
+ * Filter zero coordinates. Zero latitude and longitude are theoretically valid values, but it practice it usually
+ * indicates invalid GPS data.
+ */
+ public static final ConfigKey FILTER_ZERO = new ConfigKey(
+ "filter.zero", Boolean.class);
+
+ /**
+ * Filter duplicate records (duplicates are detected by time value).
+ */
+ public static final ConfigKey FILTER_DUPLICATE = new ConfigKey(
+ "filter.duplicate", Boolean.class);
+
+ /**
+ * 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.
+ */
+ public static final ConfigKey FILTER_FUTURE = new ConfigKey(
+ "filter.future", Long.class);
+
+ /**
+ * Filter positions with accuracy less than specified value in meters.
+ */
+ public static final ConfigKey FILTER_ACCURACY = new ConfigKey(
+ "filter.accuracy", Integer.class);
+
+ /**
+ * Filter cell and wifi locations that are coming from geolocation provider.
+ */
+ public static final ConfigKey FILTER_APPROXIMATE = new ConfigKey(
+ "filter.approximate", Boolean.class);
+
+ /**
+ * Filter positions with exactly zero speed values.
+ */
+ public static final ConfigKey FILTER_STATIC = new ConfigKey(
+ "filter.static", Boolean.class);
+
+ /**
+ * 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 FILTER_DISTANCE = new ConfigKey(
+ "filter.distance", Integer.class);
+
+ /**
+ * 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.
+ */
+ public static final ConfigKey FILTER_MAX_SPEED = new ConfigKey(
+ "filter.maxSpeed", Integer.class);
+
+ /**
+ * Filter position if time from previous position is less than specified value in seconds.
+ */
+ public static final ConfigKey FILTER_MIN_PERIOD = new ConfigKey(
+ "filter.minPeriod", Integer.class);
+
+ /**
+ * 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.
+ */
+ public static final ConfigKey FILTER_SKIP_LIMIT = new ConfigKey(
+ "filter.skipLimit", Long.class);
+
+ /**
+ * Enable attributes skipping. Attribute skipping can be enabled in the config or device attributes.
+ */
+ public static final ConfigKey FILTER_SKIP_ATTRIBUTES_ENABLE = new ConfigKey(
+ "filter.skipAttributes.enable", Boolean.class);
+
+ /**
+ * Override device time. Possible values are 'deviceTime' and 'serverTime'
+ */
+ public static final ConfigKey TIME_OVERRIDE = new ConfigKey(
+ "time.override", String.class);
+
+ /**
+ * 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 TIME_PROTOCOLS = new ConfigKey(
+ "time.protocols", String.class);
+
+ /**
+ * 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 COORDINATES_FILTER = new ConfigKey(
+ "coordinates.filter", Boolean.class);
+
+ /**
+ * Distance in meters. Distances below this value gets handled like explained in 'coordinates.filter'.
+ */
+ public static final ConfigKey COORDINATES_MIN_ERROR = new ConfigKey(
+ "coordinates.minError", Integer.class);
+
+ /**
+ * 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 COORDINATES_MAX_ERROR = new ConfigKey(
+ "coordinates.maxError", Integer.class);
+
+ /**
+ * Enable to save device IP addresses information. Disabled by default.
+ */
+ public static final ConfigKey PROCESSING_REMOTE_ADDRESS_ENABLE = new ConfigKey(
+ "processing.remoteAddress.enable", Boolean.class);
+
+ /**
+ * Enable engine hours calculation on the server side. It uses ignition value to determine engine state.
+ */
+ public static final ConfigKey PROCESSING_ENGINE_HOURS_ENABLE = new ConfigKey(
+ "processing.engineHours.enable", Boolean.class);
+
+ /**
+ * 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 PROCESSING_COPY_ATTRIBUTES_ENABLE = new ConfigKey(
+ "processing.copyAttributes.enable", Boolean.class);
+
+ /**
+ * Enable computed attributes processing.
+ */
+ public static final ConfigKey PROCESSING_COMPUTED_ATTRIBUTES_ENABLE = new ConfigKey(
+ "processing.computedAttributes.enable", Boolean.class);
+
+ /**
+ * Enable computed attributes processing.
+ */
+ public static final ConfigKey PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new ConfigKey(
+ "processing.computedAttributes.deviceAttributes", Boolean.class);
+
+ /**
+ * Boolean flag to enable or disable reverse geocoder.
+ */
+ public static final ConfigKey GEOCODER_ENABLE = new ConfigKey(
+ "geocoder.enable", Boolean.class);
+
+ /**
+ * 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 GEOCODER_TYPE = new ConfigKey(
+ "geocoder.type", String.class);
+
+ /**
+ * Geocoder server URL. Applicable only to Nominatim and Gisgraphy providers.
+ */
+ public static final ConfigKey GEOCODER_URL = new ConfigKey(
+ "geocoder.url", String.class);
+
+ /**
+ * App id for use with Here provider.
+ */
+ public static final ConfigKey GEOCODER_ID = new ConfigKey(
+ "geocoder.id", String.class);
+
+ /**
+ * Provider API key. Most providers require API keys.
+ */
+ public static final ConfigKey GEOCODER_KEY = new ConfigKey(
+ "geocoder.key", String.class);
+
+ /**
+ * Language parameter for providers that support localization (e.g. Google and Nominatim).
+ */
+ public static final ConfigKey GEOCODER_LANGUAGE = new ConfigKey(
+ "geocoder.language", String.class);
+
+ /**
+ * Address format string. Default value is %h %r, %t, %s, %c. See AddressFormat for more info.
+ */
+ public static final ConfigKey GEOCODER_FORMAT = new ConfigKey(
+ "geocoder.format", String.class);
+
+ /**
+ * Cache size for geocoding results.
+ */
+ public static final ConfigKey GEOCODER_CACHE_SIZE = new ConfigKey(
+ "geocoder.cacheSize", Integer.class);
+
+ /**
+ * Disable automatic reverse geocoding requests for all positions.
+ */
+ public static final ConfigKey GEOCODER_IGNORE_POSITIONS = new ConfigKey(
+ "geocoder.ignorePositions", Boolean.class);
+
+ /**
+ * Boolean flag to apply reverse geocoding to invalid positions.
+ */
+ public static final ConfigKey GEOCODER_PROCESS_INVALID_POSITIONS = new ConfigKey(
+ "geocoder.processInvalidPositions", Boolean.class);
+
+ /**
+ * 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 GEOCODER_REUSE_DISTANCE = new ConfigKey(
+ "geocoder.reuseDistance", Integer.class);
+
+ /**
+ * 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 GEOLOCATION_ENABLE = new ConfigKey(
+ "geolocation.enable", Boolean.class);
+
+ /**
+ * 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 GEOLOCATION_TYPE = new ConfigKey(
+ "geolocation.type", String.class);
+
+ /**
+ * Geolocation provider API URL address. Not required for most providers.
+ */
+ public static final ConfigKey GEOLOCATION_URL = new ConfigKey(
+ "geolocation.url", String.class);
+
+ /**
+ * Provider API key. OpenCellID service requires API key.
+ */
+ public static final ConfigKey GEOLOCATION_KEY = new ConfigKey(
+ "geolocation.key", String.class);
+
+ /**
+ * Boolean flag to apply geolocation to invalid positions.
+ */
+ public static final ConfigKey GEOLOCATION_PROCESS_INVALID_POSITIONS = new ConfigKey(
+ "geolocation.processInvalidPositions", Boolean.class);
+
+ /**
+ * 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 LOCATION_LATITUDE_HEMISPHERE = new ConfigKey(
+ "location.latitudeHemisphere", Boolean.class);
+
+ /**
+ * 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 LOCATION_LONGITUDE_HEMISPHERE = new ConfigKey(
+ "location.longitudeHemisphere", Boolean.class);
+
+ private Keys() {
+ }
+
+}
diff --git a/src/org/traccar/database/ActiveDevice.java b/src/main/java/org/traccar/database/ActiveDevice.java
index f491111e1..207fc454b 100644
--- a/src/org/traccar/database/ActiveDevice.java
+++ b/src/main/java/org/traccar/database/ActiveDevice.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,8 @@
*/
package org.traccar.database;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.model.Command;
@@ -48,7 +49,7 @@ public class ActiveDevice {
}
public void write(Object message) {
- getChannel().write(message, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(message, remoteAddress));
}
}
diff --git a/src/org/traccar/database/AttributesManager.java b/src/main/java/org/traccar/database/AttributesManager.java
index 28816645a..28816645a 100644
--- a/src/org/traccar/database/AttributesManager.java
+++ b/src/main/java/org/traccar/database/AttributesManager.java
diff --git a/src/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
index cc1dbde5f..8bf9ef860 100644
--- a/src/org/traccar/database/BaseObjectManager.java
+++ b/src/main/java/org/traccar/database/BaseObjectManager.java
@@ -24,11 +24,14 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.traccar.helper.Log;
+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 DataManager dataManager;
private Map<Long, T> items;
@@ -74,7 +77,7 @@ public class BaseObjectManager<T extends BaseModel> {
}
}
} catch (SQLException error) {
- Log.warning(error);
+ LOGGER.warn("Error refreshing items", error);
}
}
}
diff --git a/src/org/traccar/database/CalendarManager.java b/src/main/java/org/traccar/database/CalendarManager.java
index 44ced1082..44ced1082 100644
--- a/src/org/traccar/database/CalendarManager.java
+++ b/src/main/java/org/traccar/database/CalendarManager.java
diff --git a/src/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index 9ceb995ef..dc9512d9e 100644
--- a/src/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -20,25 +20,32 @@ 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.helper.Log;
import org.traccar.model.Command;
import org.traccar.model.Typed;
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<>();
- public CommandsManager(DataManager dataManager) {
+ private boolean queueing;
+
+ public CommandsManager(DataManager dataManager, boolean queueing) {
super(dataManager, Command.class);
+ this.queueing = queueing;
}
public boolean checkDeviceCommand(long deviceId, long commandId) {
@@ -58,10 +65,10 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
protocol.sendTextCommand(phone, command);
} else if (command.getType().equals(Command.TYPE_CUSTOM)) {
- if (Context.getSmppManager() != null) {
- Context.getSmppManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true);
+ if (Context.getSmsManager() != null) {
+ Context.getSmsManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true);
} else {
- throw new RuntimeException("SMPP client is not enabled");
+ throw new RuntimeException("SMS is not enabled");
}
} else {
throw new RuntimeException("Command " + command.getType() + " is not supported");
@@ -70,6 +77,8 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId);
if (activeDevice != null) {
activeDevice.sendCommand(command);
+ } else if (!queueing) {
+ throw new RuntimeException("Device is not online");
} else {
getDeviceQueue(deviceId).add(command);
return false;
@@ -98,17 +107,21 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
}
public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) {
- List<Typed> result = new ArrayList<>();
Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- Collection<String> commands;
- commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands();
- for (String commandKey : commands) {
- result.add(new Typed(commandKey));
- }
+ return getCommandTypes(lastPosition.getProtocol(), textChannel);
} else {
- result.add(new Typed(Command.TYPE_CUSTOM));
+ 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;
}
@@ -121,7 +134,7 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
try {
result.add(new Typed(field.get(null).toString()));
} catch (IllegalArgumentException | IllegalAccessException error) {
- Log.warning(error);
+ LOGGER.warn("Get command types error", error);
}
}
}
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java
index e5a7a272f..dd0071143 100644
--- a/src/org/traccar/database/ConnectionManager.java
+++ b/src/main/java/org/traccar/database/ConnectionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,14 +15,17 @@
*/
package org.traccar.database;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.util.Timeout;
-import org.jboss.netty.util.TimerTask;
+import io.netty.channel.Channel;
+import io.netty.util.Timeout;
+import io.netty.util.TimerTask;
+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.events.OverspeedEventHandler;
-import org.traccar.helper.Log;
+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;
@@ -40,6 +43,8 @@ import java.util.concurrent.TimeUnit;
public class ConnectionManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class);
+
private static final long DEFAULT_TIMEOUT = 600;
private final long deviceTimeout;
@@ -57,9 +62,7 @@ public class ConnectionManager {
}
public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
- ActiveDevice activeDevice = new ActiveDevice(deviceId, protocol, channel, remoteAddress);
- activeDevices.put(deviceId, activeDevice);
- Context.getCommandsManager().sendQueuedCommands(activeDevice);
+ activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress));
}
public void removeActiveDevice(Channel channel) {
@@ -121,10 +124,9 @@ public class ConnectionManager {
if (status.equals(Device.STATUS_ONLINE)) {
timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(new TimerTask() {
@Override
- public void run(Timeout timeout) throws Exception {
+ public void run(Timeout timeout) {
if (!timeout.isCancelled()) {
updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
- activeDevices.remove(deviceId);
}
}
}, deviceTimeout, TimeUnit.MILLISECONDS));
@@ -133,23 +135,29 @@ public class ConnectionManager {
try {
Context.getDeviceManager().updateDeviceStatus(device);
} catch (SQLException error) {
- Log.warning(error);
+ LOGGER.warn("Update device status error", error);
}
updateDevice(device);
+
+ if (status.equals(Device.STATUS_ONLINE) && !oldStatus.equals(Device.STATUS_ONLINE)) {
+ Context.getCommandsManager().sendQueuedCommands(getActiveDevice(deviceId));
+ }
}
public Map<Event, Position> updateDeviceState(long deviceId) {
DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
Map<Event, Position> result = new HashMap<>();
- Map<Event, Position> event = Context.getMotionEventHandler().updateMotionState(deviceState);
+ Map<Event, Position> event = Main.getInjector()
+ .getInstance(MotionEventHandler.class).updateMotionState(deviceState);
if (event != null) {
result.putAll(event);
}
- event = Context.getOverspeedEventHandler().updateOverspeedState(deviceState, Context.getDeviceManager().
- lookupAttributeDouble(deviceId, OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT, 0, false));
+ 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);
}
diff --git a/src/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java
index e88ff7f0d..8e9071736 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/main/java/org/traccar/database/DataManager.java
@@ -19,9 +19,7 @@ import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
-import java.net.URLClassLoader;
import java.sql.SQLException;
-import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
@@ -39,14 +37,18 @@ import liquibase.exception.LiquibaseException;
import liquibase.resource.FileSystemResourceAccessor;
import liquibase.resource.ResourceAccessor;
-import org.traccar.Config;
-import org.traccar.helper.Log;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.Context;
+import org.traccar.helper.DateUtil;
import org.traccar.model.Attribute;
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.Permission;
@@ -63,6 +65,8 @@ import com.zaxxer.hikari.HikariDataSource;
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";
@@ -75,17 +79,17 @@ public class DataManager {
private boolean generateQueries;
+ private boolean forceLdap;
+
public DataManager(Config config) throws Exception {
this.config = config;
+ forceLdap = config.getBoolean("ldap.force");
+
initDatabase();
initDatabaseSchema();
}
- public DataSource getDataSource() {
- return dataSource;
- }
-
private void initDatabase() throws Exception {
String jndiName = config.getString("database.jndi");
@@ -98,10 +102,17 @@ public class DataManager {
String driverFile = config.getString("database.driverFile");
if (driverFile != null) {
- URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
- Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
- method.setAccessible(true);
- method.invoke(classLoader, new File(driverFile).toURI().toURL());
+ 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("database.driver");
@@ -184,26 +195,26 @@ public class DataManager {
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");
+ 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) {
- Log.info("Query not provided: " + key);
+ LOGGER.info("Query not provided: " + key);
}
return query;
}
@@ -228,7 +239,7 @@ public class DataManager {
query = constructObjectQuery(action, clazz, extended);
config.setString(queryName, query);
} else {
- Log.info("Query not provided: " + queryName);
+ LOGGER.info("Query not provided: " + queryName);
}
}
@@ -237,12 +248,16 @@ public class DataManager {
public String getQuery(String action, Class<?> owner, Class<?> property) {
String queryName;
- if (action.equals(ACTION_SELECT_ALL)) {
- queryName = "database.select" + owner.getSimpleName() + property.getSimpleName() + "s";
- } else if (action.equals(ACTION_INSERT)) {
- queryName = "database.link" + owner.getSimpleName() + property.getSimpleName();
- } else {
- queryName = "database.unlink" + owner.getSimpleName() + property.getSimpleName();
+ 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) {
@@ -251,7 +266,7 @@ public class DataManager {
property.equals(User.class) ? ManagedUser.class : property);
config.setString(queryName, query);
} else {
- Log.info("Query not provided: " + queryName);
+ LOGGER.info("Query not provided: " + queryName);
}
}
@@ -263,11 +278,12 @@ public class DataManager {
if (propertyName.equals("ManagedUser")) {
propertyName = "User";
}
- return Introspector.decapitalize(owner.getSimpleName()) + "_" + Introspector.decapitalize(propertyName);
+ return "tc_" + Introspector.decapitalize(owner.getSimpleName())
+ + "_" + Introspector.decapitalize(propertyName);
}
private static String getObjectsTableName(Class<?> clazz) {
- String result = Introspector.decapitalize(clazz.getSimpleName());
+ String result = "tc_" + Introspector.decapitalize(clazz.getSimpleName());
// Add "s" ending if object name is not plural already
if (!result.endsWith("s")) {
result += "s";
@@ -285,7 +301,8 @@ public class DataManager {
config.getString("database.url"),
config.getString("database.user"),
config.getString("database.password"),
- null, resourceAccessor);
+ config.getString("database.driver"),
+ null, null, null, resourceAccessor);
Liquibase liquibase = new Liquibase(
config.getString("database.changelog"), resourceAccessor, database);
@@ -300,11 +317,20 @@ public class DataManager {
User user = QueryBuilder.create(dataSource, getQuery("database.loginUser"))
.setString("email", email.trim())
.executeQuerySingle(User.class);
- if (user != null && user.isPasswordValid(password)) {
- return user;
+ 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 {
- return null;
+ 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 {
@@ -321,13 +347,6 @@ public class DataManager {
.executeQuery(Position.class);
}
- public void addPosition(Position position) throws SQLException {
- position.setId(QueryBuilder.create(dataSource, getQuery(ACTION_INSERT, Position.class), true)
- .setObject(position)
- .setDate("serverTime", new Date())
- .executeUpdate());
- }
-
public void updateLatestPosition(Position position) throws SQLException {
QueryBuilder.create(dataSource, getQuery("database.updateLatestPosition"))
.setDate("now", new Date())
@@ -344,7 +363,7 @@ public class DataManager {
long historyDays = config.getInteger("database.historyDays");
if (historyDays != 0) {
Date timeLimit = new Date(System.currentTimeMillis() - historyDays * 24 * 3600 * 1000);
- Log.debug("Clearing history earlier than " + new SimpleDateFormat(Log.DATE_FORMAT).format(timeLimit));
+ LOGGER.info("Clearing history earlier than " + DateUtil.formatDate(timeLimit, false));
QueryBuilder.create(dataSource, getQuery("database.deletePositions"))
.setDate("serverTime", timeLimit)
.executeUpdate();
@@ -394,6 +413,8 @@ public class DataManager {
return Calendar.class;
case "command":
return Command.class;
+ case "maintenance":
+ return Maintenance.class;
case "notification":
return Notification.class;
default:
diff --git a/src/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
index 1eb90b7eb..fa95adeb2 100644
--- a/src/org/traccar/database/DeviceManager.java
+++ b/src/main/java/org/traccar/database/DeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -25,18 +25,22 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
-import org.traccar.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
import org.traccar.Context;
-import org.traccar.helper.Log;
+import org.traccar.model.Command;
import org.traccar.model.Device;
import org.traccar.model.DeviceState;
-import org.traccar.model.DeviceTotalDistance;
+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);
+
public static final long DEFAULT_REFRESH_DELAY = 300;
private final Config config;
@@ -65,7 +69,36 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
refreshLastPositions();
}
- private void updateDeviceCache(boolean force) throws SQLException {
+ @Override
+ public long addUnknownDevice(String uniqueId) {
+ Device device = new Device();
+ device.setName(uniqueId);
+ device.setUniqueId(uniqueId);
+ device.setCategory(Context.getConfig().getString("database.registerUnknown.defaultCategory"));
+
+ long defaultGroupId = Context.getConfig().getLong("database.registerUnknown.defaultGroupId");
+ 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())) {
@@ -82,6 +115,24 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
return devicesByUniqueId.get(uniqueId);
}
+ @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(protocol + "." + Command.KEY_DEVICE_PASSWORD);
+ if (password != null) {
+ return password;
+ }
+ }
+
+ return defaultPassword;
+ }
+
public Device getDeviceByPhone(String phone) {
return devicesByPhone.get(phone);
}
@@ -93,7 +144,7 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
try {
updateDeviceCache(true);
} catch (SQLException e) {
- Log.warning(e);
+ LOGGER.warn("Update device cache error", e);
}
result = super.getAllItems();
}
@@ -104,15 +155,35 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
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) {
- return Context.getPermissionsManager().getDevicePermissions(userId);
+ 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<>();
+ result.addAll(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<>();
@@ -160,6 +231,7 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
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())) {
devicesByUniqueId.remove(cachedDevice.getUniqueId());
@@ -168,7 +240,10 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
if (device.getPhone() != null && !device.getPhone().isEmpty()
&& !device.getPhone().equals(cachedDevice.getPhone())) {
- devicesByPhone.remove(cachedDevice.getPhone());
+ String phone = cachedDevice.getPhone();
+ if (phone != null && !phone.isEmpty()) {
+ devicesByPhone.remove(phone);
+ }
cachedDevice.setPhone(device.getPhone());
putPhone(cachedDevice);
}
@@ -204,7 +279,7 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
positions.put(position.getDeviceId(), position);
}
} catch (SQLException error) {
- Log.warning(error);
+ LOGGER.warn("Load latest positions error", error);
}
}
}
@@ -243,7 +318,8 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
List<Position> result = new LinkedList<>();
if (Context.getPermissionsManager() != null) {
- for (long deviceId : getUserItems(userId)) {
+ for (long deviceId : Context.getPermissionsManager().getUserAdmin(userId)
+ ? getAllUserItems(userId) : getUserItems(userId)) {
if (positions.containsKey(deviceId)) {
result.add(positions.get(deviceId));
}
@@ -253,32 +329,37 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
return result;
}
+ @Override
public boolean lookupAttributeBoolean(
- long deviceId, String attributeName, boolean defaultValue, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ 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 lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) {
+ Object result = lookupAttribute(deviceId, attributeName, lookupServer, lookupConfig);
return result != null ? (String) result : defaultValue;
}
- public int lookupAttributeInteger(long deviceId, String attributeName, int defaultValue, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ @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 lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ 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();
}
@@ -286,15 +367,15 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public double lookupAttributeDouble(
- long deviceId, String attributeName, double defaultValue, boolean lookupConfig) {
- Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ 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 lookupConfig) {
+ private Object lookupAttribute(long deviceId, String attributeName, boolean lookupServer, boolean lookupConfig) {
Object result = null;
Device device = getById(deviceId);
if (device != null) {
@@ -314,23 +395,27 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
}
}
- if (result == null) {
- if (lookupConfig) {
- result = Context.getConfig().getString(attributeName);
- } else {
- Server server = Context.getPermissionsManager().getServer();
- result = server.getAttributes().get(attributeName);
- }
+ 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 resetTotalDistance(DeviceTotalDistance deviceTotalDistance) throws SQLException {
- Position last = positions.get(deviceTotalDistance.getDeviceId());
+ public void resetDeviceAccumulators(DeviceAccumulators deviceAccumulators) throws SQLException {
+ Position last = positions.get(deviceAccumulators.getDeviceId());
if (last != null) {
- last.getAttributes().put(Position.KEY_TOTAL_DISTANCE, deviceTotalDistance.getTotalDistance());
- getDataManager().addPosition(last);
+ 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();
diff --git a/src/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java
index 930951460..930951460 100644
--- a/src/org/traccar/database/DriversManager.java
+++ b/src/main/java/org/traccar/database/DriversManager.java
diff --git a/src/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java
index 16785cb37..ceb85b537 100644
--- a/src/org/traccar/database/ExtendedObjectManager.java
+++ b/src/main/java/org/traccar/database/ExtendedObjectManager.java
@@ -23,8 +23,9 @@ 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.helper.Log;
import org.traccar.model.Device;
import org.traccar.model.Group;
import org.traccar.model.Permission;
@@ -32,6 +33,8 @@ 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<>();
@@ -95,7 +98,7 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
long groupId = device.getGroupId();
while (groupId != 0) {
getAllDeviceItems(device.getId()).addAll(getGroupItems(groupId));
- Group group = (Group) Context.getGroupsManager().getById(groupId);
+ Group group = Context.getGroupsManager().getById(groupId);
if (group != null) {
groupId = group.getGroupId();
} else {
@@ -105,7 +108,7 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
}
} catch (SQLException | ClassNotFoundException error) {
- Log.warning(error);
+ LOGGER.warn("Refresh permissions error", error);
}
}
}
diff --git a/src/org/traccar/database/GeofenceManager.java b/src/main/java/org/traccar/database/GeofenceManager.java
index a32847cf9..a32847cf9 100644
--- a/src/org/traccar/database/GeofenceManager.java
+++ b/src/main/java/org/traccar/database/GeofenceManager.java
diff --git a/src/org/traccar/database/GroupTree.java b/src/main/java/org/traccar/database/GroupTree.java
index 8798f55bc..8798f55bc 100644
--- a/src/org/traccar/database/GroupTree.java
+++ b/src/main/java/org/traccar/database/GroupTree.java
diff --git a/src/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java
index c0456085b..d8404c614 100644
--- a/src/org/traccar/database/GroupsManager.java
+++ b/src/main/java/org/traccar/database/GroupsManager.java
@@ -21,12 +21,15 @@ import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.Context;
-import org.traccar.helper.Log;
import org.traccar.model.Group;
public class GroupsManager extends BaseObjectManager<Group> implements ManagableObjects {
+ private static final Logger LOGGER = LoggerFactory.getLogger(GroupsManager.class);
+
private AtomicLong groupsLastUpdate = new AtomicLong();
private final long dataRefreshDelay;
@@ -47,7 +50,7 @@ public class GroupsManager extends BaseObjectManager<Group> implements Managable
}
}
- private void updateGroupCache(boolean force) throws SQLException {
+ public void updateGroupCache(boolean force) throws SQLException {
long lastUpdate = groupsLastUpdate.get();
if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay)
&& groupsLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) {
@@ -62,7 +65,7 @@ public class GroupsManager extends BaseObjectManager<Group> implements Managable
try {
updateGroupCache(true);
} catch (SQLException e) {
- Log.warning(e);
+ LOGGER.warn("Update group cache error", e);
}
result = super.getAllItems();
}
@@ -76,9 +79,9 @@ public class GroupsManager extends BaseObjectManager<Group> implements Managable
}
@Override
- protected void updateCachedItem(Group group) {
+ public void updateItem(Group group) throws SQLException {
checkGroupCycles(group);
- super.updateCachedItem(group);
+ super.updateItem(group);
}
@Override
diff --git a/src/main/java/org/traccar/database/IdentityManager.java b/src/main/java/org/traccar/database/IdentityManager.java
new file mode 100644
index 000000000..af6a6ce71
--- /dev/null
+++ b/src/main/java/org/traccar/database/IdentityManager.java
@@ -0,0 +1,50 @@
+/*
+ * 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
new file mode 100644
index 000000000..d8b5c9f52
--- /dev/null
+++ b/src/main/java/org/traccar/database/LdapProvider.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 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 javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.model.User;
+
+import java.util.Hashtable;
+
+public class LdapProvider {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LdapProvider.class);
+
+ private String url;
+ private String searchBase;
+ private String idAttribute;
+ private String nameAttribute;
+ private String mailAttribute;
+ private String searchFilter;
+ private String adminFilter;
+ private String serviceUser;
+ private String servicePassword;
+
+ public LdapProvider(Config config) {
+ String url = config.getString("ldap.url");
+ if (url != null) {
+ this.url = url;
+ } else {
+ this.url = "ldap://" + config.getString("ldap.server") + ":" + config.getInteger("ldap.port", 389);
+ }
+ this.searchBase = config.getString("ldap.base");
+ this.idAttribute = config.getString("ldap.idAttribute", "uid");
+ this.nameAttribute = config.getString("ldap.nameAttribute", "cn");
+ this.mailAttribute = config.getString("ldap.mailAttribute", "mail");
+ this.searchFilter = config.getString("ldap.searchFilter", "(" + idAttribute + "=:login)");
+ String adminGroup = config.getString("ldap.adminGroup");
+ this.adminFilter = config.getString("ldap.adminFilter");
+ if (this.adminFilter == null && adminGroup != null) {
+ this.adminFilter = "(&(" + idAttribute + "=:login)(memberOf=" + adminGroup + "))";
+ }
+ this.serviceUser = config.getString("ldap.user");
+ this.servicePassword = config.getString("ldap.password");
+ }
+
+ private InitialDirContext auth(String accountName, String password) throws NamingException {
+ Hashtable<String, String> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, url);
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, accountName);
+ env.put(Context.SECURITY_CREDENTIALS, password);
+
+ return new InitialDirContext(env);
+ }
+
+ private boolean isAdmin(String accountName) {
+ if (this.adminFilter != null) {
+ try {
+ InitialDirContext context = initContext();
+ String searchString = adminFilter.replace(":login", accountName);
+ SearchControls searchControls = new SearchControls();
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);
+ if (results.hasMoreElements()) {
+ results.nextElement();
+ if (results.hasMoreElements()) {
+ LOGGER.warn("Matched multiple users for the accountName: " + accountName);
+ return false;
+ }
+ return true;
+ }
+ } catch (NamingException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public InitialDirContext initContext() throws NamingException {
+ return auth(serviceUser, servicePassword);
+ }
+
+ private SearchResult lookupUser(String accountName) throws NamingException {
+ InitialDirContext context = initContext();
+
+ String searchString = searchFilter.replace(":login", accountName);
+
+ SearchControls searchControls = new SearchControls();
+ String[] attributeFilter = {idAttribute, nameAttribute, mailAttribute};
+ searchControls.setReturningAttributes(attributeFilter);
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+ NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);
+
+ SearchResult searchResult = null;
+ if (results.hasMoreElements()) {
+ searchResult = results.nextElement();
+ if (results.hasMoreElements()) {
+ LOGGER.warn("Matched multiple users for the accountName: " + accountName);
+ return null;
+ }
+ }
+
+ return searchResult;
+ }
+
+ public User getUser(String accountName) {
+ SearchResult ldapUser;
+ User user = new User();
+ try {
+ ldapUser = lookupUser(accountName);
+ if (ldapUser != null) {
+ Attribute attribute = ldapUser.getAttributes().get(idAttribute);
+ if (attribute != null) {
+ user.setLogin((String) attribute.get());
+ } else {
+ user.setLogin(accountName);
+ }
+ attribute = ldapUser.getAttributes().get(nameAttribute);
+ if (attribute != null) {
+ user.setName((String) attribute.get());
+ } else {
+ user.setName(accountName);
+ }
+ attribute = ldapUser.getAttributes().get(mailAttribute);
+ if (attribute != null) {
+ user.setEmail((String) attribute.get());
+ } else {
+ user.setEmail(accountName);
+ }
+ }
+ user.setAdministrator(isAdmin(accountName));
+ } catch (NamingException e) {
+ user.setLogin(accountName);
+ user.setName(accountName);
+ user.setEmail(accountName);
+ LOGGER.warn("User lookup error", e);
+ }
+ return user;
+ }
+
+ public boolean login(String username, String password) {
+ try {
+ SearchResult ldapUser = lookupUser(username);
+ if (ldapUser != null) {
+ auth(ldapUser.getNameInNamespace(), password).close();
+ return true;
+ }
+ } catch (NamingException e) {
+ return false;
+ }
+ return false;
+ }
+
+}
diff --git a/src/org/traccar/notification/NotificationMail.java b/src/main/java/org/traccar/database/MailManager.java
index 8707b10da..8a2f002cd 100644
--- a/src/org/traccar/notification/NotificationMail.java
+++ b/src/main/java/org/traccar/database/MailManager.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2018 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.
@@ -14,28 +14,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.notification;
+package org.traccar.database;
-import java.util.Properties;
+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;
-import org.traccar.Context;
-import org.traccar.helper.Log;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-import org.traccar.model.User;
-
-public final class NotificationMail {
+public final class MailManager {
- private NotificationMail() {
- }
+ private static final Logger LOGGER = LoggerFactory.getLogger(MailManager.class);
private static Properties getProperties(PropertiesProvider provider) {
Properties properties = new Properties();
@@ -45,18 +48,18 @@ public final class NotificationMail {
properties.put("mail.smtp.host", host);
properties.put("mail.smtp.port", String.valueOf(provider.getInteger("mail.smtp.port", 25)));
- String starttlsEnable = provider.getString("mail.smtp.starttls.enable");
+ Boolean starttlsEnable = provider.getBoolean("mail.smtp.starttls.enable");
if (starttlsEnable != null) {
- properties.put("mail.smtp.starttls.enable", Boolean.parseBoolean(starttlsEnable));
+ properties.put("mail.smtp.starttls.enable", String.valueOf(starttlsEnable));
}
- String starttlsRequired = provider.getString("mail.smtp.starttls.required");
+ Boolean starttlsRequired = provider.getBoolean("mail.smtp.starttls.required");
if (starttlsRequired != null) {
- properties.put("mail.smtp.starttls.required", Boolean.parseBoolean(starttlsRequired));
+ properties.put("mail.smtp.starttls.required", String.valueOf(starttlsRequired));
}
- String sslEnable = provider.getString("mail.smtp.ssl.enable");
+ Boolean sslEnable = provider.getBoolean("mail.smtp.ssl.enable");
if (sslEnable != null) {
- properties.put("mail.smtp.ssl.enable", Boolean.parseBoolean(sslEnable));
+ properties.put("mail.smtp.ssl.enable", String.valueOf(sslEnable));
}
String sslTrust = provider.getString("mail.smtp.ssl.trust");
if (sslTrust != null) {
@@ -84,7 +87,13 @@ public final class NotificationMail {
return properties;
}
- public static void sendMailSync(long userId, Event event, Position position) throws MessagingException {
+ 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;
@@ -95,7 +104,7 @@ public final class NotificationMail {
properties = getProperties(new PropertiesProvider(Context.getConfig()));
}
if (!properties.containsKey("mail.smtp.host")) {
- Log.warning("No SMTP configuration found");
+ LOGGER.warn("No SMTP configuration found");
return;
}
@@ -109,34 +118,30 @@ public final class NotificationMail {
}
message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
- MailMessage mailMessage = NotificationFormatter.formatMailMessage(userId, event, position);
- message.setSubject(mailMessage.getSubject());
+ message.setSubject(subject);
message.setSentDate(new Date());
- message.setContent(mailMessage.getBody(), "text/html; charset=utf-8");
- Transport transport = session.getTransport();
- try {
- Context.getStatisticsManager().registerMail();
+ 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());
- } finally {
- transport.close();
}
}
- public static void sendMailAsync(final long userId, final Event event, final Position position) {
- new Thread(new Runnable() {
- public void run() {
- try {
- sendMailSync(userId, event, position);
- } catch (MessagingException error) {
- Log.warning(error);
- }
- }
- }).start();
- }
-
}
diff --git a/src/main/java/org/traccar/database/MaintenancesManager.java b/src/main/java/org/traccar/database/MaintenancesManager.java
new file mode 100644
index 000000000..4e266cb78
--- /dev/null
+++ b/src/main/java/org/traccar/database/MaintenancesManager.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.database;
+
+import org.traccar.model.Maintenance;
+
+public class MaintenancesManager extends ExtendedObjectManager<Maintenance> {
+
+ public MaintenancesManager(DataManager dataManager) {
+ super(dataManager, Maintenance.class);
+ }
+
+}
diff --git a/src/org/traccar/database/ManagableObjects.java b/src/main/java/org/traccar/database/ManagableObjects.java
index ec9549493..ec9549493 100644
--- a/src/org/traccar/database/ManagableObjects.java
+++ b/src/main/java/org/traccar/database/ManagableObjects.java
diff --git a/src/org/traccar/database/MediaManager.java b/src/main/java/org/traccar/database/MediaManager.java
index 2c448a20c..edade5766 100644
--- a/src/org/traccar/database/MediaManager.java
+++ b/src/main/java/org/traccar/database/MediaManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,9 @@
*/
package org.traccar.database;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.traccar.Config;
-import org.traccar.helper.Log;
+import io.netty.buffer.ByteBuf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
@@ -32,10 +32,12 @@ import java.util.Date;
public class MediaManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MediaManager.class);
+
private String path;
- public MediaManager(Config config) {
- path = config.getString("media.path");
+ public MediaManager(String path) {
+ this.path = path;
}
private File createFile(String uniqueId, String name) throws IOException {
@@ -47,20 +49,22 @@ public class MediaManager {
return filePath.toFile();
}
- public String writeFile(String uniqueId, ChannelBuffer buf, String extension) {
- int size = buf.readableBytes();
- String name = new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()) + "." + extension;
- try (FileOutputStream output = new FileOutputStream(createFile(uniqueId, name));
- FileChannel fileChannel = output.getChannel()) {
- ByteBuffer byteBuffer = buf.toByteBuffer();
- int written = 0;
- while (written < size) {
- written += fileChannel.write(byteBuffer);
+ public String writeFile(String uniqueId, ByteBuf buf, String extension) {
+ if (path != null) {
+ int size = buf.readableBytes();
+ String name = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "." + extension;
+ try (FileOutputStream output = new FileOutputStream(createFile(uniqueId, name));
+ FileChannel fileChannel = output.getChannel()) {
+ ByteBuffer byteBuffer = buf.nioBuffer();
+ int written = 0;
+ while (written < size) {
+ written += fileChannel.write(byteBuffer);
+ }
+ fileChannel.force(false);
+ return name;
+ } catch (IOException e) {
+ LOGGER.warn("Save media file error", e);
}
- fileChannel.force(false);
- return name;
- } catch (IOException e) {
- Log.warning(e);
}
return null;
}
diff --git a/src/org/traccar/database/NotificationManager.java b/src/main/java/org/traccar/database/NotificationManager.java
index 73041a23f..09df4c571 100644
--- a/src/org/traccar/database/NotificationManager.java
+++ b/src/main/java/org/traccar/database/NotificationManager.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * 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.
@@ -19,32 +19,42 @@ package org.traccar.database;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
+import java.util.Date;
import java.util.HashSet;
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.helper.Log;
+import org.traccar.model.Calendar;
import org.traccar.model.Event;
import org.traccar.model.Notification;
import org.traccar.model.Position;
import org.traccar.model.Typed;
-import org.traccar.notification.NotificationMail;
-import org.traccar.notification.NotificationSms;
public class NotificationManager extends ExtendedObjectManager<Notification> {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificationManager.class);
+
+ private boolean geocodeOnRequest;
+
public NotificationManager(DataManager dataManager) {
super(dataManager, Notification.class);
+ geocodeOnRequest = Context.getConfig().getBoolean("geocoder.onRequest");
}
- private Set<Long> getEffectiveNotifications(long userId, long deviceId) {
+ 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)) {
- result.add(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;
@@ -54,41 +64,51 @@ public class NotificationManager extends ExtendedObjectManager<Notification> {
try {
getDataManager().addObject(event);
} catch (SQLException error) {
- Log.warning(error);
+ LOGGER.warn("Event save error", error);
+ }
+
+ if (position != null && geocodeOnRequest && Context.getGeocoder() != null && position.getAddress() == null) {
+ position.setAddress(Context.getGeocoder()
+ .getAddress(position.getLatitude(), position.getLongitude(), null));
}
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() != null
- && Context.getGeofenceManager().checkItemPermission(userId, event.getGeofenceId())) {
- boolean sentWeb = false;
- boolean sentMail = false;
- boolean sentSms = Context.getSmppManager() == null;
- for (long notificationId : getEffectiveNotifications(userId, deviceId)) {
+ 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.getServerTime())) {
Notification notification = getById(notificationId);
if (getById(notificationId).getType().equals(event.getType())) {
- if (!sentWeb && notification.getWeb()) {
- Context.getConnectionManager().updateEvent(userId, event);
- sentWeb = true;
- }
- if (!sentMail && notification.getMail()) {
- NotificationMail.sendMailAsync(userId, event, position);
- sentMail = true;
+ boolean filter = false;
+ if (event.getType().equals(Event.TYPE_ALARM)) {
+ String alarms = notification.getString("alarms");
+ if (alarms == null || !alarms.contains(event.getString(Position.KEY_ALARM))) {
+ filter = true;
+ }
}
- if (!sentSms && notification.getSms()) {
- NotificationSms.sendSmsAsync(userId, event, position);
- sentSms = true;
+ if (!filter) {
+ notificators.addAll(notification.getNotificatorsTypes());
}
}
- if (sentWeb && sentMail && sentSms) {
- break;
- }
+ }
+ for (String notificator : notificators) {
+ Context.getNotificatorManager().getNotificator(notificator).sendAsync(userId, event, position);
}
}
}
if (Context.getEventForwarder() != null) {
- Context.getEventForwarder().forwardEvent(event, position);
+ Context.getEventForwarder().forwardEvent(event, position, usersToForward);
}
}
@@ -106,7 +126,7 @@ public class NotificationManager extends ExtendedObjectManager<Notification> {
try {
types.add(new Typed(field.get(null).toString()));
} catch (IllegalArgumentException | IllegalAccessException error) {
- Log.warning(error);
+ LOGGER.warn("Get event types error", error);
}
}
}
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java
index 07b60ba58..ced0df1c0 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/main/java/org/traccar/database/PermissionsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,8 +15,9 @@
*/
package org.traccar.database;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.Context;
-import org.traccar.helper.Log;
import org.traccar.model.Attribute;
import org.traccar.model.BaseModel;
import org.traccar.model.Calendar;
@@ -25,6 +26,7 @@ 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.Permission;
@@ -39,6 +41,8 @@ import java.util.Set;
public class PermissionsManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PermissionsManager.class);
+
private final DataManager dataManager;
private final UsersManager usersManager;
@@ -57,33 +61,48 @@ public class PermissionsManager {
}
public User getUser(long userId) {
- return (User) usersManager.getById(userId);
+ return usersManager.getById(userId);
}
public Set<Long> getGroupPermissions(long userId) {
if (!groupPermissions.containsKey(userId)) {
- groupPermissions.put(userId, new HashSet<Long>());
+ groupPermissions.put(userId, new HashSet<>());
}
return groupPermissions.get(userId);
}
public Set<Long> getDevicePermissions(long userId) {
if (!devicePermissions.containsKey(userId)) {
- devicePermissions.put(userId, new HashSet<Long>());
+ devicePermissions.put(userId, new HashSet<>());
}
return devicePermissions.get(userId);
}
- public Set<Long> getDeviceUsers(long deviceId) {
+ private Set<Long> getAllDeviceUsers(long deviceId) {
if (!deviceUsers.containsKey(deviceId)) {
- deviceUsers.put(deviceId, new HashSet<Long>());
+ deviceUsers.put(deviceId, new HashSet<>());
}
return deviceUsers.get(deviceId);
}
+ 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) {
if (!groupDevices.containsKey(groupId)) {
- groupDevices.put(groupId, new HashSet<Long>());
+ groupDevices.put(groupId, new HashSet<>());
}
return groupDevices.get(groupId);
}
@@ -92,7 +111,7 @@ public class PermissionsManager {
try {
server = dataManager.getServer();
} catch (SQLException error) {
- Log.warning(error);
+ LOGGER.warn("Refresh server config error", error);
}
}
@@ -127,20 +146,20 @@ public class PermissionsManager {
}
} catch (SQLException | ClassNotFoundException error) {
- Log.warning(error);
+ LOGGER.warn("Refresh device permissions error", error);
}
deviceUsers.clear();
for (Map.Entry<Long, Set<Long>> entry : devicePermissions.entrySet()) {
for (long deviceId : entry.getValue()) {
- getDeviceUsers(deviceId).add(entry.getKey());
+ getAllDeviceUsers(deviceId).add(entry.getKey());
}
}
}
public boolean getUserAdmin(long userId) {
User user = getUser(userId);
- return user != null && user.getAdmin();
+ return user != null && user.getAdministrator();
}
public void checkAdmin(long userId) throws SecurityException {
@@ -174,14 +193,14 @@ public class PermissionsManager {
}
}
- public void checkDeviceLimit(long userId) throws SecurityException, SQLException {
+ public void checkDeviceLimit(long userId) throws SecurityException {
int deviceLimit = getUser(userId).getDeviceLimit();
if (deviceLimit != -1) {
int deviceCount = 0;
if (getUserManager(userId)) {
- deviceCount = Context.getDeviceManager().getManagedItems(userId).size();
+ deviceCount = Context.getDeviceManager().getAllManagedItems(userId).size();
} else {
- deviceCount = Context.getDeviceManager().getUserItems(userId).size();
+ deviceCount = Context.getDeviceManager().getAllUserItems(userId).size();
}
if (deviceCount >= deviceLimit) {
throw new SecurityException("User device limit reached");
@@ -242,7 +261,7 @@ public class PermissionsManager {
}
public void checkUserUpdate(long userId, User before, User after) throws SecurityException {
- if (before.getAdmin() != after.getAdmin()
+ if (before.getAdministrator() != after.getAdministrator()
|| before.getDeviceLimit() != after.getDeviceLimit()
|| before.getUserLimit() != after.getUserLimit()) {
checkAdmin(userId);
@@ -255,7 +274,8 @@ public class PermissionsManager {
}
if (before.getReadonly() != after.getReadonly()
|| before.getDeviceReadonly() != after.getDeviceReadonly()
- || before.getDisabled() != after.getDisabled()) {
+ || before.getDisabled() != after.getDisabled()
+ || before.getLimitCommands() != after.getLimitCommands()) {
if (userId == after.getId()) {
checkAdmin(userId);
}
@@ -321,6 +341,8 @@ public class PermissionsManager {
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 {
@@ -346,6 +368,7 @@ public class PermissionsManager {
Context.getDriversManager().refreshUserItems();
Context.getAttributesManager().refreshUserItems();
Context.getCommandsManager().refreshUserItems();
+ Context.getMaintenancesManager().refreshUserItems();
if (Context.getNotificationManager() != null) {
Context.getNotificationManager().refreshUserItems();
}
@@ -358,6 +381,7 @@ public class PermissionsManager {
Context.getDriversManager().refreshExtendedPermissions();
Context.getAttributesManager().refreshExtendedPermissions();
Context.getCommandsManager().refreshExtendedPermissions();
+ Context.getMaintenancesManager().refreshExtendedPermissions();
}
public void refreshPermissions(Permission permission) {
@@ -378,6 +402,8 @@ public class PermissionsManager {
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(Notification.class)
&& Context.getNotificationManager() != null) {
Context.getNotificationManager().refreshUserItems();
@@ -391,6 +417,8 @@ public class PermissionsManager {
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(Notification.class)
&& Context.getNotificationManager() != null) {
Context.getNotificationManager().refreshExtendedPermissions();
diff --git a/src/org/traccar/database/QueryBuilder.java b/src/main/java/org/traccar/database/QueryBuilder.java
index af33458a8..5528b2320 100644
--- a/src/org/traccar/database/QueryBuilder.java
+++ b/src/main/java/org/traccar/database/QueryBuilder.java
@@ -16,8 +16,9 @@
package org.traccar.database;
import com.fasterxml.jackson.core.JsonProcessingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.Context;
-import org.traccar.helper.Log;
import org.traccar.model.MiscFormatter;
import org.traccar.model.Permission;
@@ -43,6 +44,8 @@ import java.util.Map;
public final class QueryBuilder {
+ private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class);
+
private final Map<String, List<Integer>> indexMap = new HashMap<>();
private Connection connection;
private PreparedStatement statement;
@@ -289,7 +292,7 @@ public final class QueryBuilder {
}
}
} catch (IllegalAccessException | InvocationTargetException | JsonProcessingException error) {
- Log.warning(error);
+ LOGGER.warn("Get property error", error);
}
}
}
@@ -321,7 +324,7 @@ public final class QueryBuilder {
try {
method.invoke(object, resultSet.getBoolean(name));
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -332,7 +335,7 @@ public final class QueryBuilder {
try {
method.invoke(object, resultSet.getInt(name));
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -343,7 +346,7 @@ public final class QueryBuilder {
try {
method.invoke(object, resultSet.getLong(name));
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -354,7 +357,7 @@ public final class QueryBuilder {
try {
method.invoke(object, resultSet.getDouble(name));
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -365,7 +368,7 @@ public final class QueryBuilder {
try {
method.invoke(object, resultSet.getString(name));
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -379,7 +382,7 @@ public final class QueryBuilder {
method.invoke(object, new Date(timestamp.getTime()));
}
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -390,7 +393,7 @@ public final class QueryBuilder {
try {
method.invoke(object, resultSet.getBytes(name));
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -403,7 +406,7 @@ public final class QueryBuilder {
try {
method.invoke(object, Context.getObjectMapper().readValue(value, parameterType));
} catch (InvocationTargetException | IllegalAccessException | IOException error) {
- Log.warning(error);
+ LOGGER.warn("Set property error", error);
}
}
}
diff --git a/src/org/traccar/database/QueryExtended.java b/src/main/java/org/traccar/database/QueryExtended.java
index 07bc2c211..07bc2c211 100644
--- a/src/org/traccar/database/QueryExtended.java
+++ b/src/main/java/org/traccar/database/QueryExtended.java
diff --git a/src/org/traccar/database/QueryIgnore.java b/src/main/java/org/traccar/database/QueryIgnore.java
index ac835cf2f..ac835cf2f 100644
--- a/src/org/traccar/database/QueryIgnore.java
+++ b/src/main/java/org/traccar/database/QueryIgnore.java
diff --git a/src/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java
index 0b4d11378..15dda4520 100644
--- a/src/org/traccar/database/SimpleObjectManager.java
+++ b/src/main/java/org/traccar/database/SimpleObjectManager.java
@@ -22,8 +22,9 @@ 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.helper.Log;
import org.traccar.model.BaseModel;
import org.traccar.model.Permission;
import org.traccar.model.User;
@@ -31,6 +32,8 @@ 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) {
@@ -77,7 +80,7 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
getUserItems(permission.getOwnerId()).add(permission.getPropertyId());
}
} catch (SQLException | ClassNotFoundException error) {
- Log.warning(error);
+ LOGGER.warn("Error getting permissions", error);
}
}
}
diff --git a/src/org/traccar/database/StatisticsManager.java b/src/main/java/org/traccar/database/StatisticsManager.java
index 9a3ff06bd..e59f8e767 100644
--- a/src/org/traccar/database/StatisticsManager.java
+++ b/src/main/java/org/traccar/database/StatisticsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,13 +15,17 @@
*/
package org.traccar.database;
-import com.ning.http.client.Request;
-import com.ning.http.client.RequestBuilder;
-import org.joda.time.format.ISODateTimeFormat;
-import org.traccar.Context;
-import org.traccar.helper.Log;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.helper.DateUtil;
import org.traccar.model.Statistics;
+import javax.inject.Inject;
+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;
@@ -31,8 +35,14 @@ import java.util.concurrent.atomic.AtomicInteger;
public class StatisticsManager {
+ private static final Logger LOGGER = LoggerFactory.getLogger(StatisticsManager.class);
+
private static final int SPLIT_MODE = Calendar.DAY_OF_MONTH;
+ private final Config config;
+ private final DataManager dataManager;
+ private final Client client;
+
private AtomicInteger lastUpdate = new AtomicInteger(Calendar.getInstance().get(SPLIT_MODE));
private Set<Long> users = new HashSet<>();
@@ -46,6 +56,13 @@ public class StatisticsManager {
private int geocoderRequests;
private int geolocationRequests;
+ @Inject
+ public StatisticsManager(Config config, DataManager dataManager, Client client) {
+ this.config = config;
+ this.dataManager = dataManager;
+ this.client = client;
+ }
+
private void checkSplit() {
int currentUpdate = Calendar.getInstance().get(SPLIT_MODE);
if (lastUpdate.getAndSet(currentUpdate) != currentUpdate) {
@@ -62,30 +79,29 @@ public class StatisticsManager {
statistics.setGeolocationRequests(geolocationRequests);
try {
- Context.getDataManager().addObject(statistics);
+ dataManager.addObject(statistics);
} catch (SQLException e) {
- Log.warning(e);
+ LOGGER.warn("Error saving statistics", e);
}
- String url = Context.getConfig().getString("server.statistics");
+ String url = config.getString(Keys.SERVER_STATISTICS);
if (url != null) {
- String time = ISODateTimeFormat.dateTime().print(statistics.getCaptureTime().getTime());
- Request request = new RequestBuilder("POST")
- .setUrl(url)
- .addHeader("Content-Type", "application/x-www-form-urlencoded")
- .addFormParam("version", Log.getAppVersion())
- .addFormParam("captureTime", time)
- .addFormParam("activeUsers", String.valueOf(statistics.getActiveUsers()))
- .addFormParam("activeDevices", String.valueOf(statistics.getActiveDevices()))
- .addFormParam("requests", String.valueOf(statistics.getRequests()))
- .addFormParam("messagesReceived", String.valueOf(statistics.getMessagesReceived()))
- .addFormParam("messagesStored", String.valueOf(statistics.getMessagesStored()))
- .addFormParam("mailSent", String.valueOf(statistics.getMailSent()))
- .addFormParam("smsSent", String.valueOf(statistics.getSmsSent()))
- .addFormParam("geocoderRequests", String.valueOf(statistics.getGeocoderRequests()))
- .addFormParam("geolocationRequests", String.valueOf(statistics.getGeolocationRequests()))
- .build();
- Context.getAsyncHttpClient().prepareRequest(request).execute();
+ String time = DateUtil.formatDate(statistics.getCaptureTime());
+
+ Form form = new Form();
+ form.param("version", getClass().getPackage().getImplementationVersion());
+ form.param("captureTime", time);
+ form.param("activeUsers", String.valueOf(statistics.getActiveUsers()));
+ form.param("activeDevices", String.valueOf(statistics.getActiveDevices()));
+ form.param("requests", String.valueOf(statistics.getRequests()));
+ form.param("messagesReceived", String.valueOf(statistics.getMessagesReceived()));
+ form.param("messagesStored", String.valueOf(statistics.getMessagesStored()));
+ form.param("mailSent", String.valueOf(statistics.getMailSent()));
+ form.param("smsSent", String.valueOf(statistics.getSmsSent()));
+ form.param("geocoderRequests", String.valueOf(statistics.getGeocoderRequests()));
+ form.param("geolocationRequests", String.valueOf(statistics.getGeolocationRequests()));
+
+ client.target(url).request().async().post(Entity.form(form));
}
users.clear();
diff --git a/src/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java
index 576a9e6c7..576a9e6c7 100644
--- a/src/org/traccar/database/UsersManager.java
+++ b/src/main/java/org/traccar/database/UsersManager.java
diff --git a/src/org/traccar/geocoder/Address.java b/src/main/java/org/traccar/geocoder/Address.java
index d542d1b19..fe39da8e1 100644
--- a/src/org/traccar/geocoder/Address.java
+++ b/src/main/java/org/traccar/geocoder/Address.java
@@ -97,4 +97,14 @@ public class Address {
this.house = house;
}
+ private String formattedAddress;
+
+ public String getFormattedAddress() {
+ return formattedAddress;
+ }
+
+ public void setFormattedAddress(String formattedAddress) {
+ this.formattedAddress = formattedAddress;
+ }
+
}
diff --git a/src/org/traccar/geocoder/AddressFormat.java b/src/main/java/org/traccar/geocoder/AddressFormat.java
index 0800d6dcc..ad19432b9 100644
--- a/src/org/traccar/geocoder/AddressFormat.java
+++ b/src/main/java/org/traccar/geocoder/AddressFormat.java
@@ -30,6 +30,7 @@ import java.text.ParsePosition;
* %u - suburb
* %r - street (road)
* %h - house
+ * %f - formatted address
*
*/
public class AddressFormat extends Format {
@@ -66,6 +67,7 @@ public class AddressFormat extends Format {
result = replace(result, "%u", address.getSuburb());
result = replace(result, "%r", address.getStreet());
result = replace(result, "%h", address.getHouse());
+ result = replace(result, "%f", address.getFormattedAddress());
result = result.replaceAll("^[, ]*", "");
diff --git a/src/main/java/org/traccar/geocoder/BanGeocoder.java b/src/main/java/org/traccar/geocoder/BanGeocoder.java
new file mode 100644
index 000000000..b1f0900a4
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/BanGeocoder.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 Olivier Girondel (olivier@biniou.info)
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/*
+ * API documentation: https://adresse.data.gouv.fr/api
+ */
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+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);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ JsonArray result = json.getJsonArray("features");
+
+ if (result != null && !result.isEmpty()) {
+ JsonObject location = result.getJsonObject(0).getJsonObject("properties");
+ Address address = new Address();
+
+ address.setCountry("FR");
+ if (location.containsKey("postcode")) {
+ address.setPostcode(location.getString("postcode"));
+ }
+ if (location.containsKey("context")) {
+ address.setDistrict(location.getString("context"));
+ }
+ if (location.containsKey("name")) {
+ address.setStreet(location.getString("name"));
+ }
+ if (location.containsKey("city")) {
+ address.setSettlement(location.getString("city"));
+ }
+ if (location.containsKey("housenumber")) {
+ address.setHouse(location.getString("housenumber"));
+ }
+ if (location.containsKey("label")) {
+ address.setFormattedAddress(location.getString("label"));
+ }
+
+ return address;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/geocoder/BingMapsGeocoder.java b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
index a9b36219a..32a26ee0c 100644
--- a/src/org/traccar/geocoder/BingMapsGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java
@@ -1,5 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
+ * Copyright 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.
@@ -20,8 +21,8 @@ import javax.json.JsonObject;
public class BingMapsGeocoder extends JsonGeocoder {
- public BingMapsGeocoder(String url, String key, int cacheSize) {
- super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize);
+ public BingMapsGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat);
}
@Override
@@ -50,6 +51,9 @@ public class BingMapsGeocoder extends JsonGeocoder {
if (location.containsKey("postalCode")) {
address.setPostcode(location.getString("postalCode"));
}
+ if (location.containsKey("formattedAddress")) {
+ address.setFormattedAddress(location.getString("formattedAddress"));
+ }
return address;
}
}
diff --git a/src/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
index 0c76e4625..c7a68c293 100644
--- a/src/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
@@ -1,5 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
+ * Copyright 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.
@@ -19,8 +20,8 @@ import javax.json.JsonObject;
public class FactualGeocoder extends JsonGeocoder {
- public FactualGeocoder(String url, String key, int cacheSize) {
- super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize);
+ public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
index 73ff85aa4..39a3300a0 100644
--- a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -30,8 +30,8 @@ public class GeocodeFarmGeocoder extends JsonGeocoder {
}
return url;
}
- public GeocodeFarmGeocoder(String key, String language, int cacheSize) {
- super(formatUrl(key, language), cacheSize);
+ public GeocodeFarmGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(key, language), cacheSize, addressFormat);
}
@Override
@@ -41,23 +41,27 @@ public class GeocodeFarmGeocoder extends JsonGeocoder {
JsonObject result = json
.getJsonObject("geocoding_results")
.getJsonArray("RESULTS")
- .getJsonObject(0)
- .getJsonObject("ADDRESS");
+ .getJsonObject(0);
- if (result.containsKey("street_number")) {
- address.setStreet(result.getString("street_number"));
+ JsonObject resultAddress = result.getJsonObject("ADDRESS");
+
+ if (result.containsKey("formatted_address")) {
+ address.setFormattedAddress(result.getString("formatted_address"));
+ }
+ if (resultAddress.containsKey("street_number")) {
+ address.setStreet(resultAddress.getString("street_number"));
}
- if (result.containsKey("street_name")) {
- address.setStreet(result.getString("street_name"));
+ if (resultAddress.containsKey("street_name")) {
+ address.setStreet(resultAddress.getString("street_name"));
}
- if (result.containsKey("locality")) {
- address.setSettlement(result.getString("locality"));
+ if (resultAddress.containsKey("locality")) {
+ address.setSettlement(resultAddress.getString("locality"));
}
- if (result.containsKey("admin_1")) {
- address.setState(result.getString("admin_1"));
+ if (resultAddress.containsKey("admin_1")) {
+ address.setState(resultAddress.getString("admin_1"));
}
- if (result.containsKey("country")) {
- address.setCountry(result.getString("country"));
+ if (resultAddress.containsKey("country")) {
+ address.setCountry(resultAddress.getString("country"));
}
return address;
diff --git a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
new file mode 100644
index 000000000..aca360c3d
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.JsonObject;
+
+public class GeocodeXyzGeocoder extends JsonGeocoder {
+
+ private static String formatUrl(String key) {
+ String url = "https://geocode.xyz/%f,%f?geoit=JSON";
+ if (key != null) {
+ url += "&key=" + key;
+ }
+ return url;
+ }
+
+ public GeocodeXyzGeocoder(String key, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(key), cacheSize, addressFormat);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ Address address = new Address();
+
+ if (json.containsKey("stnumber")) {
+ address.setHouse(json.getString("stnumber"));
+ }
+ if (json.containsKey("staddress")) {
+ address.setStreet(json.getString("staddress"));
+ }
+ if (json.containsKey("city")) {
+ address.setSettlement(json.getString("city"));
+ }
+ if (json.containsKey("region")) {
+ address.setState(json.getString("region"));
+ }
+ if (json.containsKey("prov")) {
+ address.setCountry(json.getString("prov"));
+ }
+ if (json.containsKey("postal")) {
+ address.setPostcode(json.getString("postal"));
+ }
+
+ return address;
+ }
+
+}
diff --git a/src/org/traccar/geocoder/Geocoder.java b/src/main/java/org/traccar/geocoder/Geocoder.java
index 3ce3fb67f..587a27520 100644
--- a/src/org/traccar/geocoder/Geocoder.java
+++ b/src/main/java/org/traccar/geocoder/Geocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -25,6 +25,6 @@ public interface Geocoder {
}
- void getAddress(AddressFormat format, double latitude, double longitude, ReverseGeocoderCallback callback);
+ String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback);
}
diff --git a/src/org/traccar/geocoder/GeocoderException.java b/src/main/java/org/traccar/geocoder/GeocoderException.java
index 608916641..608916641 100644
--- a/src/org/traccar/geocoder/GeocoderException.java
+++ b/src/main/java/org/traccar/geocoder/GeocoderException.java
diff --git a/src/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
index 1432166e9..3a173f985 100644
--- a/src/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 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.
@@ -19,12 +19,12 @@ import javax.json.JsonObject;
public class GisgraphyGeocoder extends JsonGeocoder {
- public GisgraphyGeocoder() {
- this("http://services.gisgraphy.com/reversegeocoding/search", 0);
+ public GisgraphyGeocoder(AddressFormat addressFormat) {
+ this("http://services.gisgraphy.com/reversegeocoding/search", 0, addressFormat);
}
- public GisgraphyGeocoder(String url, int cacheSize) {
- super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize);
+ public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) {
+ super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize, addressFormat);
}
@Override
@@ -45,6 +45,9 @@ public class GisgraphyGeocoder extends JsonGeocoder {
if (result.containsKey("countryCode")) {
address.setCountry(result.getString("countryCode"));
}
+ if (result.containsKey("formatedFull")) {
+ address.setFormattedAddress(result.getString("formatedFull"));
+ }
return address;
}
diff --git a/src/org/traccar/geocoder/GoogleGeocoder.java b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
index b38870c8f..9494cab45 100644
--- a/src/org/traccar/geocoder/GoogleGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -32,8 +32,8 @@ public class GoogleGeocoder extends JsonGeocoder {
return url;
}
- public GoogleGeocoder(String key, String language, int cacheSize) {
- super(formatUrl(key, language), cacheSize);
+ public GoogleGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(key, language), cacheSize, addressFormat);
}
@Override
@@ -46,6 +46,10 @@ public class GoogleGeocoder extends JsonGeocoder {
JsonObject result = (JsonObject) results.get(0);
JsonArray components = result.getJsonArray("address_components");
+ if (result.containsKey("formatted_address")) {
+ address.setFormattedAddress(result.getString("formatted_address"));
+ }
+
for (JsonObject component : components.getValuesAs(JsonObject.class)) {
String value = component.getString("short_name");
@@ -86,4 +90,9 @@ public class GoogleGeocoder extends JsonGeocoder {
return null;
}
+ @Override
+ protected String parseError(JsonObject json) {
+ return json.getString("error_message");
+ }
+
}
diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java
new file mode 100644
index 000000000..aaf11d74d
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.JsonObject;
+
+public class HereGeocoder extends JsonGeocoder {
+
+ private static String formatUrl(String url, String id, String key, String language) {
+ if (url == null) {
+ url = "https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json";
+ }
+ url += "?mode=retrieveAddresses&maxresults=1";
+ url += "&prox=%f,%f,0";
+ url += "&app_id=" + id;
+ url += "&app_code=" + key;
+ url += "&apiKey=" + key;
+ if (language != null) {
+ url += "&language=" + language;
+ }
+ return url;
+ }
+
+ public HereGeocoder(
+ String url, String id, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(url, id, key, language), cacheSize, addressFormat);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ JsonObject result = json
+ .getJsonObject("Response")
+ .getJsonArray("View")
+ .getJsonObject(0)
+ .getJsonArray("Result")
+ .getJsonObject(0)
+ .getJsonObject("Location")
+ .getJsonObject("Address");
+
+ if (result != null) {
+ Address address = new Address();
+
+ if (json.containsKey("Label")) {
+ address.setFormattedAddress(json.getString("Label"));
+ }
+
+ if (result.containsKey("HouseNumber")) {
+ address.setHouse(result.getString("HouseNumber"));
+ }
+ if (result.containsKey("Street")) {
+ address.setStreet(result.getString("Street"));
+ }
+ 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")) {
+ address.setCountry(result.getString("Country").toUpperCase());
+ }
+ if (result.containsKey("PostalCode")) {
+ address.setPostcode(result.getString("PostalCode"));
+ }
+
+ return address;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/geocoder/JsonGeocoder.java b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
new file mode 100644
index 000000000..ed59a1d8d
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.geocoder;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+
+import javax.json.JsonObject;
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.InvocationCallback;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public abstract class JsonGeocoder implements Geocoder {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JsonGeocoder.class);
+
+ private final String url;
+ private final AddressFormat addressFormat;
+
+ private Map<Map.Entry<Double, Double>, String> cache;
+
+ public JsonGeocoder(String url, final int cacheSize, AddressFormat addressFormat) {
+ this.url = url;
+ this.addressFormat = addressFormat;
+ if (cacheSize > 0) {
+ this.cache = Collections.synchronizedMap(new LinkedHashMap<Map.Entry<Double, Double>, String>() {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > cacheSize;
+ }
+ });
+ }
+ }
+
+ private String handleResponse(
+ double latitude, double longitude, JsonObject json, ReverseGeocoderCallback callback) {
+
+ Address address = parseAddress(json);
+ if (address != null) {
+ String formattedAddress = addressFormat.format(address);
+ if (cache != null) {
+ cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress);
+ }
+ if (callback != null) {
+ callback.onSuccess(formattedAddress);
+ }
+ return formattedAddress;
+ } else {
+ String msg = "Empty address. Error: " + parseError(json);
+ if (callback != null) {
+ callback.onFailure(new GeocoderException(msg));
+ } else {
+ LOGGER.warn(msg);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getAddress(
+ final double latitude, final double longitude, final ReverseGeocoderCallback callback) {
+
+ if (cache != null) {
+ String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude));
+ if (cachedAddress != null) {
+ if (callback != null) {
+ callback.onSuccess(cachedAddress);
+ }
+ return cachedAddress;
+ }
+ }
+
+ Invocation.Builder request = Context.getClient().target(String.format(url, latitude, longitude)).request();
+
+ if (callback != null) {
+ request.async().get(new InvocationCallback<JsonObject>() {
+ @Override
+ public void completed(JsonObject json) {
+ handleResponse(latitude, longitude, json, callback);
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ callback.onFailure(throwable);
+ }
+ });
+ } else {
+ try {
+ return handleResponse(latitude, longitude, request.get(JsonObject.class), null);
+ } catch (ClientErrorException e) {
+ LOGGER.warn("Geocoder network error", e);
+ }
+ }
+ return null;
+ }
+
+ public abstract Address parseAddress(JsonObject json);
+
+ protected String parseError(JsonObject json) {
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
index 7d7217e91..4029e3f07 100644
--- a/src/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
@@ -1,5 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
+ * Copyright 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.
@@ -20,8 +21,8 @@ import javax.json.JsonObject;
public class MapQuestGeocoder extends JsonGeocoder {
- public MapQuestGeocoder(String url, String key, int cacheSize) {
- super(url + "?key=" + key + "&location=%f,%f", cacheSize);
+ public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "?key=" + key + "&location=%f,%f", cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
new file mode 100644
index 000000000..2b70708a1
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 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.geocoder;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+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);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ JsonArray results = json.getJsonArray("results");
+
+ if (!results.isEmpty()) {
+ Address address = new Address();
+
+ JsonObject result = (JsonObject) results.get(0);
+
+ if (result.containsKey("formatted_address")) {
+ address.setFormattedAddress(result.getString("formatted_address"));
+ }
+
+ if (result.containsKey("house_number") && !result.getString("house_number").isEmpty()) {
+ address.setHouse(result.getString("house_number"));
+ } else if (result.containsKey("house_name") && !result.getString("house_name").isEmpty()) {
+ address.setHouse(result.getString("house_name"));
+ }
+
+ if (result.containsKey("street")) {
+ address.setStreet(result.getString("street"));
+ }
+
+ if (result.containsKey("locality") && !result.getString("locality").isEmpty()) {
+ address.setSuburb(result.getString("locality"));
+ } else if (result.containsKey("sublocality") && !result.getString("sublocality").isEmpty()) {
+ address.setSuburb(result.getString("sublocality"));
+ } else if (result.containsKey("subsublocality") && !result.getString("subsublocality").isEmpty()) {
+ address.setSuburb(result.getString("subsublocality"));
+ }
+
+ if (result.containsKey("city") && !result.getString("city").isEmpty()) {
+ address.setSettlement(result.getString("city"));
+ } else if (result.containsKey("village") && !result.getString("village").isEmpty()) {
+ address.setSettlement(result.getString("village"));
+ }
+
+ if (result.containsKey("district")) {
+ address.setDistrict(result.getString("district"));
+ } else if (result.containsKey("subDistrict")) {
+ address.setDistrict(result.getString("subDistrict"));
+ }
+
+ if (result.containsKey("state")) {
+ address.setState(result.getString("state"));
+ }
+
+ if (result.containsKey("pincode")) {
+ address.setPostcode(result.getString("pincode"));
+ }
+
+ return address;
+ }
+ return null;
+ }
+}
diff --git a/src/org/traccar/geocoder/NominatimGeocoder.java b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
index 051727e93..8db25bf15 100644
--- a/src/org/traccar/geocoder/NominatimGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -21,7 +21,7 @@ public class NominatimGeocoder extends JsonGeocoder {
private static String formatUrl(String url, String key, String language) {
if (url == null) {
- url = "http://nominatim.openstreetmap.org/reverse";
+ url = "https://nominatim.openstreetmap.org/reverse";
}
url += "?format=json&lat=%f&lon=%f&zoom=18&addressdetails=1";
if (key != null) {
@@ -33,8 +33,8 @@ public class NominatimGeocoder extends JsonGeocoder {
return url;
}
- public NominatimGeocoder(String url, String key, String language, int cacheSize) {
- super(formatUrl(url, key, language), cacheSize);
+ public NominatimGeocoder(String url, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(url, key, language), cacheSize, addressFormat);
}
@Override
@@ -44,6 +44,10 @@ public class NominatimGeocoder extends JsonGeocoder {
if (result != null) {
Address address = new Address();
+ if (json.containsKey("display_name")) {
+ address.setFormattedAddress(json.getString("display_name"));
+ }
+
if (result.containsKey("house_number")) {
address.setHouse(result.getString("house_number"));
}
diff --git a/src/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
index 9fa56a4a3..822b6e91e 100644
--- a/src/org/traccar/geocoder/OpenCageGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 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.
@@ -21,8 +21,8 @@ import javax.json.JsonObject;
public class OpenCageGeocoder extends JsonGeocoder {
- public OpenCageGeocoder(String url, String key, int cacheSize) {
- super(url + "/json?q=%f,%f&key=" + key, cacheSize);
+ public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "/json?q=%f,%f&no_annotations=1&key=" + key, cacheSize, addressFormat);
}
@Override
@@ -33,6 +33,9 @@ public class OpenCageGeocoder extends JsonGeocoder {
if (location != null) {
Address address = new Address();
+ if (result.getJsonObject(0).containsKey("formatted")) {
+ address.setFormattedAddress(result.getJsonObject(0).getString("formatted"));
+ }
if (location.containsKey("building")) {
address.setHouse(location.getString("building"));
}
diff --git a/src/org/traccar/geofence/GeofenceCircle.java b/src/main/java/org/traccar/geofence/GeofenceCircle.java
index d78734714..f6fca63ca 100644
--- a/src/org/traccar/geofence/GeofenceCircle.java
+++ b/src/main/java/org/traccar/geofence/GeofenceCircle.java
@@ -39,9 +39,13 @@ public class GeofenceCircle extends GeofenceGeometry {
this.radius = radius;
}
+ public double distanceFromCenter(double latitude, double longitude) {
+ return DistanceCalculator.distance(centerLatitude, centerLongitude, latitude, longitude);
+ }
+
@Override
public boolean containsPoint(double latitude, double longitude) {
- return DistanceCalculator.distance(centerLatitude, centerLongitude, latitude, longitude) <= radius;
+ return distanceFromCenter(latitude, longitude) <= radius;
}
@Override
diff --git a/src/org/traccar/geofence/GeofenceGeometry.java b/src/main/java/org/traccar/geofence/GeofenceGeometry.java
index 857ba3414..857ba3414 100644
--- a/src/org/traccar/geofence/GeofenceGeometry.java
+++ b/src/main/java/org/traccar/geofence/GeofenceGeometry.java
diff --git a/src/org/traccar/geofence/GeofencePolygon.java b/src/main/java/org/traccar/geofence/GeofencePolygon.java
index 2048ba26d..2048ba26d 100644
--- a/src/org/traccar/geofence/GeofencePolygon.java
+++ b/src/main/java/org/traccar/geofence/GeofencePolygon.java
diff --git a/src/org/traccar/geofence/GeofencePolyline.java b/src/main/java/org/traccar/geofence/GeofencePolyline.java
index d84f512e3..d84f512e3 100644
--- a/src/org/traccar/geofence/GeofencePolyline.java
+++ b/src/main/java/org/traccar/geofence/GeofencePolyline.java
diff --git a/src/org/traccar/geolocation/GeolocationException.java b/src/main/java/org/traccar/geolocation/GeolocationException.java
index 5847cc807..5847cc807 100644
--- a/src/org/traccar/geolocation/GeolocationException.java
+++ b/src/main/java/org/traccar/geolocation/GeolocationException.java
diff --git a/src/org/traccar/geolocation/GeolocationProvider.java b/src/main/java/org/traccar/geolocation/GeolocationProvider.java
index d9dec6bbb..d9dec6bbb 100644
--- a/src/org/traccar/geolocation/GeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/GeolocationProvider.java
diff --git a/src/org/traccar/geolocation/GoogleGeolocationProvider.java b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
index 5901b47cd..5901b47cd 100644
--- a/src/org/traccar/geolocation/GoogleGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java
diff --git a/src/org/traccar/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
index c6a73a52b..c6a73a52b 100644
--- a/src/org/traccar/geolocation/MozillaGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java
diff --git a/src/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
index d6e45b550..cb3094e16 100644
--- a/src/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,25 +15,21 @@
*/
package org.traccar.geolocation;
-import com.ning.http.client.AsyncCompletionHandler;
-import com.ning.http.client.Response;
import org.traccar.Context;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
-import javax.json.Json;
import javax.json.JsonObject;
-import javax.json.JsonReader;
+import javax.ws.rs.client.InvocationCallback;
public class OpenCellIdGeolocationProvider implements GeolocationProvider {
private String url;
- public OpenCellIdGeolocationProvider(String key) {
- this("http://opencellid.org/cell/get", key);
- }
-
public OpenCellIdGeolocationProvider(String url, String key) {
+ if (url == null) {
+ url = "http://opencellid.org/cell/get";
+ }
this.url = url + "?format=json&mcc=%d&mnc=%d&lac=%d&cellid=%d&key=" + key;
}
@@ -45,25 +41,21 @@ public class OpenCellIdGeolocationProvider implements GeolocationProvider {
String request = String.format(url, cellTower.getMobileCountryCode(), cellTower.getMobileNetworkCode(),
cellTower.getLocationAreaCode(), cellTower.getCellId());
- Context.getAsyncHttpClient().prepareGet(request).execute(new AsyncCompletionHandler() {
+ Context.getClient().target(request).request().async().get(new InvocationCallback<JsonObject>() {
@Override
- public Object onCompleted(Response response) throws Exception {
- try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
- JsonObject json = reader.readObject();
- if (json.containsKey("lat") && json.containsKey("lon")) {
- callback.onSuccess(
- json.getJsonNumber("lat").doubleValue(),
- json.getJsonNumber("lon").doubleValue(), 0);
- } else {
- callback.onFailure(new GeolocationException("Coordinates are missing"));
- }
+ public void completed(JsonObject json) {
+ if (json.containsKey("lat") && json.containsKey("lon")) {
+ callback.onSuccess(
+ json.getJsonNumber("lat").doubleValue(),
+ json.getJsonNumber("lon").doubleValue(), 0);
+ } else {
+ callback.onFailure(new GeolocationException("Coordinates are missing"));
}
- return null;
}
@Override
- public void onThrowable(Throwable t) {
- callback.onFailure(t);
+ public void failed(Throwable throwable) {
+ callback.onFailure(throwable);
}
});
diff --git a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
new file mode 100644
index 000000000..f71620d8a
--- /dev/null
+++ b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.geolocation;
+
+import org.traccar.Context;
+import org.traccar.model.Network;
+
+import javax.json.JsonObject;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+
+public class UniversalGeolocationProvider implements GeolocationProvider {
+
+ private String url;
+
+ public UniversalGeolocationProvider(String url, String key) {
+ 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>() {
+ @Override
+ public void completed(JsonObject json) {
+ if (json.containsKey("error")) {
+ callback.onFailure(new GeolocationException(json.getJsonObject("error").getString("message")));
+ } else {
+ JsonObject location = json.getJsonObject("location");
+ callback.onSuccess(
+ location.getJsonNumber("lat").doubleValue(),
+ location.getJsonNumber("lng").doubleValue(),
+ json.getJsonNumber("accuracy").doubleValue());
+ }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ callback.onFailure(throwable);
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/geolocation/UnwiredGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
index dcc5a4e7a..963bcb688 100644
--- a/src/org/traccar/geolocation/UnwiredGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -17,21 +17,16 @@ package org.traccar.geolocation;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.ning.http.client.AsyncCompletionHandler;
-import com.ning.http.client.Response;
import org.traccar.Context;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.WifiAccessPoint;
-import javax.json.Json;
import javax.json.JsonObject;
-import javax.json.JsonReader;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
import java.util.Collection;
public class UnwiredGeolocationProvider implements GeolocationProvider {
@@ -90,39 +85,27 @@ public class UnwiredGeolocationProvider implements GeolocationProvider {
@Override
public void getLocation(Network network, final LocationProviderCallback callback) {
- try {
- ObjectNode json = objectMapper.valueToTree(network);
- json.put("token", key);
- String request = objectMapper.writeValueAsString(json);
- Context.getAsyncHttpClient().preparePost(url)
- .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
- .setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(request.length()))
- .setBody(request).execute(new AsyncCompletionHandler() {
- @Override
- public Object onCompleted(Response response) throws Exception {
- try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
- JsonObject json = reader.readObject();
- if (json.getString("status").equals("error")) {
- callback.onFailure(
- new GeolocationException(json.getString("message")));
- } else {
- callback.onSuccess(
- json.getJsonNumber("lat").doubleValue(),
- json.getJsonNumber("lon").doubleValue(),
- json.getJsonNumber("accuracy").doubleValue());
- }
- }
- return null;
- }
+ ObjectNode json = objectMapper.valueToTree(network);
+ json.put("token", key);
- @Override
- public void onThrowable(Throwable t) {
- callback.onFailure(t);
+ Context.getClient().target(url).request().async().post(Entity.json(json), new InvocationCallback<JsonObject>() {
+ @Override
+ public void completed(JsonObject json) {
+ if (json.getString("status").equals("error")) {
+ callback.onFailure(new GeolocationException(json.getString("message")));
+ } else {
+ callback.onSuccess(
+ json.getJsonNumber("lat").doubleValue(),
+ json.getJsonNumber("lon").doubleValue(),
+ json.getJsonNumber("accuracy").doubleValue());
}
- });
- } catch (JsonProcessingException e) {
- callback.onFailure(e);
- }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ callback.onFailure(throwable);
+ }
+ });
}
}
diff --git a/src/org/traccar/processing/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
index 1e702d17f..153da29b9 100644
--- a/src/org/traccar/processing/ComputedAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.processing;
+package org.traccar.handler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -25,35 +25,47 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import io.netty.channel.ChannelHandler;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.JexlException;
import org.apache.commons.jexl2.MapContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.BaseDataHandler;
-import org.traccar.Context;
-import org.traccar.helper.Log;
+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;
+@ChannelHandler.Sharable
public class ComputedAttributesHandler extends BaseDataHandler {
- private JexlEngine engine;
+ private static final Logger LOGGER = LoggerFactory.getLogger(ComputedAttributesHandler.class);
- private boolean mapDeviceAttributes;
+ private final IdentityManager identityManager;
+ private final AttributesManager attributesManager;
- public ComputedAttributesHandler() {
+ private final JexlEngine engine;
+
+ private final boolean includeDeviceAttributes;
+
+ public ComputedAttributesHandler(
+ Config config, IdentityManager identityManager, AttributesManager attributesManager) {
+ this.identityManager = identityManager;
+ this.attributesManager = attributesManager;
engine = new JexlEngine();
engine.setStrict(true);
- engine.setFunctions(Collections.singletonMap("math", (Object) Math.class));
- if (Context.getConfig() != null) {
- mapDeviceAttributes = Context.getConfig().getBoolean("processing.computedAttributes.deviceAttributes");
- }
+ engine.setFunctions(Collections.singletonMap("math", Math.class));
+ includeDeviceAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES);
}
private MapContext prepareContext(Position position) {
MapContext result = new MapContext();
- if (mapDeviceAttributes) {
- Device device = Context.getIdentityManager().getById(position.getDeviceId());
+ if (includeDeviceAttributes) {
+ Device device = identityManager.getById(position.getDeviceId());
if (device != null) {
for (Object key : device.getAttributes().keySet()) {
result.set((String) key, device.getAttributes().get(key));
@@ -75,43 +87,49 @@ public class ComputedAttributesHandler extends BaseDataHandler {
}
}
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Attribute reflection error", error);
}
}
}
return result;
}
+ /**
+ * @deprecated logic needs to be extracted to be used in API resource
+ */
+ @Deprecated
public Object computeAttribute(Attribute attribute, Position position) throws JexlException {
return engine.createExpression(attribute.getExpression()).evaluate(prepareContext(position));
}
@Override
protected Position handlePosition(Position position) {
- Collection<Attribute> attributes = Context.getAttributesManager().getItems(
- Context.getAttributesManager().getAllDeviceItems(position.getDeviceId()));
+ Collection<Attribute> attributes = attributesManager.getItems(
+ attributesManager.getAllDeviceItems(position.getDeviceId()));
for (Attribute attribute : attributes) {
if (attribute.getAttribute() != null) {
Object result = null;
try {
result = computeAttribute(attribute, position);
} catch (JexlException error) {
- Log.warning(error);
+ LOGGER.warn("Attribute computation error", error);
}
if (result != null) {
try {
switch (attribute.getType()) {
case "number":
- position.getAttributes().put(attribute.getAttribute(), (Number) result);
+ Number numberValue = (Number) result;
+ position.getAttributes().put(attribute.getAttribute(), numberValue);
break;
case "boolean":
- position.getAttributes().put(attribute.getAttribute(), (Boolean) result);
+ Boolean booleanValue = (Boolean) result;
+ position.getAttributes().put(attribute.getAttribute(), booleanValue);
break;
default:
position.getAttributes().put(attribute.getAttribute(), result.toString());
}
} catch (ClassCastException error) {
- Log.warning(error);
+ LOGGER.warn("Attribute cast error", error);
}
}
}
diff --git a/src/org/traccar/processing/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
index 9fbcfa73f..3cd7d144d 100644
--- a/src/org/traccar/processing/CopyAttributesHandler.java
+++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,31 +14,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.processing;
+package org.traccar.handler;
+import io.netty.channel.ChannelHandler;
import org.traccar.BaseDataHandler;
-import org.traccar.Context;
+import org.traccar.database.IdentityManager;
import org.traccar.model.Position;
+@ChannelHandler.Sharable
public class CopyAttributesHandler extends BaseDataHandler {
- private Position getLastPosition(long deviceId) {
- if (Context.getIdentityManager() != null) {
- return Context.getIdentityManager().getLastPosition(deviceId);
- }
- return null;
+ private IdentityManager identityManager;
+
+ public CopyAttributesHandler(IdentityManager identityManager) {
+ this.identityManager = identityManager;
}
@Override
protected Position handlePosition(Position position) {
- String attributesString = Context.getDeviceManager().lookupAttributeString(
- position.getDeviceId(), "processing.copyAttributes", "", true);
- Position last = getLastPosition(position.getDeviceId());
+ String attributesString = identityManager.lookupAttributeString(
+ position.getDeviceId(), "processing.copyAttributes", "", false, true);
if (attributesString.isEmpty()) {
attributesString = Position.KEY_DRIVER_UNIQUE_ID;
} else {
attributesString += "," + Position.KEY_DRIVER_UNIQUE_ID;
}
+ Position last = identityManager.getLastPosition(position.getDeviceId());
if (last != null) {
for (String attribute : attributesString.split("[ ,]")) {
if (last.getAttributes().containsKey(attribute) && !position.getAttributes().containsKey(attribute)) {
diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DefaultDataHandler.java
new file mode 100644
index 000000000..9d8ea044d
--- /dev/null
+++ b/src/main/java/org/traccar/handler/DefaultDataHandler.java
@@ -0,0 +1,48 @@
+/*
+ * 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.handler;
+
+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;
+
+@ChannelHandler.Sharable
+public class DefaultDataHandler extends BaseDataHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDataHandler.class);
+
+ private final DataManager dataManager;
+
+ public DefaultDataHandler(DataManager dataManager) {
+ this.dataManager = dataManager;
+ }
+
+ @Override
+ protected Position handlePosition(Position position) {
+
+ try {
+ dataManager.addObject(position);
+ } catch (Exception error) {
+ LOGGER.warn("Failed to store position", error);
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java
index 295bc3b29..a336a884e 100644
--- a/src/org/traccar/DistanceHandler.java
+++ b/src/main/java/org/traccar/handler/DistanceHandler.java
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Amila Silva
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -14,31 +14,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
+import io.netty.channel.ChannelHandler;
+import 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 java.math.BigDecimal;
import java.math.RoundingMode;
+@ChannelHandler.Sharable
public class DistanceHandler extends BaseDataHandler {
+ private final IdentityManager identityManager;
+
private final boolean filter;
private final int coordinatesMinError;
private final int coordinatesMaxError;
- public DistanceHandler(boolean filter, int coordinatesMinError, int coordinatesMaxError) {
- this.filter = filter;
- this.coordinatesMinError = coordinatesMinError;
- this.coordinatesMaxError = coordinatesMaxError;
- }
-
- private Position getLastPosition(long deviceId) {
- if (Context.getIdentityManager() != null) {
- return Context.getIdentityManager().getLastPosition(deviceId);
- }
- return null;
+ public DistanceHandler(Config config, IdentityManager identityManager) {
+ this.identityManager = identityManager;
+ this.filter = config.getBoolean(Keys.COORDINATES_FILTER);
+ this.coordinatesMinError = config.getInteger(Keys.COORDINATES_MIN_ERROR);
+ this.coordinatesMaxError = config.getInteger(Keys.COORDINATES_MAX_ERROR);
}
@Override
@@ -50,7 +52,7 @@ public class DistanceHandler extends BaseDataHandler {
}
double totalDistance = 0.0;
- Position last = getLastPosition(position.getDeviceId());
+ Position last = identityManager != null ? identityManager.getLastPosition(position.getDeviceId()) : null;
if (last != null) {
totalDistance = last.getDouble(Position.KEY_TOTAL_DISTANCE);
if (!position.getAttributes().containsKey(Position.KEY_DISTANCE)) {
diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java
new file mode 100644
index 000000000..92da84e6b
--- /dev/null
+++ b/src/main/java/org/traccar/handler/EngineHoursHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.handler;
+
+import io.netty.channel.ChannelHandler;
+import org.traccar.BaseDataHandler;
+import org.traccar.database.IdentityManager;
+import org.traccar.model.Position;
+
+@ChannelHandler.Sharable
+public class EngineHoursHandler extends BaseDataHandler {
+
+ private final IdentityManager identityManager;
+
+ public EngineHoursHandler(IdentityManager identityManager) {
+ this.identityManager = identityManager;
+ }
+
+ @Override
+ protected Position handlePosition(Position position) {
+ if (!position.getAttributes().containsKey(Position.KEY_HOURS)) {
+ Position last = identityManager.getLastPosition(position.getDeviceId());
+ if (last != null) {
+ long hours = last.getLong(Position.KEY_HOURS);
+ if (last.getBoolean(Position.KEY_IGNITION) && position.getBoolean(Position.KEY_IGNITION)) {
+ hours += position.getFixTime().getTime() - last.getFixTime().getTime();
+ }
+ if (hours != 0) {
+ position.set(Position.KEY_HOURS, hours);
+ }
+ }
+ }
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java
index 4cd3eb0eb..7cd9153c1 100644
--- a/src/org/traccar/FilterHandler.java
+++ b/src/main/java/org/traccar/handler/FilterHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -13,79 +13,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
-
-import org.traccar.helper.Log;
+package org.traccar.handler;
+
+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.model.Position;
+@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 void setFilterInvalid(boolean filterInvalid) {
- this.filterInvalid = filterInvalid;
- }
-
- public void setFilterZero(boolean filterZero) {
- this.filterZero = filterZero;
- }
-
- public void setFilterDuplicate(boolean filterDuplicate) {
- this.filterDuplicate = filterDuplicate;
- }
-
- public void setFilterFuture(long filterFuture) {
- this.filterFuture = filterFuture;
- }
-
- public void setFilterApproximate(boolean filterApproximate) {
- this.filterApproximate = filterApproximate;
- }
-
- public void setFilterStatic(boolean filterStatic) {
- this.filterStatic = filterStatic;
- }
-
- public void setFilterDistance(int filterDistance) {
- this.filterDistance = filterDistance;
- }
-
- public void setFilterMaxSpeed(int filterMaxSpeed) {
- this.filterMaxSpeed = filterMaxSpeed;
- }
-
- public void setSkipLimit(long skipLimit) {
- this.skipLimit = skipLimit;
- }
-
- public void setSkipAttributes(boolean skipAttributes) {
- this.skipAttributes = skipAttributes;
- }
-
- public FilterHandler() {
- Config config = Context.getConfig();
- if (config != null) {
- filterInvalid = config.getBoolean("filter.invalid");
- filterZero = config.getBoolean("filter.zero");
- filterDuplicate = config.getBoolean("filter.duplicate");
- filterFuture = config.getLong("filter.future") * 1000;
- filterApproximate = config.getBoolean("filter.approximate");
- filterStatic = config.getBoolean("filter.static");
- filterDistance = config.getInteger("filter.distance");
- filterMaxSpeed = config.getInteger("filter.maxSpeed");
- skipLimit = config.getLong("filter.skipLimit") * 1000;
- skipAttributes = config.getBoolean("filter.skipAttributes.enable");
- }
+ public FilterHandler(Config config) {
+ 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;
+ 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;
+ skipLimit = config.getLong(Keys.FILTER_SKIP_LIMIT) * 1000;
+ skipAttributes = config.getBoolean(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE);
}
private boolean filterInvalid(Position position) {
@@ -114,6 +84,10 @@ public class FilterHandler extends BaseDataHandler {
return filterFuture != 0 && position.getFixTime().getTime() > System.currentTimeMillis() + filterFuture;
}
+ private boolean filterAccuracy(Position position) {
+ return filterAccuracy != 0 && position.getAccuracy() > filterAccuracy;
+ }
+
private boolean filterApproximate(Position position) {
return filterApproximate && position.getBoolean(Position.KEY_APPROXIMATE);
}
@@ -138,9 +112,17 @@ public class FilterHandler extends BaseDataHandler {
return false;
}
+ private boolean filterMinPeriod(Position position, Position last) {
+ if (filterMinPeriod != 0 && last != null) {
+ long time = position.getFixTime().getTime() - last.getFixTime().getTime();
+ return time > 0 && time < filterMinPeriod;
+ }
+ return false;
+ }
+
private boolean skipLimit(Position position, Position last) {
if (skipLimit != 0 && last != null) {
- return (position.getFixTime().getTime() - last.getFixTime().getTime()) > skipLimit;
+ return (position.getServerTime().getTime() - last.getServerTime().getTime()) > skipLimit;
}
return false;
}
@@ -148,7 +130,7 @@ public class FilterHandler extends BaseDataHandler {
private boolean skipAttributes(Position position) {
if (skipAttributes) {
String attributesString = Context.getIdentityManager().lookupAttributeString(
- position.getDeviceId(), "filter.skipAttributes", "", true);
+ position.getDeviceId(), "filter.skipAttributes", "", false, true);
for (String attribute : attributesString.split("[ ,]")) {
if (position.getAttributes().containsKey(attribute)) {
return true;
@@ -167,34 +149,36 @@ public class FilterHandler extends BaseDataHandler {
last = Context.getIdentityManager().getLastPosition(position.getDeviceId());
}
- if (skipLimit(position, last) || skipAttributes(position)) {
- return false;
- }
-
if (filterInvalid(position)) {
filterType.append("Invalid ");
}
if (filterZero(position)) {
filterType.append("Zero ");
}
- if (filterDuplicate(position, last)) {
+ if (filterDuplicate(position, last) && !skipLimit(position, last) && !skipAttributes(position)) {
filterType.append("Duplicate ");
}
if (filterFuture(position)) {
filterType.append("Future ");
}
+ if (filterAccuracy(position)) {
+ filterType.append("Accuracy ");
+ }
if (filterApproximate(position)) {
filterType.append("Approximate ");
}
- if (filterStatic(position)) {
+ if (filterStatic(position) && !skipLimit(position, last) && !skipAttributes(position)) {
filterType.append("Static ");
}
- if (filterDistance(position, last)) {
+ 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 ");
+ }
if (filterType.length() > 0) {
@@ -203,10 +187,8 @@ public class FilterHandler extends BaseDataHandler {
message.append(filterType.toString());
message.append("filters from device: ");
message.append(Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId());
- message.append(" with id: ");
- message.append(position.getDeviceId());
- Log.info(message.toString());
+ LOGGER.info(message.toString());
return true;
}
diff --git a/src/main/java/org/traccar/handler/GeocoderHandler.java b/src/main/java/org/traccar/handler/GeocoderHandler.java
new file mode 100644
index 000000000..b96f01b3a
--- /dev/null
+++ b/src/main/java/org/traccar/handler/GeocoderHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 - 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;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.IdentityManager;
+import org.traccar.database.StatisticsManager;
+import org.traccar.geocoder.Geocoder;
+import org.traccar.model.Position;
+
+@ChannelHandler.Sharable
+public class GeocoderHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GeocoderHandler.class);
+
+ private final Geocoder geocoder;
+ private final IdentityManager identityManager;
+ private final StatisticsManager statisticsManager;
+ private final boolean ignorePositions;
+ private final boolean processInvalidPositions;
+ private final int geocoderReuseDistance;
+
+ public GeocoderHandler(
+ Config config, Geocoder geocoder, IdentityManager identityManager, StatisticsManager statisticsManager) {
+ this.geocoder = geocoder;
+ this.identityManager = identityManager;
+ this.statisticsManager = statisticsManager;
+ ignorePositions = Context.getConfig().getBoolean(Keys.GEOCODER_IGNORE_POSITIONS);
+ processInvalidPositions = config.getBoolean(Keys.GEOCODER_PROCESS_INVALID_POSITIONS);
+ geocoderReuseDistance = config.getInteger(Keys.GEOCODER_REUSE_DISTANCE, 0);
+ }
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object message) {
+ if (message instanceof Position && !ignorePositions) {
+ final Position position = (Position) message;
+ if (processInvalidPositions || position.getValid()) {
+ if (geocoderReuseDistance != 0) {
+ Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ if (lastPosition != null && lastPosition.getAddress() != null
+ && position.getDouble(Position.KEY_DISTANCE) <= geocoderReuseDistance) {
+ position.setAddress(lastPosition.getAddress());
+ ctx.fireChannelRead(position);
+ return;
+ }
+ }
+
+ if (statisticsManager != null) {
+ statisticsManager.registerGeocoderRequest();
+ }
+
+ geocoder.getAddress(position.getLatitude(), position.getLongitude(),
+ new Geocoder.ReverseGeocoderCallback() {
+ @Override
+ public void onSuccess(String address) {
+ position.setAddress(address);
+ ctx.fireChannelRead(position);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ LOGGER.warn("Geocoding failed", e);
+ ctx.fireChannelRead(position);
+ }
+ });
+ } else {
+ ctx.fireChannelRead(position);
+ }
+ } else {
+ ctx.fireChannelRead(message);
+ }
+ }
+
+}
diff --git a/src/org/traccar/GeolocationHandler.java b/src/main/java/org/traccar/handler/GeolocationHandler.java
index 346ad5904..0e78322c8 100644
--- a/src/org/traccar/GeolocationHandler.java
+++ b/src/main/java/org/traccar/handler/GeolocationHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,41 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
-import org.jboss.netty.channel.ChannelEvent;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelUpstreamHandler;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.MessageEvent;
-import org.traccar.helper.Log;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.StatisticsManager;
import org.traccar.geolocation.GeolocationProvider;
import org.traccar.model.Position;
-public class GeolocationHandler implements ChannelUpstreamHandler {
+@ChannelHandler.Sharable
+public class GeolocationHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GeolocationHandler.class);
private final GeolocationProvider geolocationProvider;
+ private final StatisticsManager statisticsManager;
private final boolean processInvalidPositions;
- public GeolocationHandler(GeolocationProvider geolocationProvider, boolean processInvalidPositions) {
+ public GeolocationHandler(
+ Config config, GeolocationProvider geolocationProvider, StatisticsManager statisticsManager) {
this.geolocationProvider = geolocationProvider;
- this.processInvalidPositions = processInvalidPositions;
+ this.statisticsManager = statisticsManager;
+ this.processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS);
}
@Override
- public void handleUpstream(final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
- if (!(evt instanceof MessageEvent)) {
- ctx.sendUpstream(evt);
- return;
- }
-
- final MessageEvent event = (MessageEvent) evt;
- Object message = event.getMessage();
+ public void channelRead(final ChannelHandlerContext ctx, Object message) {
if (message instanceof Position) {
final Position position = (Position) message;
if ((position.getOutdated() || processInvalidPositions && !position.getValid())
&& position.getNetwork() != null) {
- Context.getStatisticsManager().registerGeolocationRequest();
+ if (statisticsManager != null) {
+ statisticsManager.registerGeolocationRequest();
+ }
geolocationProvider.getLocation(position.getNetwork(),
new GeolocationProvider.LocationProviderCallback() {
@@ -62,21 +65,20 @@ public class GeolocationHandler implements ChannelUpstreamHandler {
position.setAltitude(0);
position.setSpeed(0);
position.setCourse(0);
- position.set(Position.KEY_RSSI, 0);
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
+ ctx.fireChannelRead(position);
}
@Override
public void onFailure(Throwable e) {
- Log.warning(e);
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
+ LOGGER.warn("Geolocation network error", e);
+ ctx.fireChannelRead(position);
}
});
} else {
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
+ ctx.fireChannelRead(position);
}
} else {
- Channels.fireMessageReceived(ctx, message, event.getRemoteAddress());
+ ctx.fireChannelRead(message);
}
}
diff --git a/src/org/traccar/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java
index b1e7d3fb2..aff3d8a64 100644
--- a/src/org/traccar/HemisphereHandler.java
+++ b/src/main/java/org/traccar/handler/HemisphereHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -13,20 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import org.traccar.BaseDataHandler;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
import org.traccar.model.Position;
-import java.net.SocketAddress;
-
-public class HemisphereHandler extends ExtendedObjectDecoder {
+@ChannelHandler.Sharable
+public class HemisphereHandler extends BaseDataHandler {
private int latitudeFactor;
private int longitudeFactor;
- public HemisphereHandler() {
- String latitudeHemisphere = Context.getConfig().getString("location.latitudeHemisphere");
+ public HemisphereHandler(Config config) {
+ String latitudeHemisphere = config.getString(Keys.LOCATION_LATITUDE_HEMISPHERE);
if (latitudeHemisphere != null) {
if (latitudeHemisphere.equalsIgnoreCase("N")) {
latitudeFactor = 1;
@@ -34,7 +36,7 @@ public class HemisphereHandler extends ExtendedObjectDecoder {
latitudeFactor = -1;
}
}
- String longitudeHemisphere = Context.getConfig().getString("location.longitudeHemisphere");
+ String longitudeHemisphere = config.getString(Keys.LOCATION_LATITUDE_HEMISPHERE);
if (longitudeHemisphere != null) {
if (longitudeHemisphere.equalsIgnoreCase("E")) {
longitudeFactor = 1;
@@ -45,20 +47,14 @@ public class HemisphereHandler extends ExtendedObjectDecoder {
}
@Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- if (msg instanceof Position) {
- Position position = (Position) msg;
- if (latitudeFactor != 0) {
- position.setLatitude(Math.abs(position.getLatitude()) * latitudeFactor);
- }
- if (longitudeFactor != 0) {
- position.setLongitude(Math.abs(position.getLongitude()) * longitudeFactor);
- }
+ protected Position handlePosition(Position position) {
+ if (latitudeFactor != 0) {
+ position.setLatitude(Math.abs(position.getLatitude()) * latitudeFactor);
}
-
- return msg;
+ if (longitudeFactor != 0) {
+ position.setLongitude(Math.abs(position.getLongitude()) * longitudeFactor);
+ }
+ return position;
}
}
diff --git a/src/org/traccar/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java
index 901965dd5..e8051dd75 100644
--- a/src/org/traccar/MotionHandler.java
+++ b/src/main/java/org/traccar/handler/MotionHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,10 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
+import io.netty.channel.ChannelHandler;
+import org.traccar.BaseDataHandler;
import org.traccar.model.Position;
+@ChannelHandler.Sharable
public class MotionHandler extends BaseDataHandler {
private double speedThreshold;
diff --git a/src/main/java/org/traccar/handler/NetworkMessageHandler.java b/src/main/java/org/traccar/handler/NetworkMessageHandler.java
new file mode 100644
index 000000000..b1d926bfa
--- /dev/null
+++ b/src/main/java/org/traccar/handler/NetworkMessageHandler.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.DatagramPacket;
+import org.traccar.NetworkMessage;
+
+import java.net.InetSocketAddress;
+
+public class NetworkMessageHandler extends ChannelDuplexHandler {
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ if (ctx.channel() instanceof DatagramChannel) {
+ DatagramPacket packet = (DatagramPacket) msg;
+ ctx.fireChannelRead(new NetworkMessage(packet.content(), packet.sender()));
+ } else if (msg instanceof ByteBuf) {
+ ByteBuf buffer = (ByteBuf) msg;
+ ctx.fireChannelRead(new NetworkMessage(buffer, ctx.channel().remoteAddress()));
+ }
+ }
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
+ if (msg instanceof NetworkMessage) {
+ NetworkMessage message = (NetworkMessage) msg;
+ if (ctx.channel() instanceof DatagramChannel) {
+ InetSocketAddress recipient = (InetSocketAddress) message.getRemoteAddress();
+ InetSocketAddress sender = (InetSocketAddress) ctx.channel().localAddress();
+ ctx.write(new DatagramPacket((ByteBuf) message.getMessage(), recipient, sender), promise);
+ } else {
+ ctx.write(message.getMessage(), promise);
+ }
+ } else {
+ ctx.write(msg, promise);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/OpenChannelHandler.java b/src/main/java/org/traccar/handler/OpenChannelHandler.java
new file mode 100644
index 000000000..d09d617ab
--- /dev/null
+++ b/src/main/java/org/traccar/handler/OpenChannelHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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;
+
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.TrackerServer;
+
+public class OpenChannelHandler extends ChannelDuplexHandler {
+
+ private final TrackerServer server;
+
+ public OpenChannelHandler(TrackerServer server) {
+ this.server = server;
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ server.getChannelGroup().add(ctx.channel());
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+ server.getChannelGroup().remove(ctx.channel());
+ }
+
+}
diff --git a/src/org/traccar/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/RemoteAddressHandler.java
index 188bbcab9..c09b8c39a 100644
--- a/src/org/traccar/RemoteAddressHandler.java
+++ b/src/main/java/org/traccar/handler/RemoteAddressHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,28 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
import org.traccar.model.Position;
import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-public class RemoteAddressHandler extends ExtendedObjectDecoder {
+@ChannelHandler.Sharable
+public class RemoteAddressHandler extends ChannelInboundHandlerAdapter {
@Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
- String hostAddress = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
+ 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);
}
- return msg;
+ ctx.fireChannelRead(msg);
}
}
diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
new file mode 100644
index 000000000..13c5f8281
--- /dev/null
+++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.NetworkMessage;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+public class StandardLoggingHandler extends ChannelDuplexHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StandardLoggingHandler.class);
+
+ private final String protocol;
+
+ public StandardLoggingHandler(String protocol) {
+ this.protocol = protocol;
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ log(ctx, false, msg);
+ super.channelRead(ctx, msg);
+ }
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
+ log(ctx, true, msg);
+ super.write(ctx, msg, promise);
+ }
+
+ public void log(ChannelHandlerContext ctx, boolean downstream, Object o) {
+ if (o instanceof NetworkMessage) {
+ NetworkMessage networkMessage = (NetworkMessage) o;
+ if (networkMessage.getMessage() instanceof ByteBuf) {
+ log(ctx, downstream, networkMessage.getRemoteAddress(), (ByteBuf) networkMessage.getMessage());
+ }
+ } else if (o instanceof ByteBuf) {
+ log(ctx, downstream, ctx.channel().remoteAddress(), (ByteBuf) o);
+ }
+ }
+
+ 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(protocol);
+ if (downstream) {
+ message.append(" > ");
+ } else {
+ message.append(" < ");
+ }
+
+ if (remoteAddress instanceof InetSocketAddress) {
+ message.append(((InetSocketAddress) remoteAddress).getHostString());
+ } else {
+ message.append("unknown");
+ }
+ 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
new file mode 100644
index 000000000..d8039518c
--- /dev/null
+++ b/src/main/java/org/traccar/handler/TimeHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 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;
+
+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 java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+@ChannelHandler.Sharable
+public class TimeHandler extends ChannelInboundHandlerAdapter {
+
+ private final boolean useServerTime;
+ private final Set<String> protocols;
+
+ public TimeHandler(Config config) {
+ useServerTime = config.getString(Keys.TIME_OVERRIDE).equalsIgnoreCase("serverTime");
+ String protocolList = Context.getConfig().getString(Keys.TIME_PROTOCOLS);
+ if (protocolList != null) {
+ protocols = new HashSet<>(Arrays.asList(protocolList.split("[, ]")));
+ } else {
+ protocols = null;
+ }
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+
+ if (msg instanceof Position && (protocols == null
+ || protocols.contains(ctx.pipeline().get(BaseProtocolDecoder.class).getProtocolName()))) {
+
+ Position position = (Position) msg;
+ if (useServerTime) {
+ position.setDeviceTime(position.getServerTime());
+ }
+ position.setFixTime(position.getDeviceTime());
+
+ }
+ ctx.fireChannelRead(msg);
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
new file mode 100644
index 000000000..0b7c8d23e
--- /dev/null
+++ b/src/main/java/org/traccar/handler/events/AlertEventHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 - 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 java.util.Collections;
+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;
+
+@ChannelHandler.Sharable
+public class AlertEventHandler extends BaseEventHandler {
+
+ private final IdentityManager identityManager;
+ private final boolean ignoreDuplicateAlerts;
+
+ public AlertEventHandler(Config config, IdentityManager identityManager) {
+ this.identityManager = identityManager;
+ ignoreDuplicateAlerts = config.getBoolean(Keys.EVENT_IGNORE_DUPLICATE_ALERTS);
+ }
+
+ @Override
+ protected Map<Event, Position> analyzePosition(Position position) {
+ Object alarm = position.getAttributes().get(Position.KEY_ALARM);
+ if (alarm != null) {
+ boolean ignoreAlert = false;
+ if (ignoreDuplicateAlerts) {
+ Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ if (lastPosition != null && alarm.equals(lastPosition.getAttributes().get(Position.KEY_ALARM))) {
+ ignoreAlert = true;
+ }
+ }
+ if (!ignoreAlert) {
+ Event event = new Event(Event.TYPE_ALARM, position.getDeviceId(), position.getId());
+ event.set(Position.KEY_ALARM, (String) alarm);
+ return Collections.singletonMap(event, position);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
index b6f7e2085..41f677f6c 100644
--- a/src/org/traccar/BaseEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/BaseEventHandler.java
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.handler.events;
import java.util.Map;
+import org.traccar.BaseDataHandler;
+import org.traccar.Context;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -24,7 +26,6 @@ public abstract class BaseEventHandler extends BaseDataHandler {
@Override
protected Position handlePosition(Position position) {
-
Map<Event, Position> events = analyzePosition(position);
if (events != null && Context.getNotificationManager() != null) {
Context.getNotificationManager().updateEvents(events);
diff --git a/src/org/traccar/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
index 775aa903f..cfe676653 100644
--- a/src/org/traccar/events/CommandResultEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java
@@ -13,15 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
import java.util.Collections;
import java.util.Map;
-import org.traccar.BaseEventHandler;
+import io.netty.channel.ChannelHandler;
import org.traccar.model.Event;
import org.traccar.model.Position;
+@ChannelHandler.Sharable
public class CommandResultEventHandler extends BaseEventHandler {
@Override
diff --git a/src/org/traccar/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
index 39b8eb9c0..994df93fa 100644
--- a/src/org/traccar/events/DriverEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/DriverEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,27 +14,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
import java.util.Collections;
import java.util.Map;
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
+import io.netty.channel.ChannelHandler;
+import org.traccar.database.IdentityManager;
import org.traccar.model.Event;
import org.traccar.model.Position;
+@ChannelHandler.Sharable
public class DriverEventHandler extends BaseEventHandler {
+ private final IdentityManager identityManager;
+
+ public DriverEventHandler(IdentityManager identityManager) {
+ this.identityManager = identityManager;
+ }
+
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- if (!Context.getIdentityManager().isLatestPosition(position)) {
+ if (!identityManager.isLatestPosition(position)) {
return null;
}
String driverUniqueId = position.getString(Position.KEY_DRIVER_UNIQUE_ID);
if (driverUniqueId != null) {
String oldDriverUniqueId = null;
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
+ Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
if (lastPosition != null) {
oldDriverUniqueId = lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
}
diff --git a/src/org/traccar/events/FuelDropEventHandler.java b/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java
index 2ee3e1a58..bc1426b86 100644
--- a/src/org/traccar/events/FuelDropEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/FuelDropEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
+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;
@@ -24,26 +24,33 @@ 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 = Context.getIdentityManager().getById(position.getDeviceId());
+ Device device = identityManager.getById(position.getDeviceId());
if (device == null) {
return null;
}
- if (!Context.getIdentityManager().isLatestPosition(position)) {
+ if (!identityManager.isLatestPosition(position)) {
return null;
}
- double fuelDropThreshold = Context.getDeviceManager()
- .lookupAttributeDouble(device.getId(), ATTRIBUTE_FUEL_DROP_THRESHOLD, 0, false);
+ double fuelDropThreshold = identityManager
+ .lookupAttributeDouble(device.getId(), ATTRIBUTE_FUEL_DROP_THRESHOLD, 0, true, false);
if (fuelDropThreshold > 0) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
+ Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
if (position.getAttributes().containsKey(Position.KEY_FUEL_LEVEL)
&& lastPosition != null && lastPosition.getAttributes().containsKey(Position.KEY_FUEL_LEVEL)) {
diff --git a/src/org/traccar/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
index 31d82a81e..067c97957 100644
--- a/src/org/traccar/events/GeofenceEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -13,36 +13,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
+import io.netty.channel.ChannelHandler;
+import org.traccar.database.CalendarManager;
import org.traccar.database.GeofenceManager;
+import org.traccar.database.IdentityManager;
import org.traccar.model.Calendar;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
+@ChannelHandler.Sharable
public class GeofenceEventHandler extends BaseEventHandler {
- private GeofenceManager geofenceManager;
+ private final IdentityManager identityManager;
+ private final GeofenceManager geofenceManager;
+ private final CalendarManager calendarManager;
- public GeofenceEventHandler() {
- geofenceManager = Context.getGeofenceManager();
+ public GeofenceEventHandler(
+ IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager) {
+ this.identityManager = identityManager;
+ this.geofenceManager = geofenceManager;
+ this.calendarManager = calendarManager;
}
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getById(position.getDeviceId());
+ Device device = identityManager.getById(position.getDeviceId());
if (device == null) {
return null;
}
- if (!Context.getIdentityManager().isLatestPosition(position) || !position.getValid()) {
+ if (!identityManager.isLatestPosition(position) || !position.getValid()) {
return null;
}
@@ -58,24 +65,25 @@ public class GeofenceEventHandler extends BaseEventHandler {
device.setGeofenceIds(currentGeofences);
Map<Event, Position> events = new HashMap<>();
- for (long geofenceId : newGeofences) {
+ for (long geofenceId : oldGeofences) {
long calendarId = geofenceManager.getById(geofenceId).getCalendarId();
- Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null;
+ Calendar calendar = calendarId != 0 ? calendarManager.getById(calendarId) : null;
if (calendar == null || calendar.checkMoment(position.getFixTime())) {
- Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position.getDeviceId(), position.getId());
+ Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position.getDeviceId(), position.getId());
event.setGeofenceId(geofenceId);
events.put(event, position);
}
}
- for (long geofenceId : oldGeofences) {
+ for (long geofenceId : newGeofences) {
long calendarId = geofenceManager.getById(geofenceId).getCalendarId();
- Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null;
+ Calendar calendar = calendarId != 0 ? calendarManager.getById(calendarId) : null;
if (calendar == null || calendar.checkMoment(position.getFixTime())) {
- Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position.getDeviceId(), position.getId());
+ Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position.getDeviceId(), position.getId());
event.setGeofenceId(geofenceId);
events.put(event, position);
}
}
return events;
}
+
}
diff --git a/src/org/traccar/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
index cc53b216c..ec133bafc 100644
--- a/src/org/traccar/events/IgnitionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,23 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
import java.util.Collections;
import java.util.Map;
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
+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;
+@ChannelHandler.Sharable
public class IgnitionEventHandler extends BaseEventHandler {
+ private final IdentityManager identityManager;
+
+ public IgnitionEventHandler(IdentityManager identityManager) {
+ this.identityManager = identityManager;
+ }
+
@Override
protected Map<Event, Position> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getById(position.getDeviceId());
- if (device == null || !Context.getIdentityManager().isLatestPosition(position)) {
+ Device device = identityManager.getById(position.getDeviceId());
+ if (device == null || !identityManager.isLatestPosition(position)) {
return null;
}
@@ -39,7 +46,7 @@ public class IgnitionEventHandler extends BaseEventHandler {
if (position.getAttributes().containsKey(Position.KEY_IGNITION)) {
boolean ignition = position.getBoolean(Position.KEY_IGNITION);
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
+ Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
if (lastPosition != null && lastPosition.getAttributes().containsKey(Position.KEY_IGNITION)) {
boolean oldIgnition = lastPosition.getBoolean(Position.KEY_IGNITION);
diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
new file mode 100644
index 000000000..93ae74142
--- /dev/null
+++ b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java
@@ -0,0 +1,72 @@
+/*
+ * 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.handler.events;
+
+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;
+
+@ChannelHandler.Sharable
+public class MaintenanceEventHandler extends BaseEventHandler {
+
+ private final IdentityManager identityManager;
+ private final MaintenancesManager maintenancesManager;
+
+ public MaintenanceEventHandler(IdentityManager identityManager, MaintenancesManager maintenancesManager) {
+ this.identityManager = identityManager;
+ this.maintenancesManager = maintenancesManager;
+ }
+
+ @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) {
+ return null;
+ }
+
+ Map<Event, Position> events = new HashMap<>();
+ for (long maintenanceId : maintenancesManager.getAllDeviceItems(position.getDeviceId())) {
+ Maintenance maintenance = maintenancesManager.getById(maintenanceId);
+ 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())
+ < (long) ((newValue - maintenance.getStart()) / maintenance.getPeriod())) {
+ Event event = new Event(Event.TYPE_MAINTENANCE, position.getDeviceId(), position.getId());
+ event.setMaintenanceId(maintenanceId);
+ event.set(maintenance.getType(), newValue);
+ events.put(event, position);
+ }
+ }
+ }
+
+ return events;
+ }
+
+}
diff --git a/src/org/traccar/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
index 0c1c4848f..9ec02ccfb 100644
--- a/src/org/traccar/events/MotionEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/MotionEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,13 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
import java.util.Collections;
import java.util.Map;
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
+import io.netty.channel.ChannelHandler;
+import org.traccar.database.DeviceManager;
+import org.traccar.database.IdentityManager;
import org.traccar.model.Device;
import org.traccar.model.DeviceState;
import org.traccar.model.Event;
@@ -28,11 +29,16 @@ import org.traccar.model.Position;
import org.traccar.reports.ReportUtils;
import org.traccar.reports.model.TripsConfig;
+@ChannelHandler.Sharable
public class MotionEventHandler extends BaseEventHandler {
- private TripsConfig tripsConfig;
+ private final IdentityManager identityManager;
+ private final DeviceManager deviceManager;
+ private final TripsConfig tripsConfig;
- public MotionEventHandler(TripsConfig tripsConfig) {
+ public MotionEventHandler(IdentityManager identityManager, DeviceManager deviceManager, TripsConfig tripsConfig) {
+ this.identityManager = identityManager;
+ this.deviceManager = deviceManager;
this.tripsConfig = tripsConfig;
}
@@ -105,24 +111,24 @@ public class MotionEventHandler extends BaseEventHandler {
protected Map<Event, Position> analyzePosition(Position position) {
long deviceId = position.getDeviceId();
- Device device = Context.getIdentityManager().getById(deviceId);
+ Device device = identityManager.getById(deviceId);
if (device == null) {
return null;
}
- if (!Context.getIdentityManager().isLatestPosition(position)
+ if (!identityManager.isLatestPosition(position)
|| !tripsConfig.getProcessInvalidPositions() && !position.getValid()) {
return null;
}
Map<Event, Position> result = null;
- DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+ DeviceState deviceState = deviceManager.getDeviceState(deviceId);
if (deviceState.getMotionState() == null) {
deviceState.setMotionState(position.getBoolean(Position.KEY_MOTION));
} else {
result = updateMotionState(deviceState, position);
}
- Context.getDeviceManager().setDeviceState(deviceId, deviceState);
+ deviceManager.setDeviceState(deviceId, deviceState);
return result;
}
diff --git a/src/org/traccar/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
index cb658415c..e534df9de 100644
--- a/src/org/traccar/events/OverspeedEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,37 +14,52 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.handler.events;
import java.util.Collections;
import java.util.Map;
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
+import io.netty.channel.ChannelHandler;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.DeviceManager;
+import org.traccar.database.GeofenceManager;
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;
+@ChannelHandler.Sharable
public class OverspeedEventHandler extends BaseEventHandler {
+ public static final String ATTRIBUTE_SPEED = "speed";
public static final String ATTRIBUTE_SPEED_LIMIT = "speedLimit";
- private boolean notRepeat;
- private long minimalDuration;
+ private final DeviceManager deviceManager;
+ private final GeofenceManager geofenceManager;
- public OverspeedEventHandler(long minimalDuration, boolean notRepeat) {
- this.notRepeat = notRepeat;
- this.minimalDuration = minimalDuration;
+ 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);
+ 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.getDeviceId(), position.getId());
- event.set("speed", deviceState.getOverspeedPosition().getSpeed());
+ 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);
}
@@ -61,7 +77,8 @@ public class OverspeedEventHandler extends BaseEventHandler {
return result;
}
- public Map<Event, Position> updateOverspeedState(DeviceState deviceState, Position position, double speedLimit) {
+ public Map<Event, Position> updateOverspeedState(
+ DeviceState deviceState, Position position, double speedLimit, long geofenceId) {
Map<Event, Position> result = null;
Boolean oldOverspeed = deviceState.getOverspeedState();
@@ -71,12 +88,15 @@ public class OverspeedEventHandler extends BaseEventHandler {
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) {
@@ -92,29 +112,52 @@ public class OverspeedEventHandler extends BaseEventHandler {
protected Map<Event, Position> analyzePosition(Position position) {
long deviceId = position.getDeviceId();
- Device device = Context.getIdentityManager().getById(deviceId);
+ Device device = deviceManager.getById(deviceId);
if (device == null) {
return null;
}
- if (!Context.getIdentityManager().isLatestPosition(position) || !position.getValid()) {
+ if (!deviceManager.isLatestPosition(position) || !position.getValid()) {
return null;
}
- double speedLimit = Context.getDeviceManager().lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, false);
+ double speedLimit = deviceManager.lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, true, false);
+
+ double geofenceSpeedLimit = 0;
+ long overspeedGeofenceId = 0;
+
+ if (geofenceManager != null && device.getGeofenceIds() != null) {
+ for (long geofenceId : device.getGeofenceIds()) {
+ Geofence geofence = geofenceManager.getById(geofenceId);
+ if (geofence != null) {
+ double currentSpeedLimit = geofence.getDouble(ATTRIBUTE_SPEED_LIMIT);
+ if (currentSpeedLimit > 0 && geofenceSpeedLimit == 0
+ || preferLowest && currentSpeedLimit < geofenceSpeedLimit
+ || !preferLowest && currentSpeedLimit > geofenceSpeedLimit) {
+ geofenceSpeedLimit = currentSpeedLimit;
+ overspeedGeofenceId = geofenceId;
+ }
+ }
+ }
+ }
+ if (geofenceSpeedLimit > 0) {
+ speedLimit = geofenceSpeedLimit;
+ }
+
if (speedLimit == 0) {
return null;
}
Map<Event, Position> result = null;
- DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+ 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);
+ result = updateOverspeedState(deviceState, position, speedLimit, overspeedGeofenceId);
}
- Context.getDeviceManager().setDeviceState(deviceId, deviceState);
+ deviceManager.setDeviceState(deviceId, deviceState);
return result;
}
diff --git a/src/org/traccar/protocol/Arnavi4FrameDecoder.java b/src/main/java/org/traccar/handler/protocol/Arnavi4FrameDecoder.java
index b13f3fd7d..b13f3fd7d 100644
--- a/src/org/traccar/protocol/Arnavi4FrameDecoder.java
+++ b/src/main/java/org/traccar/handler/protocol/Arnavi4FrameDecoder.java
diff --git a/src/org/traccar/protocol/Arnavi4Protocol.java b/src/main/java/org/traccar/handler/protocol/Arnavi4Protocol.java
index 381a9b457..381a9b457 100644
--- a/src/org/traccar/protocol/Arnavi4Protocol.java
+++ b/src/main/java/org/traccar/handler/protocol/Arnavi4Protocol.java
diff --git a/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java b/src/main/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoder.java
index 06abb563f..06abb563f 100644
--- a/src/org/traccar/protocol/Arnavi4ProtocolDecoder.java
+++ b/src/main/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoder.java
diff --git a/src/org/traccar/helper/BcdUtil.java b/src/main/java/org/traccar/helper/BcdUtil.java
index 495f94104..c87529e32 100644
--- a/src/org/traccar/helper/BcdUtil.java
+++ b/src/main/java/org/traccar/helper/BcdUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,14 +15,14 @@
*/
package org.traccar.helper;
-import org.jboss.netty.buffer.ChannelBuffer;
+import io.netty.buffer.ByteBuf;
public final class BcdUtil {
private BcdUtil() {
}
- public static int readInteger(ChannelBuffer buf, int digits) {
+ public static int readInteger(ByteBuf buf, int digits) {
int result = 0;
for (int i = 0; i < digits / 2; i++) {
@@ -42,7 +42,7 @@ public final class BcdUtil {
return result;
}
- public static double readCoordinate(ChannelBuffer buf) {
+ public static double readCoordinate(ByteBuf buf) {
int b1 = buf.readUnsignedByte();
int b2 = buf.readUnsignedByte();
int b3 = buf.readUnsignedByte();
diff --git a/src/org/traccar/helper/BitBuffer.java b/src/main/java/org/traccar/helper/BitBuffer.java
index ac307efdf..f30a4557b 100644
--- a/src/org/traccar/helper/BitBuffer.java
+++ b/src/main/java/org/traccar/helper/BitBuffer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,12 +15,12 @@
*/
package org.traccar.helper;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
public class BitBuffer {
- private final ChannelBuffer buffer;
+ private final ByteBuf buffer;
private int writeByte;
private int writeCount;
@@ -29,10 +29,10 @@ public class BitBuffer {
private int readCount;
public BitBuffer() {
- buffer = ChannelBuffers.dynamicBuffer();
+ buffer = Unpooled.buffer();
}
- public BitBuffer(ChannelBuffer buffer) {
+ public BitBuffer(ByteBuf buffer) {
this.buffer = buffer;
}
diff --git a/src/org/traccar/helper/BitUtil.java b/src/main/java/org/traccar/helper/BitUtil.java
index b6108edff..b6108edff 100644
--- a/src/org/traccar/helper/BitUtil.java
+++ b/src/main/java/org/traccar/helper/BitUtil.java
diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java
new file mode 100644
index 000000000..15c619ec5
--- /dev/null
+++ b/src/main/java/org/traccar/helper/BufferUtil.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper;
+
+import java.nio.charset.StandardCharsets;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+
+public final class BufferUtil {
+
+ private BufferUtil() {
+ }
+
+ public static int indexOf(String needle, ByteBuf haystack) {
+ ByteBuf needleBuffer = Unpooled.wrappedBuffer(needle.getBytes(StandardCharsets.US_ASCII));
+ try {
+ return ByteBufUtil.indexOf(needleBuffer, haystack);
+ } finally {
+ needleBuffer.release();
+ }
+ }
+
+ public static int indexOf(String needle, ByteBuf haystack, int startIndex, int endIndex) {
+ ByteBuf wrappedHaystack = Unpooled.wrappedBuffer(haystack);
+ wrappedHaystack.readerIndex(startIndex - haystack.readerIndex());
+ wrappedHaystack.writerIndex(endIndex - haystack.readerIndex());
+ int result = indexOf(needle, wrappedHaystack);
+ return result < 0 ? result : haystack.readerIndex() + result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java
new file mode 100644
index 000000000..adfa697c5
--- /dev/null
+++ b/src/main/java/org/traccar/helper/Checksum.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.zip.CRC32;
+
+public final class Checksum {
+
+ private Checksum() {
+ }
+
+ public static class Algorithm {
+
+ private int poly;
+ private int init;
+ private boolean refIn;
+ private boolean refOut;
+ private int xorOut;
+ private int[] table;
+
+ public Algorithm(int bits, int poly, int init, boolean refIn, boolean refOut, int xorOut) {
+ this.poly = poly;
+ this.init = init;
+ this.refIn = refIn;
+ this.refOut = refOut;
+ this.xorOut = xorOut;
+ this.table = bits == 8 ? initTable8() : initTable16();
+ }
+
+ private int[] initTable8() {
+ int[] table = new int[256];
+ int crc;
+ for (int i = 0; i < 256; i++) {
+ crc = i;
+ for (int j = 0; j < 8; j++) {
+ boolean bit = (crc & 0x80) != 0;
+ crc <<= 1;
+ if (bit) {
+ crc ^= poly;
+ }
+ }
+ table[i] = crc & 0xFF;
+ }
+ return table;
+ }
+
+ private int[] initTable16() {
+ int[] table = new int[256];
+ int crc;
+ for (int i = 0; i < 256; i++) {
+ crc = i << 8;
+ for (int j = 0; j < 8; j++) {
+ boolean bit = (crc & 0x8000) != 0;
+ crc <<= 1;
+ if (bit) {
+ crc ^= poly;
+ }
+ }
+ table[i] = crc & 0xFFFF;
+ }
+ return table;
+ }
+
+ }
+
+ private static int reverse(int value, int bits) {
+ int result = 0;
+ for (int i = 0; i < bits; i++) {
+ result = (result << 1) | (value & 1);
+ value >>= 1;
+ }
+ return result;
+ }
+
+ public static int crc8(Algorithm algorithm, ByteBuffer buf) {
+ int crc = algorithm.init;
+ while (buf.hasRemaining()) {
+ int b = buf.get() & 0xFF;
+ if (algorithm.refIn) {
+ b = reverse(b, 8);
+ }
+ crc = algorithm.table[(crc & 0xFF) ^ b];
+ }
+ if (algorithm.refOut) {
+ crc = reverse(crc, 8);
+ }
+ return (crc ^ algorithm.xorOut) & 0xFF;
+ }
+
+ public static int crc16(Algorithm algorithm, ByteBuffer buf) {
+ int crc = algorithm.init;
+ while (buf.hasRemaining()) {
+ int b = buf.get() & 0xFF;
+ if (algorithm.refIn) {
+ b = reverse(b, 8);
+ }
+ crc = (crc << 8) ^ algorithm.table[((crc >> 8) & 0xFF) ^ b];
+ }
+ if (algorithm.refOut) {
+ crc = reverse(crc, 16);
+ }
+ return (crc ^ algorithm.xorOut) & 0xFFFF;
+ }
+
+ public static final Algorithm CRC8_EGTS = new Algorithm(8, 0x31, 0xFF, false, false, 0x00);
+ public static final Algorithm CRC8_ROHC = new Algorithm(8, 0x07, 0xFF, true, true, 0x00);
+
+ public static final Algorithm CRC16_IBM = new Algorithm(16, 0x8005, 0x0000, true, true, 0x0000);
+ public static final Algorithm CRC16_X25 = new Algorithm(16, 0x1021, 0xFFFF, true, true, 0xFFFF);
+ public static final Algorithm CRC16_MODBUS = new Algorithm(16, 0x8005, 0xFFFF, true, true, 0x0000);
+ public static final Algorithm CRC16_CCITT_FALSE = new Algorithm(16, 0x1021, 0xFFFF, false, false, 0x0000);
+ public static final Algorithm CRC16_KERMIT = new Algorithm(16, 0x1021, 0x0000, true, true, 0x0000);
+ public static final Algorithm CRC16_XMODEM = new Algorithm(16, 0x1021, 0x0000, false, false, 0x0000);
+
+ public static int crc32(ByteBuffer buf) {
+ CRC32 checksum = new CRC32();
+ while (buf.hasRemaining()) {
+ checksum.update(buf.get());
+ }
+ return (int) checksum.getValue();
+ }
+
+ public static int xor(ByteBuffer buf) {
+ int checksum = 0;
+ while (buf.hasRemaining()) {
+ checksum ^= buf.get();
+ }
+ return checksum;
+ }
+
+ public static int xor(String string) {
+ byte checksum = 0;
+ for (byte b : string.getBytes(StandardCharsets.US_ASCII)) {
+ checksum ^= b;
+ }
+ return checksum;
+ }
+
+ public static String nmea(String msg) {
+ int checksum = 0;
+ byte[] bytes = msg.getBytes(StandardCharsets.US_ASCII);
+ for (int i = 1; i < bytes.length; i++) {
+ checksum ^= bytes[i];
+ }
+ return String.format("*%02x", checksum).toUpperCase();
+ }
+
+ public static int sum(ByteBuffer buf) {
+ byte checksum = 0;
+ while (buf.hasRemaining()) {
+ checksum += buf.get();
+ }
+ return checksum;
+ }
+
+ public static String sum(String msg) {
+ byte checksum = 0;
+ for (byte b : msg.getBytes(StandardCharsets.US_ASCII)) {
+ checksum += b;
+ }
+ return String.format("%02X", checksum).toUpperCase();
+ }
+
+ public static long luhn(long imei) {
+ long checksum = 0;
+ long remain = imei;
+
+ for (int i = 0; remain != 0; i++) {
+ long digit = remain % 10;
+
+ if (i % 2 == 0) {
+ digit *= 2;
+ if (digit >= 10) {
+ digit = 1 + (digit % 10);
+ }
+ }
+
+ checksum += digit;
+ remain /= 10;
+ }
+
+ return (10 - (checksum % 10)) % 10;
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/DataConverter.java b/src/main/java/org/traccar/helper/DataConverter.java
new file mode 100644
index 000000000..7abd4ae93
--- /dev/null
+++ b/src/main/java/org/traccar/helper/DataConverter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+
+public final class DataConverter {
+
+ private DataConverter() {
+ }
+
+ public static byte[] parseHex(String string) {
+ try {
+ return Hex.decodeHex(string);
+ } catch (DecoderException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String printHex(byte[] data) {
+ return Hex.encodeHexString(data);
+ }
+
+ public static byte[] parseBase64(String string) {
+ return Base64.decodeBase64(string);
+ }
+
+ public static String printBase64(byte[] data) {
+ return Base64.encodeBase64String(data);
+ }
+
+}
diff --git a/src/org/traccar/helper/DateBuilder.java b/src/main/java/org/traccar/helper/DateBuilder.java
index 6e1b779f0..9752f6977 100644
--- a/src/org/traccar/helper/DateBuilder.java
+++ b/src/main/java/org/traccar/helper/DateBuilder.java
@@ -69,7 +69,9 @@ public class DateBuilder {
public DateBuilder setCurrentDate() {
Calendar now = Calendar.getInstance(calendar.getTimeZone());
- return setYear(now.get(Calendar.YEAR)).setMonth(now.get(Calendar.MONTH)).setDay(now.get(Calendar.DAY_OF_MONTH));
+ return setYear(now.get(Calendar.YEAR))
+ .setMonth(now.get(Calendar.MONTH) + 1)
+ .setDay(now.get(Calendar.DAY_OF_MONTH));
}
public DateBuilder setHour(int hour) {
diff --git a/src/org/traccar/helper/DateUtil.java b/src/main/java/org/traccar/helper/DateUtil.java
index 30bb1b2fa..20a483e3c 100644
--- a/src/org/traccar/helper/DateUtil.java
+++ b/src/main/java/org/traccar/helper/DateUtil.java
@@ -15,16 +15,15 @@
*/
package org.traccar.helper;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
-import org.joda.time.format.DateTimeFormatter;
-import org.joda.time.format.ISODateTimeFormat;
-
public final class DateUtil {
- private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateTimeParser();
-
private DateUtil() {
}
@@ -61,6 +60,19 @@ public final class DateUtil {
}
public static Date parseDate(String value) {
- return DATE_FORMAT.parseDateTime(value).toDate();
+ return Date.from(Instant.from(DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(value)));
+ }
+
+ public static String formatDate(Date date) {
+ return formatDate(date, true);
}
+
+ public static String formatDate(Date date, boolean zoned) {
+ if (zoned) {
+ return DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault()).format(date.toInstant());
+ } else {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
+ }
+ }
+
}
diff --git a/src/org/traccar/helper/DistanceCalculator.java b/src/main/java/org/traccar/helper/DistanceCalculator.java
index 88d4ef8a4..88d4ef8a4 100644
--- a/src/org/traccar/helper/DistanceCalculator.java
+++ b/src/main/java/org/traccar/helper/DistanceCalculator.java
diff --git a/src/org/traccar/helper/Hashing.java b/src/main/java/org/traccar/helper/Hashing.java
index 3fcc124fa..e91310eda 100644
--- a/src/org/traccar/helper/Hashing.java
+++ b/src/main/java/org/traccar/helper/Hashing.java
@@ -17,7 +17,6 @@ package org.traccar.helper;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
-import javax.xml.bind.DatatypeConverter;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
@@ -75,13 +74,13 @@ public final class Hashing {
RANDOM.nextBytes(salt);
byte[] hash = function(password.toCharArray(), salt);
return new HashingResult(
- DatatypeConverter.printHexBinary(hash),
- DatatypeConverter.printHexBinary(salt));
+ DataConverter.printHex(hash),
+ DataConverter.printHex(salt));
}
public static boolean validatePassword(String password, String hashHex, String saltHex) {
- byte[] hash = DatatypeConverter.parseHexBinary(hashHex);
- byte[] salt = DatatypeConverter.parseHexBinary(saltHex);
+ byte[] hash = DataConverter.parseHex(hashHex);
+ byte[] salt = DataConverter.parseHex(saltHex);
return slowEquals(hash, function(password.toCharArray(), salt));
}
diff --git a/src/org/traccar/helper/LocationTree.java b/src/main/java/org/traccar/helper/LocationTree.java
index 3aff3ce33..3aff3ce33 100644
--- a/src/org/traccar/helper/LocationTree.java
+++ b/src/main/java/org/traccar/helper/LocationTree.java
diff --git a/src/main/java/org/traccar/helper/Log.java b/src/main/java/org/traccar/helper/Log.java
new file mode 100644
index 000000000..358f11a31
--- /dev/null
+++ b/src/main/java/org/traccar/helper/Log.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2012 - 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.helper;
+
+import org.traccar.config.Config;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Formatter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+public final class Log {
+
+ private Log() {
+ }
+
+ private static final String STACK_PACKAGE = "org.traccar";
+ private static final int STACK_LIMIT = 3;
+
+ private static class RollingFileHandler extends Handler {
+
+ private String name;
+ private String suffix;
+ private Writer writer;
+ private boolean rotate;
+
+ RollingFileHandler(String name, boolean rotate) {
+ this.name = name;
+ this.rotate = rotate;
+ }
+
+ @Override
+ public synchronized void publish(LogRecord record) {
+ if (isLoggable(record)) {
+ try {
+ String suffix = "";
+ if (rotate) {
+ suffix = new SimpleDateFormat("yyyyMMdd").format(new Date(record.getMillis()));
+ if (writer != null && !suffix.equals(this.suffix)) {
+ writer.close();
+ writer = null;
+ if (!new File(name).renameTo(new File(name + "." + this.suffix))) {
+ throw new RuntimeException("Log file renaming failed");
+ }
+ }
+ }
+ if (writer == null) {
+ this.suffix = suffix;
+ writer = new BufferedWriter(
+ new OutputStreamWriter(new FileOutputStream(name, true), StandardCharsets.UTF_8));
+ }
+ writer.write(getFormatter().format(record));
+ writer.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void flush() {
+ if (writer != null) {
+ try {
+ writer.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void close() throws SecurityException {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ }
+
+ public static class LogFormatter extends Formatter {
+
+ private boolean fullStackTraces;
+
+ LogFormatter(boolean fullStackTraces) {
+ this.fullStackTraces = fullStackTraces;
+ }
+
+ private static String formatLevel(Level level) {
+ switch (level.getName()) {
+ case "FINEST":
+ return "TRACE";
+ case "FINER":
+ case "FINE":
+ case "CONFIG":
+ return "DEBUG";
+ case "INFO":
+ return "INFO";
+ case "WARNING":
+ return "WARN";
+ case "SEVERE":
+ default:
+ return "ERROR";
+ }
+ }
+
+ @Override
+ public String format(LogRecord record) {
+ StringBuilder message = new StringBuilder();
+
+ if (record.getMessage() != null) {
+ message.append(record.getMessage());
+ }
+
+ if (record.getThrown() != null) {
+ if (message.length() > 0) {
+ message.append(" - ");
+ }
+ if (fullStackTraces) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ record.getThrown().printStackTrace(printWriter);
+ message.append(System.lineSeparator()).append(stringWriter.toString());
+ } else {
+ message.append(exceptionStack(record.getThrown()));
+ }
+ }
+
+ return String.format("%1$tF %1$tT %2$5s: %3$s%n",
+ new Date(record.getMillis()), formatLevel(record.getLevel()), message.toString());
+ }
+
+ }
+
+ public static void setupDefaultLogger() {
+ String path = null;
+ URL url = ClassLoader.getSystemClassLoader().getResource(".");
+ if (url != null) {
+ File jarPath = new File(url.getPath());
+ File logsPath = new File(jarPath, "logs");
+ if (!logsPath.exists() || !logsPath.isDirectory()) {
+ logsPath = jarPath;
+ }
+ path = new File(logsPath, "tracker-server.log").getPath();
+ }
+ setupLogger(path == null, path, Level.WARNING.getName(), false, true);
+ }
+
+ public static void setupLogger(Config config) {
+ setupLogger(
+ config.getBoolean("logger.console"),
+ config.getString("logger.file"),
+ config.getString("logger.level"),
+ config.getBoolean("logger.fullStackTraces"),
+ config.getBoolean("logger.rotate"));
+ }
+
+ private static void setupLogger(
+ boolean console, String file, String levelString, boolean fullStackTraces, boolean rotate) {
+
+ Logger rootLogger = Logger.getLogger("");
+ for (Handler handler : rootLogger.getHandlers()) {
+ rootLogger.removeHandler(handler);
+ }
+
+ Handler handler;
+ if (console) {
+ handler = new ConsoleHandler();
+ } else {
+ handler = new RollingFileHandler(file, rotate);
+ }
+
+ handler.setFormatter(new LogFormatter(fullStackTraces));
+
+ Level level = Level.parse(levelString.toUpperCase());
+ rootLogger.setLevel(level);
+ handler.setLevel(level);
+ handler.setFilter(record -> record != null && !record.getLoggerName().startsWith("sun"));
+
+ rootLogger.addHandler(handler);
+ }
+
+ public static String exceptionStack(Throwable exception) {
+ Throwable cause = exception.getCause();
+ while (cause != null && exception != cause) {
+ exception = cause;
+ cause = cause.getCause();
+ }
+
+ StringBuilder s = new StringBuilder();
+ String exceptionMsg = exception.getMessage();
+ if (exceptionMsg != null) {
+ s.append(exceptionMsg);
+ s.append(" - ");
+ }
+ s.append(exception.getClass().getSimpleName());
+ StackTraceElement[] stack = exception.getStackTrace();
+
+ if (stack.length > 0) {
+ int count = STACK_LIMIT;
+ boolean first = true;
+ boolean skip = false;
+ String file = "";
+ s.append(" (");
+ for (StackTraceElement element : stack) {
+ if (count > 0 && element.getClassName().startsWith(STACK_PACKAGE)) {
+ if (!first) {
+ s.append(" < ");
+ } else {
+ first = false;
+ }
+
+ if (skip) {
+ s.append("... < ");
+ skip = false;
+ }
+
+ if (file.equals(element.getFileName())) {
+ s.append("*");
+ } else {
+ file = element.getFileName();
+ s.append(file, 0, file.length() - 5); // remove ".java"
+ count -= 1;
+ }
+ s.append(":").append(element.getLineNumber());
+ } else {
+ skip = true;
+ }
+ }
+ if (skip) {
+ if (!first) {
+ s.append(" < ");
+ }
+ s.append("...");
+ }
+ s.append(")");
+ }
+ return s.toString();
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java
new file mode 100644
index 000000000..16d55ec60
--- /dev/null
+++ b/src/main/java/org/traccar/helper/LogAction.java
@@ -0,0 +1,107 @@
+/*
+ * 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.helper;
+
+import java.beans.Introspector;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.BaseModel;
+
+public final class LogAction {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(LogAction.class);
+
+ private LogAction() {
+ }
+
+ private static final String ACTION_CREATE = "create";
+ private static final String ACTION_EDIT = "edit";
+ private static final String ACTION_REMOVE = "remove";
+
+ private static final String ACTION_LINK = "link";
+ private static final String ACTION_UNLINK = "unlink";
+
+ private static final String ACTION_LOGIN = "login";
+ private static final String ACTION_LOGOUT = "logout";
+
+ private static final String ACTION_DEVICE_ACCUMULATORS = "resetDeviceAccumulators";
+
+ 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_FAILED = "login failed from: %s";
+ private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d";
+
+ public static void create(long userId, BaseModel object) {
+ logObjectAction(ACTION_CREATE, userId, object.getClass(), object.getId());
+ }
+
+ public static void edit(long userId, BaseModel object) {
+ logObjectAction(ACTION_EDIT, userId, object.getClass(), object.getId());
+ }
+
+ public static void remove(long userId, Class<?> clazz, long objectId) {
+ logObjectAction(ACTION_REMOVE, userId, clazz, objectId);
+ }
+
+ public static void link(long userId, Class<?> owner, long ownerId, Class<?> property, long propertyId) {
+ logLinkAction(ACTION_LINK, userId, owner, ownerId, property, propertyId);
+ }
+
+ public static void unlink(long userId, Class<?> owner, long ownerId, Class<?> property, long propertyId) {
+ logLinkAction(ACTION_UNLINK, userId, owner, ownerId, property, propertyId);
+ }
+
+ public static void login(long userId) {
+ logLoginAction(ACTION_LOGIN, userId);
+ }
+
+ public static void logout(long userId) {
+ logLoginAction(ACTION_LOGOUT, userId);
+ }
+
+ public static void failedLogin(String remoteAddress) {
+ if (remoteAddress == null || remoteAddress.isEmpty()) {
+ remoteAddress = "unknown";
+ }
+ LOGGER.info(String.format(PATTERN_LOGIN_FAILED, remoteAddress));
+ }
+
+ public static void resetDeviceAccumulators(long userId, long deviceId) {
+ LOGGER.info(String.format(
+ PATTERN_DEVICE_ACCUMULATORS, userId, ACTION_DEVICE_ACCUMULATORS, deviceId));
+ }
+
+ private static void logObjectAction(String action, long userId, Class<?> clazz, long objectId) {
+ LOGGER.info(String.format(
+ PATTERN_OBJECT, userId, action, Introspector.decapitalize(clazz.getSimpleName()), objectId));
+ }
+
+ private static void logLinkAction(String action, long userId,
+ Class<?> owner, long ownerId, Class<?> property, long propertyId) {
+ LOGGER.info(String.format(
+ PATTERN_LINK, userId, action,
+ Introspector.decapitalize(owner.getSimpleName()), ownerId,
+ Introspector.decapitalize(property.getSimpleName()), propertyId));
+ }
+
+ private static void logLoginAction(String action, long userId) {
+ LOGGER.info(String.format(PATTERN_LOGIN, userId, action));
+ }
+
+}
diff --git a/src/org/traccar/helper/ObdDecoder.java b/src/main/java/org/traccar/helper/ObdDecoder.java
index 4bc3bcdfb..1bdcce352 100644
--- a/src/org/traccar/helper/ObdDecoder.java
+++ b/src/main/java/org/traccar/helper/ObdDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,15 +29,6 @@ public final class ObdDecoder {
private static final int MODE_FREEZE_FRAME = 0x02;
private static final int MODE_CODES = 0x03;
- private static final int PID_ENGINE_LOAD = 0x04;
- private static final int PID_COOLANT_TEMPERATURE = 0x05;
- private static final int PID_ENGINE_RPM = 0x0C;
- private static final int PID_VEHICLE_SPEED = 0x0D;
- private static final int PID_THROTTLE_POSITION = 0x11;
- private static final int PID_MIL_DISTANCE = 0x21;
- private static final int PID_FUEL_LEVEL = 0x2F;
- private static final int PID_DISTANCE_CLEARED = 0x31;
-
public static Map.Entry<String, Object> decode(int mode, String value) {
switch (mode) {
case MODE_CURRENT:
@@ -86,21 +77,25 @@ public final class ObdDecoder {
public static Map.Entry<String, Object> decodeData(int pid, int value, boolean convert) {
switch (pid) {
- case PID_ENGINE_LOAD:
+ case 0x04:
return createEntry(Position.KEY_ENGINE_LOAD, convert ? value * 100 / 255 : value);
- case PID_COOLANT_TEMPERATURE:
+ case 0x05:
return createEntry(Position.KEY_COOLANT_TEMP, convert ? value - 40 : value);
- case PID_ENGINE_RPM:
+ case 0x0B:
+ return createEntry("mapIntake", value);
+ case 0x0C:
return createEntry(Position.KEY_RPM, convert ? value / 4 : value);
- case PID_VEHICLE_SPEED:
+ case 0x0D:
return createEntry(Position.KEY_OBD_SPEED, value);
- case PID_THROTTLE_POSITION:
+ case 0x0F:
+ return createEntry("intakeTemp", convert ? value - 40 : value);
+ case 0x11:
return createEntry(Position.KEY_THROTTLE, convert ? value * 100 / 255 : value);
- case PID_MIL_DISTANCE:
+ case 0x21:
return createEntry("milDistance", value);
- case PID_FUEL_LEVEL:
+ case 0x2F:
return createEntry(Position.KEY_FUEL_LEVEL, convert ? value * 100 / 255 : value);
- case PID_DISTANCE_CLEARED:
+ case 0x31:
return createEntry("clearedDistance", value);
default:
return null;
diff --git a/src/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java
index 1471ec237..1471ec237 100644
--- a/src/org/traccar/helper/Parser.java
+++ b/src/main/java/org/traccar/helper/Parser.java
diff --git a/src/org/traccar/helper/PatternBuilder.java b/src/main/java/org/traccar/helper/PatternBuilder.java
index f3de5c1e5..5c4638189 100644
--- a/src/org/traccar/helper/PatternBuilder.java
+++ b/src/main/java/org/traccar/helper/PatternBuilder.java
@@ -56,7 +56,7 @@ public class PatternBuilder {
}
public PatternBuilder any() {
- fragments.add(".*?");
+ fragments.add(".*");
return this;
}
diff --git a/src/org/traccar/helper/PatternUtil.java b/src/main/java/org/traccar/helper/PatternUtil.java
index 1bbb166a6..74813e1d9 100644
--- a/src/org/traccar/helper/PatternUtil.java
+++ b/src/main/java/org/traccar/helper/PatternUtil.java
@@ -15,6 +15,9 @@
*/
package org.traccar.helper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.lang.management.ManagementFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -22,6 +25,8 @@ import java.util.regex.PatternSyntaxException;
public final class PatternUtil {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PatternUtil.class);
+
private PatternUtil() {
}
@@ -66,7 +71,7 @@ public final class PatternUtil {
result.stringTail = input.substring(matcher.group(1).length());
}
} catch (PatternSyntaxException error) {
- Log.warning(error);
+ LOGGER.warn("Pattern matching error", error);
}
}
diff --git a/src/main/java/org/traccar/helper/SanitizerModule.java b/src/main/java/org/traccar/helper/SanitizerModule.java
new file mode 100644
index 000000000..af9ac5c2b
--- /dev/null
+++ b/src/main/java/org/traccar/helper/SanitizerModule.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import org.owasp.encoder.Encode;
+
+import java.io.IOException;
+
+public class SanitizerModule extends SimpleModule {
+
+ public static class SanitizerSerializer extends StdSerializer<String> {
+
+ protected SanitizerSerializer() {
+ super(String.class);
+ }
+
+ @Override
+ public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+ gen.writeString(Encode.forHtml(value));
+ }
+
+ }
+
+ public SanitizerModule() {
+ addSerializer(new SanitizerSerializer());
+ }
+
+}
diff --git a/src/main/java/org/traccar/helper/ServletHelper.java b/src/main/java/org/traccar/helper/ServletHelper.java
new file mode 100644
index 000000000..b6c587ec3
--- /dev/null
+++ b/src/main/java/org/traccar/helper/ServletHelper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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.helper;
+
+import javax.servlet.http.HttpServletRequest;
+
+public final class ServletHelper {
+
+ private ServletHelper() {
+ }
+
+ public static String retrieveRemoteAddress(HttpServletRequest request) {
+
+ if (request != null) {
+ String remoteAddress = request.getHeader("X-FORWARDED-FOR");
+
+ if (remoteAddress != null && !remoteAddress.isEmpty()) {
+ int separatorIndex = remoteAddress.indexOf(",");
+ if (separatorIndex > 0) {
+ return remoteAddress.substring(0, separatorIndex); // remove the additional data
+ } else {
+ return remoteAddress;
+ }
+ } else {
+ return request.getRemoteAddr();
+ }
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/traccar/helper/UnitsConverter.java b/src/main/java/org/traccar/helper/UnitsConverter.java
index 56d15e4e7..3dd435df4 100644
--- a/src/org/traccar/helper/UnitsConverter.java
+++ b/src/main/java/org/traccar/helper/UnitsConverter.java
@@ -23,6 +23,8 @@ public final class UnitsConverter {
private static final double KNOTS_TO_CPS_RATIO = 0.0194384449;
private static final double METERS_TO_FEET_RATIO = 0.3048;
private static final double METERS_TO_MILE_RATIO = 1609.34;
+ private static final long MILLISECONDS_TO_HOURS_RATIO = 3600000;
+ private static final long MILLISECONDS_TO_MINUTES_RATIO = 60000;
private UnitsConverter() {
}
@@ -71,4 +73,16 @@ public final class UnitsConverter {
return value * METERS_TO_MILE_RATIO;
}
+ public static long msFromHours(long value) {
+ return value * MILLISECONDS_TO_HOURS_RATIO;
+ }
+
+ public static long msFromHours(double value) {
+ return (long) (value * MILLISECONDS_TO_HOURS_RATIO);
+ }
+
+ public static long msFromMinutes(long value) {
+ return value * MILLISECONDS_TO_MINUTES_RATIO;
+ }
+
}
diff --git a/src/org/traccar/model/Attribute.java b/src/main/java/org/traccar/model/Attribute.java
index 45d40b3ec..45d40b3ec 100644
--- a/src/org/traccar/model/Attribute.java
+++ b/src/main/java/org/traccar/model/Attribute.java
diff --git a/src/org/traccar/model/BaseModel.java b/src/main/java/org/traccar/model/BaseModel.java
index 8bdb916e8..8bdb916e8 100644
--- a/src/org/traccar/model/BaseModel.java
+++ b/src/main/java/org/traccar/model/BaseModel.java
diff --git a/src/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java
index 56d3eb74c..56d3eb74c 100644
--- a/src/org/traccar/model/Calendar.java
+++ b/src/main/java/org/traccar/model/Calendar.java
diff --git a/src/org/traccar/model/CellTower.java b/src/main/java/org/traccar/model/CellTower.java
index 6d1dfbd7f..6d1dfbd7f 100644
--- a/src/org/traccar/model/CellTower.java
+++ b/src/main/java/org/traccar/model/CellTower.java
diff --git a/src/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java
index 16205ede1..abe538a10 100644
--- a/src/org/traccar/model/Command.java
+++ b/src/main/java/org/traccar/model/Command.java
@@ -33,12 +33,14 @@ public class Command extends Message implements Cloneable {
public static final String TYPE_ALARM_DISARM = "alarmDisarm";
public static final String TYPE_SET_TIMEZONE = "setTimezone";
public static final String TYPE_REQUEST_PHOTO = "requestPhoto";
+ public static final String TYPE_POWER_OFF = "powerOff";
public static final String TYPE_REBOOT_DEVICE = "rebootDevice";
public static final String TYPE_SEND_SMS = "sendSms";
public static final String TYPE_SEND_USSD = "sendUssd";
public static final String TYPE_SOS_NUMBER = "sosNumber";
public static final String TYPE_SILENCE_TIME = "silenceTime";
public static final String TYPE_SET_PHONEBOOK = "setPhonebook";
+ public static final String TYPE_MESSAGE = "message";
public static final String TYPE_VOICE_MESSAGE = "voiceMessage";
public static final String TYPE_OUTPUT_CONTROL = "outputControl";
public static final String TYPE_VOICE_MONITORING = "voiceMonitoring";
@@ -66,6 +68,7 @@ public class Command extends Message implements Cloneable {
public static final String KEY_UNIQUE_ID = "uniqueId";
public static final String KEY_FREQUENCY = "frequency";
+ public static final String KEY_LANGUAGE = "language";
public static final String KEY_TIMEZONE = "timezone";
public static final String KEY_DEVICE_PASSWORD = "devicePassword";
public static final String KEY_RADIUS = "radius";
diff --git a/src/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java
index c8a28404c..0c9be932d 100644
--- a/src/org/traccar/model/Device.java
+++ b/src/main/java/org/traccar/model/Device.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -21,7 +21,7 @@ import java.util.List;
import org.traccar.database.QueryExtended;
import org.traccar.database.QueryIgnore;
-public class Device extends ExtendedModel {
+public class Device extends GroupedModel {
private String name;
@@ -88,16 +88,6 @@ public class Device extends ExtendedModel {
this.positionId = positionId;
}
- private long groupId;
-
- public long getGroupId() {
- return groupId;
- }
-
- public void setGroupId(long groupId) {
- this.groupId = groupId;
- }
-
private List<Long> geofenceIds;
@QueryIgnore
@@ -149,4 +139,14 @@ public class Device extends ExtendedModel {
this.category = category;
}
+ private boolean disabled;
+
+ public boolean getDisabled() {
+ return disabled;
+ }
+
+ public void setDisabled(boolean disabled) {
+ this.disabled = disabled;
+ }
+
}
diff --git a/src/org/traccar/model/DeviceTotalDistance.java b/src/main/java/org/traccar/model/DeviceAccumulators.java
index 4c89b7689..8a90826c4 100644
--- a/src/org/traccar/model/DeviceTotalDistance.java
+++ b/src/main/java/org/traccar/model/DeviceAccumulators.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * 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.
@@ -16,7 +16,7 @@
*/
package org.traccar.model;
-public class DeviceTotalDistance {
+public class DeviceAccumulators {
private long deviceId;
@@ -28,14 +28,24 @@ public class DeviceTotalDistance {
this.deviceId = deviceId;
}
- private double totalDistance;
+ private Double totalDistance;
- public double getTotalDistance() {
+ public Double getTotalDistance() {
return totalDistance;
}
- public void setTotalDistance(double totalDistance) {
+ public void setTotalDistance(Double totalDistance) {
this.totalDistance = totalDistance;
}
+ private Long hours;
+
+ public Long getHours() {
+ return hours;
+ }
+
+ public void setHours(Long hours) {
+ this.hours = hours;
+ }
+
}
diff --git a/src/org/traccar/model/DeviceState.java b/src/main/java/org/traccar/model/DeviceState.java
index f2d0ff614..75d6726ee 100644
--- a/src/org/traccar/model/DeviceState.java
+++ b/src/main/java/org/traccar/model/DeviceState.java
@@ -58,4 +58,14 @@ public class DeviceState {
return overspeedPosition;
}
+ private long overspeedGeofenceId;
+
+ public void setOverspeedGeofenceId(long overspeedGeofenceId) {
+ this.overspeedGeofenceId = overspeedGeofenceId;
+ }
+
+ public long getOverspeedGeofenceId() {
+ return overspeedGeofenceId;
+ }
+
}
diff --git a/src/org/traccar/model/Driver.java b/src/main/java/org/traccar/model/Driver.java
index 05f52fd4d..05f52fd4d 100644
--- a/src/org/traccar/model/Driver.java
+++ b/src/main/java/org/traccar/model/Driver.java
diff --git a/src/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java
index 47b60af01..ee7fcc679 100644
--- a/src/org/traccar/model/Event.java
+++ b/src/main/java/org/traccar/model/Event.java
@@ -64,19 +64,11 @@ public class Event extends Message {
private Date serverTime;
public Date getServerTime() {
- if (serverTime != null) {
- return new Date(serverTime.getTime());
- } else {
- return null;
- }
+ return serverTime;
}
public void setServerTime(Date serverTime) {
- if (serverTime != null) {
- this.serverTime = new Date(serverTime.getTime());
- } else {
- this.serverTime = null;
- }
+ this.serverTime = serverTime;
}
private long positionId;
@@ -99,4 +91,14 @@ public class Event extends Message {
this.geofenceId = geofenceId;
}
+ private long maintenanceId = 0;
+
+ public long getMaintenanceId() {
+ return maintenanceId;
+ }
+
+ public void setMaintenanceId(long maintenanceId) {
+ this.maintenanceId = maintenanceId;
+ }
+
}
diff --git a/src/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java
index 8353d0e66..8353d0e66 100644
--- a/src/org/traccar/model/ExtendedModel.java
+++ b/src/main/java/org/traccar/model/ExtendedModel.java
diff --git a/src/org/traccar/model/Geofence.java b/src/main/java/org/traccar/model/Geofence.java
index 21c196da9..8560d22e9 100644
--- a/src/org/traccar/model/Geofence.java
+++ b/src/main/java/org/traccar/model/Geofence.java
@@ -26,7 +26,7 @@ import org.traccar.geofence.GeofencePolyline;
import com.fasterxml.jackson.annotation.JsonIgnore;
-public class Geofence extends ExtendedModel {
+public class Geofence extends ScheduledModel {
public static final String TYPE_GEOFENCE_CILCLE = "geofenceCircle";
public static final String TYPE_GEOFENCE_POLYGON = "geofencePolygon";
@@ -65,7 +65,9 @@ public class Geofence extends ExtendedModel {
} else if (area.startsWith("POLYGON")) {
geometry = new GeofencePolygon(area);
} else if (area.startsWith("LINESTRING")) {
- geometry = new GeofencePolyline(area, Context.getConfig().getDouble("geofence.polylineDistance", 25));
+ final double distance = getDouble("polylineDistance");
+ geometry = new GeofencePolyline(area, distance > 0 ? distance
+ : Context.getConfig().getDouble("geofence.polylineDistance", 25));
} else {
throw new ParseException("Unknown geometry type", 0);
}
@@ -87,14 +89,4 @@ public class Geofence extends ExtendedModel {
area = geometry.toWkt();
this.geometry = geometry;
}
-
- private long calendarId;
-
- public long getCalendarId() {
- return calendarId;
- }
-
- public void setCalendarId(long calendarId) {
- this.calendarId = calendarId;
- }
}
diff --git a/src/main/java/org/traccar/model/Group.java b/src/main/java/org/traccar/model/Group.java
new file mode 100644
index 000000000..91ea2319d
--- /dev/null
+++ b/src/main/java/org/traccar/model/Group.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+public class Group extends GroupedModel {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/src/org/traccar/model/Group.java b/src/main/java/org/traccar/model/GroupedModel.java
index aad206aad..6b1aa75b1 100644
--- a/src/org/traccar/model/Group.java
+++ b/src/main/java/org/traccar/model/GroupedModel.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,17 +16,7 @@
*/
package org.traccar.model;
-public class Group extends ExtendedModel {
-
- private String name;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
+public class GroupedModel extends ExtendedModel {
private long groupId;
diff --git a/src/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Maintenance.java
index 9d6034fff..73f67ea96 100644
--- a/src/org/traccar/model/Notification.java
+++ b/src/main/java/org/traccar/model/Maintenance.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +16,16 @@
*/
package org.traccar.model;
-public class Notification extends ExtendedModel {
+public class Maintenance extends ExtendedModel {
- private boolean always;
+ private String name;
- public boolean getAlways() {
- return always;
+ public String getName() {
+ return name;
}
- public void setAlways(boolean always) {
- this.always = always;
+ public void setName(String name) {
+ this.name = name;
}
private String type;
@@ -37,33 +38,24 @@ public class Notification extends ExtendedModel {
this.type = type;
}
- private boolean web;
+ private double start;
- public boolean getWeb() {
- return web;
+ public double getStart() {
+ return start;
}
- public void setWeb(boolean web) {
- this.web = web;
+ public void setStart(double start) {
+ this.start = start;
}
- private boolean mail;
+ private double period;
- public boolean getMail() {
- return mail;
+ public double getPeriod() {
+ return period;
}
- public void setMail(boolean mail) {
- this.mail = mail;
+ public void setPeriod(double period) {
+ this.period = period;
}
- private boolean sms;
-
- public boolean getSms() {
- return sms;
- }
-
- public void setSms(boolean sms) {
- this.sms = sms;
- }
}
diff --git a/src/org/traccar/model/ManagedUser.java b/src/main/java/org/traccar/model/ManagedUser.java
index 03c5ef48d..03c5ef48d 100644
--- a/src/org/traccar/model/ManagedUser.java
+++ b/src/main/java/org/traccar/model/ManagedUser.java
diff --git a/src/org/traccar/model/Message.java b/src/main/java/org/traccar/model/Message.java
index dad9c20f0..dad9c20f0 100644
--- a/src/org/traccar/model/Message.java
+++ b/src/main/java/org/traccar/model/Message.java
diff --git a/src/org/traccar/model/MiscFormatter.java b/src/main/java/org/traccar/model/MiscFormatter.java
index c6511f063..c6511f063 100644
--- a/src/org/traccar/model/MiscFormatter.java
+++ b/src/main/java/org/traccar/model/MiscFormatter.java
diff --git a/src/org/traccar/model/Network.java b/src/main/java/org/traccar/model/Network.java
index 2d56950f1..4d67fc5d8 100644
--- a/src/org/traccar/model/Network.java
+++ b/src/main/java/org/traccar/model/Network.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -30,6 +30,10 @@ public class Network {
addCellTower(cellTower);
}
+ public Network(WifiAccessPoint wifiAccessPoint) {
+ addWifiAccessPoint(wifiAccessPoint);
+ }
+
private Integer homeMobileCountryCode;
public Integer getHomeMobileCountryCode() {
diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java
new file mode 100644
index 000000000..f1983a03a
--- /dev/null
+++ b/src/main/java/org/traccar/model/Notification.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.traccar.database.QueryIgnore;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class Notification extends ScheduledModel {
+
+ private boolean always;
+
+ public boolean getAlways() {
+ return always;
+ }
+
+ public void setAlways(boolean always) {
+ this.always = always;
+ }
+
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+
+ private String notificators;
+
+ public String getNotificators() {
+ return notificators;
+ }
+
+ public void setNotificators(String transports) {
+ this.notificators = transports;
+ }
+
+
+ @JsonIgnore
+ @QueryIgnore
+ public Set<String> getNotificatorsTypes() {
+ final Set<String> result = new HashSet<>();
+ if (notificators != null) {
+ final String[] transportsList = notificators.split(",");
+ for (String transport : transportsList) {
+ result.add(transport.trim());
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/src/org/traccar/model/Permission.java b/src/main/java/org/traccar/model/Permission.java
index 1006b1c47..1006b1c47 100644
--- a/src/org/traccar/model/Permission.java
+++ b/src/main/java/org/traccar/model/Permission.java
diff --git a/src/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java
index 099e6d686..2c0e22c9e 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/main/java/org/traccar/model/Position.java
@@ -39,6 +39,7 @@ public class Position extends Message {
public static final String KEY_ODOMETER_TRIP = "tripOdometer"; // meters
public static final String KEY_HOURS = "hours";
public static final String KEY_STEPS = "steps";
+ public static final String KEY_HEART_RATE = "heartRate";
public static final String KEY_INPUT = "input";
public static final String KEY_OUTPUT = "output";
public static final String KEY_IMAGE = "image";
@@ -51,6 +52,7 @@ public class Position extends Message {
public static final String KEY_BATTERY = "battery"; // volts
public static final String KEY_BATTERY_LEVEL = "batteryLevel"; // percentage
public static final String KEY_FUEL_LEVEL = "fuel"; // liters
+ public static final String KEY_FUEL_USED = "fuelUsed"; // liters
public static final String KEY_FUEL_CONSUMPTION = "fuelConsumption"; // liters/hour
public static final String KEY_VERSION_FW = "versionFw";
@@ -58,6 +60,7 @@ public class Position extends Message {
public static final String KEY_TYPE = "type";
public static final String KEY_IGNITION = "ignition";
public static final String KEY_FLAGS = "flags";
+ public static final String KEY_ANTENNA = "antenna";
public static final String KEY_CHARGE = "charge";
public static final String KEY_IP = "ip";
public static final String KEY_ARCHIVE = "archive";
@@ -78,6 +81,10 @@ public class Position extends Message {
public static final String KEY_COMMAND = "command";
public static final String KEY_BLOCKED = "blocked";
public static final String KEY_DOOR = "door";
+ public static final String KEY_AXLE_WEIGHT = "axleWeight";
+ public static final String KEY_G_SENSOR = "gSensor";
+ public static final String KEY_ICCID = "iccid";
+ public static final String KEY_PHONE = "phone";
public static final String KEY_DTCS = "dtcs";
public static final String KEY_OBD_SPEED = "obdSpeed"; // knots
@@ -108,15 +115,20 @@ public class Position extends Message {
public static final String ALARM_POWER_OFF = "powerOff";
public static final String ALARM_POWER_ON = "powerOn";
public static final String ALARM_DOOR = "door";
+ public static final String ALARM_LOCK = "lock";
+ public static final String ALARM_UNLOCK = "unlock";
public static final String ALARM_GEOFENCE = "geofence";
public static final String ALARM_GEOFENCE_ENTER = "geofenceEnter";
public static final String ALARM_GEOFENCE_EXIT = "geofenceExit";
public static final String ALARM_GPS_ANTENNA_CUT = "gpsAntennaCut";
public static final String ALARM_ACCIDENT = "accident";
public static final String ALARM_TOW = "tow";
+ public static final String ALARM_IDLE = "idle";
+ public static final String ALARM_HIGH_RPM = "highRpm";
public static final String ALARM_ACCELERATION = "hardAcceleration";
public static final String ALARM_BRAKING = "hardBraking";
public static final String ALARM_CORNERING = "hardCornering";
+ public static final String ALARM_LANE_CHANGE = "laneChange";
public static final String ALARM_FATIGUE_DRIVING = "fatigueDriving";
public static final String ALARM_POWER_CUT = "powerCut";
public static final String ALARM_POWER_RESTORED = "powerRestored";
@@ -126,10 +138,18 @@ public class Position extends Message {
public static final String ALARM_SHOCK = "shock";
public static final String ALARM_BONNET = "bonnet";
public static final String ALARM_FOOT_BRAKE = "footBrake";
- public static final String ALARM_OIL_LEAK = "oilLeak";
+ public static final String ALARM_FUEL_LEAK = "fuelLeak";
public static final String ALARM_TAMPERING = "tampering";
public static final String ALARM_REMOVING = "removing";
+ public Position() {
+ }
+
+ public Position(String protocol) {
+ this.protocol = protocol;
+ this.serverTime = new Date();
+ }
+
private String protocol;
public String getProtocol() {
@@ -140,58 +160,34 @@ public class Position extends Message {
this.protocol = protocol;
}
- private Date serverTime;
+ private Date serverTime = new Date();
public Date getServerTime() {
- if (serverTime != null) {
- return new Date(serverTime.getTime());
- } else {
- return null;
- }
+ return serverTime;
}
public void setServerTime(Date serverTime) {
- if (serverTime != null) {
- this.serverTime = new Date(serverTime.getTime());
- } else {
- this.serverTime = null;
- }
+ this.serverTime = serverTime;
}
private Date deviceTime;
public Date getDeviceTime() {
- if (deviceTime != null) {
- return new Date(deviceTime.getTime());
- } else {
- return null;
- }
+ return deviceTime;
}
public void setDeviceTime(Date deviceTime) {
- if (deviceTime != null) {
- this.deviceTime = new Date(deviceTime.getTime());
- } else {
- this.deviceTime = null;
- }
+ this.deviceTime = deviceTime;
}
private Date fixTime;
public Date getFixTime() {
- if (fixTime != null) {
- return new Date(fixTime.getTime());
- } else {
- return null;
- }
+ return fixTime;
}
public void setFixTime(Date fixTime) {
- if (fixTime != null) {
- this.fixTime = new Date(fixTime.getTime());
- } else {
- this.fixTime = null;
- }
+ this.fixTime = fixTime;
}
public void setTime(Date time) {
diff --git a/src/main/java/org/traccar/model/ScheduledModel.java b/src/main/java/org/traccar/model/ScheduledModel.java
new file mode 100644
index 000000000..9e6a4b9a6
--- /dev/null
+++ b/src/main/java/org/traccar/model/ScheduledModel.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+public class ScheduledModel extends ExtendedModel {
+
+ private long calendarId;
+
+ public long getCalendarId() {
+ return calendarId;
+ }
+
+ public void setCalendarId(long calendarId) {
+ this.calendarId = calendarId;
+ }
+}
diff --git a/src/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java
index 072e85d55..ad37e7078 100644
--- a/src/org/traccar/model/Server.java
+++ b/src/main/java/org/traccar/model/Server.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -16,13 +16,12 @@
package org.traccar.model;
import org.traccar.database.QueryIgnore;
-import org.traccar.helper.Log;
public class Server extends ExtendedModel {
@QueryIgnore
public String getVersion() {
- return Log.getAppVersion();
+ return getClass().getPackage().getImplementationVersion();
}
public void setVersion(String version) {
@@ -157,4 +156,14 @@ public class Server extends ExtendedModel {
public void setLimitCommands(boolean limitCommands) {
this.limitCommands = limitCommands;
}
+
+ private String poiLayer;
+
+ public String getPoiLayer() {
+ return poiLayer;
+ }
+
+ public void setPoiLayer(String poiLayer) {
+ this.poiLayer = poiLayer;
+ }
}
diff --git a/src/org/traccar/model/Statistics.java b/src/main/java/org/traccar/model/Statistics.java
index 2acf8514f..cb72c91dd 100644
--- a/src/org/traccar/model/Statistics.java
+++ b/src/main/java/org/traccar/model/Statistics.java
@@ -22,19 +22,11 @@ public class Statistics extends ExtendedModel {
private Date captureTime;
public Date getCaptureTime() {
- if (captureTime != null) {
- return new Date(captureTime.getTime());
- } else {
- return null;
- }
+ return captureTime;
}
public void setCaptureTime(Date captureTime) {
- if (captureTime != null) {
- this.captureTime = new Date(captureTime.getTime());
- } else {
- this.captureTime = null;
- }
+ this.captureTime = captureTime;
}
private int activeUsers;
diff --git a/src/org/traccar/model/Typed.java b/src/main/java/org/traccar/model/Typed.java
index 313ec7bcd..313ec7bcd 100644
--- a/src/org/traccar/model/Typed.java
+++ b/src/main/java/org/traccar/model/Typed.java
diff --git a/src/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java
index 5d89dcfae..976b6aac0 100644
--- a/src/org/traccar/model/User.java
+++ b/src/main/java/org/traccar/model/User.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -35,6 +35,16 @@ public class User extends ExtendedModel {
this.name = name;
}
+ private String login;
+
+ public String getLogin() {
+ return login;
+ }
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
private String email;
public String getEmail() {
@@ -65,14 +75,14 @@ public class User extends ExtendedModel {
this.readonly = readonly;
}
- private boolean admin;
+ private boolean administrator;
- public boolean getAdmin() {
- return admin;
+ public boolean getAdministrator() {
+ return administrator;
}
- public void setAdmin(boolean admin) {
- this.admin = admin;
+ public void setAdministrator(boolean administrator) {
+ this.administrator = administrator;
}
private String map;
@@ -148,19 +158,11 @@ public class User extends ExtendedModel {
private Date expirationTime;
public Date getExpirationTime() {
- if (expirationTime != null) {
- return new Date(expirationTime.getTime());
- } else {
- return null;
- }
+ return expirationTime;
}
public void setExpirationTime(Date expirationTime) {
- if (expirationTime != null) {
- this.expirationTime = new Date(expirationTime.getTime());
- } else {
- this.expirationTime = null;
- }
+ this.expirationTime = expirationTime;
}
private int deviceLimit;
@@ -201,7 +203,7 @@ public class User extends ExtendedModel {
public void setToken(String token) {
if (token != null && !token.isEmpty()) {
- if (!token.matches("^[a-zA-Z0-9]{16,}$")) {
+ if (!token.matches("^[a-zA-Z0-9-]{16,}$")) {
throw new IllegalArgumentException("Illegal token");
}
this.token = token;
@@ -220,6 +222,16 @@ public class User extends ExtendedModel {
this.limitCommands = limitCommands;
}
+ private String poiLayer;
+
+ public String getPoiLayer() {
+ return poiLayer;
+ }
+
+ public void setPoiLayer(String poiLayer) {
+ this.poiLayer = poiLayer;
+ }
+
@QueryIgnore
public String getPassword() {
return null;
diff --git a/src/org/traccar/model/WifiAccessPoint.java b/src/main/java/org/traccar/model/WifiAccessPoint.java
index 87a77f3c0..87a77f3c0 100644
--- a/src/org/traccar/model/WifiAccessPoint.java
+++ b/src/main/java/org/traccar/model/WifiAccessPoint.java
diff --git a/src/main/java/org/traccar/notification/EventForwarder.java b/src/main/java/org/traccar/notification/EventForwarder.java
new file mode 100644
index 000000000..c0010ebbd
--- /dev/null
+++ b/src/main/java/org/traccar/notification/EventForwarder.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 - 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.notification;
+
+import org.traccar.Context;
+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.AsyncInvoker;
+import javax.ws.rs.client.Invocation;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class EventForwarder {
+
+ private final String url;
+ private final String header;
+
+ public EventForwarder() {
+ url = Context.getConfig().getString("event.forward.url", "http://localhost/");
+ header = Context.getConfig().getString("event.forward.header");
+ }
+
+ 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());
+ }
+ }
+
+ executeRequest(event, position, users, requestBuilder.async());
+ }
+
+ 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;
+ }
+
+ protected abstract void executeRequest(
+ Event event, Position position, Set<Long> users, AsyncInvoker invoker);
+
+}
diff --git a/src/org/traccar/notification/MailMessage.java b/src/main/java/org/traccar/notification/FullMessage.java
index 0fce43740..f66537c6e 100644
--- a/src/org/traccar/notification/MailMessage.java
+++ b/src/main/java/org/traccar/notification/FullMessage.java
@@ -16,12 +16,12 @@
*/
package org.traccar.notification;
-public class MailMessage {
+public class FullMessage {
private String subject;
private String body;
- public MailMessage(String subject, String body) {
+ public FullMessage(String subject, String body) {
this.subject = subject;
this.body = body;
}
diff --git a/src/main/java/org/traccar/notification/JsonTypeEventForwarder.java b/src/main/java/org/traccar/notification/JsonTypeEventForwarder.java
new file mode 100644
index 000000000..55d926fc8
--- /dev/null
+++ b/src/main/java/org/traccar/notification/JsonTypeEventForwarder.java
@@ -0,0 +1,18 @@
+package org.traccar.notification;
+
+import java.util.Set;
+
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.Entity;
+
+public class JsonTypeEventForwarder extends EventForwarder {
+
+ @Override
+ protected void executeRequest(Event event, Position position, Set<Long> users, AsyncInvoker invoker) {
+ invoker.post(Entity.json(preparePayload(event, position, users)));
+ }
+
+}
diff --git a/src/main/java/org/traccar/notification/MessageException.java b/src/main/java/org/traccar/notification/MessageException.java
new file mode 100644
index 000000000..710b927b0
--- /dev/null
+++ b/src/main/java/org/traccar/notification/MessageException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notification;
+
+public class MessageException extends Exception {
+
+ public MessageException(Throwable cause) {
+ super(cause);
+ }
+
+ public MessageException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java
index 8da819430..2f8100226 100644
--- a/src/org/traccar/notification/NotificationFormatter.java
+++ b/src/main/java/org/traccar/notification/NotificationFormatter.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2018 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.
@@ -18,6 +18,7 @@ package org.traccar.notification;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
import java.util.Locale;
import org.apache.velocity.Template;
@@ -25,8 +26,9 @@ import org.apache.velocity.VelocityContext;
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.helper.Log;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -35,10 +37,13 @@ import org.traccar.reports.ReportUtils;
public final class NotificationFormatter {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificationFormatter.class);
+
private NotificationFormatter() {
}
public static VelocityContext prepareContext(long userId, Event event, Position position) {
+
User user = Context.getPermissionsManager().getUser(userId);
Device device = Context.getIdentityManager().getById(event.getDeviceId());
@@ -55,6 +60,9 @@ public final class NotificationFormatter {
if (event.getGeofenceId() != 0) {
velocityContext.put("geofence", Context.getGeofenceManager().getById(event.getGeofenceId()));
}
+ if (event.getMaintenanceId() != 0) {
+ velocityContext.put("maintenance", Context.getMaintenancesManager().getById(event.getMaintenanceId()));
+ }
String driverUniqueId = event.getString(Position.KEY_DRIVER_UNIQUE_ID);
if (driverUniqueId != null) {
velocityContext.put("driver", Context.getDriversManager().getDriverByUniqueId(driverUniqueId));
@@ -68,31 +76,43 @@ public final class NotificationFormatter {
}
public static Template getTemplate(Event event, String path) {
+
+ String templateFilePath;
Template template;
+
try {
- template = Context.getVelocityEngine().getTemplate(path + event.getType() + ".vm",
- StandardCharsets.UTF_8.name());
+ templateFilePath = Paths.get(path, event.getType() + ".vm").toString();
+ template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
} catch (ResourceNotFoundException error) {
- Log.warning(error);
- template = Context.getVelocityEngine().getTemplate(path + "unknown.vm",
- StandardCharsets.UTF_8.name());
+ LOGGER.warn("Notification template error", error);
+ templateFilePath = Paths.get(path, "unknown.vm").toString();
+ template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name());
}
return template;
}
- public static MailMessage formatMailMessage(long userId, Event event, Position position) {
+ public static FullMessage formatFullMessage(long userId, Event event, Position position) {
VelocityContext velocityContext = prepareContext(userId, event, position);
- StringWriter writer = new StringWriter();
- getTemplate(event, Context.getConfig().getString("mail.templatesPath", "mail") + "/")
- .merge(velocityContext, writer);
- return new MailMessage((String) velocityContext.get("subject"), writer.toString());
+ String formattedMessage = formatMessage(velocityContext, userId, event, position, "full");
+
+ return new FullMessage((String) velocityContext.get("subject"), formattedMessage);
}
- public static String formatSmsMessage(long userId, Event event, Position position) {
- VelocityContext velocityContext = prepareContext(userId, event, position);
+ public static String formatShortMessage(long userId, Event event, Position position) {
+ return formatMessage(null, userId, event, position, "short");
+ }
+
+ private static String formatMessage(VelocityContext vc, Long userId, Event event, Position position,
+ String templatePath) {
+
+ VelocityContext velocityContext = vc;
+ if (velocityContext == null) {
+ velocityContext = prepareContext(userId, event, position);
+ }
StringWriter writer = new StringWriter();
- getTemplate(event, Context.getConfig().getString("sms.templatesPath", "sms") + "/")
- .merge(velocityContext, writer);
+ getTemplate(event, templatePath).merge(velocityContext, writer);
+
return writer.toString();
}
+
}
diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java
new file mode 100644
index 000000000..191748379
--- /dev/null
+++ b/src/main/java/org/traccar/notification/NotificatorManager.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notification;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.model.Typed;
+import org.traccar.notificators.NotificatorFirebase;
+import org.traccar.notificators.NotificatorMail;
+import org.traccar.notificators.NotificatorNull;
+import org.traccar.notificators.Notificator;
+import org.traccar.notificators.NotificatorSms;
+import org.traccar.notificators.NotificatorWeb;
+import org.traccar.notificators.NotificatorTelegram;
+
+public final class NotificatorManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorManager.class);
+
+ private static final Notificator NULL_NOTIFICATOR = new NotificatorNull();
+
+ private final Map<String, Notificator> notificators = new HashMap<>();
+
+ 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 "telegram":
+ defaultNotificator = NotificatorTelegram.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());
+ }
+ }
+ }
+
+ 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;
+ }
+ return notificator;
+ }
+
+ public Set<Typed> getAllNotificatorTypes() {
+ Set<Typed> result = new HashSet<>();
+ for (String notificator : notificators.keySet()) {
+ result.add(new Typed(notificator));
+ }
+ return result;
+ }
+
+}
diff --git a/src/org/traccar/notification/PropertiesProvider.java b/src/main/java/org/traccar/notification/PropertiesProvider.java
index c5ba688e8..f0078feef 100644
--- a/src/org/traccar/notification/PropertiesProvider.java
+++ b/src/main/java/org/traccar/notification/PropertiesProvider.java
@@ -15,7 +15,7 @@
*/
package org.traccar.notification;
-import org.traccar.Config;
+import org.traccar.config.Config;
import org.traccar.model.ExtendedModel;
public class PropertiesProvider {
@@ -61,4 +61,21 @@ public class PropertiesProvider {
}
}
+ public Boolean getBoolean(String key) {
+ if (config != null) {
+ if (config.hasKey(key)) {
+ return config.getBoolean(key);
+ } else {
+ return null;
+ }
+ } else {
+ Object result = extendedModel.getAttributes().get(key);
+ if (result != null) {
+ return result instanceof String ? Boolean.valueOf((String) result) : (Boolean) result;
+ } else {
+ return null;
+ }
+ }
+ }
+
}
diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java
new file mode 100644
index 000000000..5e40971c6
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/Notificator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notificators;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.notification.MessageException;
+
+public abstract class Notificator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Notificator.class);
+
+ public void sendAsync(final long userId, final Event event, final Position position) {
+ new Thread(new Runnable() {
+ public void run() {
+ 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;
+
+}
diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java
new file mode 100644
index 000000000..75d325de2
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notificators;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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 javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+
+public class NotificatorFirebase extends Notificator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorFirebase.class);
+
+ private static final String URL = "https://fcm.googleapis.com/fcm/send";
+
+ private String key;
+
+ public static class Notification {
+ @JsonProperty("body")
+ private String body;
+ }
+
+ public static class Message {
+ @JsonProperty("registration_ids")
+ private String[] tokens;
+ @JsonProperty("notification")
+ private Notification notification;
+ }
+
+ public NotificatorFirebase() {
+ key = Context.getConfig().getString("notificator.firebase.key");
+ }
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) {
+ final User user = Context.getPermissionsManager().getUser(userId);
+ if (user.getAttributes().containsKey("notificationTokens")) {
+
+ Notification notification = new Notification();
+ notification.body = NotificationFormatter.formatShortMessage(userId, event, position).trim();
+
+ 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);
+ }
+ });
+ }
+ }
+
+ @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
new file mode 100644
index 000000000..6b9774c58
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorMail.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 - 2018 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.notificators;
+
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.notification.FullMessage;
+import org.traccar.notification.MessageException;
+import org.traccar.notification.NotificationFormatter;
+
+import javax.mail.MessagingException;
+
+public final class NotificatorMail extends Notificator {
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) throws MessageException {
+ try {
+ FullMessage message = NotificationFormatter.formatFullMessage(userId, event, position);
+ Context.getMailManager().sendMessage(userId, message.getSubject(), message.getBody());
+ } catch (MessagingException e) {
+ throw new MessageException(e);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/notificators/NotificatorNull.java b/src/main/java/org/traccar/notificators/NotificatorNull.java
new file mode 100644
index 000000000..9364336be
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorNull.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notificators;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public final class NotificatorNull extends Notificator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorNull.class);
+
+ @Override
+ public void sendAsync(long userId, Event event, Position position) {
+ LOGGER.warn("You are using null notificatior, please check your configuration, notification not sent");
+ }
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) {
+ LOGGER.warn("You are using null notificatior, please check your configuration, notification not sent");
+ }
+
+}
diff --git a/src/main/java/org/traccar/notificators/NotificatorSms.java b/src/main/java/org/traccar/notificators/NotificatorSms.java
new file mode 100644
index 000000000..d5c791eae
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorSms.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 - 2018 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.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.sms.SmsManager;
+
+public final class NotificatorSms extends Notificator {
+
+ private final SmsManager smsManager;
+
+ public NotificatorSms() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ final String smsClass = Context.getConfig().getString("notificator.sms.manager.class", "");
+ if (smsClass.length() > 0) {
+ smsManager = (SmsManager) Class.forName(smsClass).newInstance();
+ } else {
+ smsManager = Context.getSmsManager();
+ }
+ }
+
+ @Override
+ public void sendAsync(long userId, Event event, Position position) {
+ final User user = Context.getPermissionsManager().getUser(userId);
+ if (user.getPhone() != null) {
+ Main.getInjector().getInstance(StatisticsManager.class).registerSms();
+ smsManager.sendMessageAsync(user.getPhone(),
+ NotificationFormatter.formatShortMessage(userId, event, position), false);
+ }
+ }
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) throws MessageException, InterruptedException {
+ final User user = Context.getPermissionsManager().getUser(userId);
+ if (user.getPhone() != null) {
+ Main.getInjector().getInstance(StatisticsManager.class).registerSms();
+ smsManager.sendMessageSync(user.getPhone(),
+ NotificationFormatter.formatShortMessage(userId, event, position), false);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
new file mode 100644
index 000000000..c0b51d043
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 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.notificators;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.notification.NotificationFormatter;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+
+public class NotificatorTelegram extends Notificator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorTelegram.class);
+
+ private String url;
+ private String chatId;
+
+ public static class Message {
+ @JsonProperty("chat_id")
+ private String chatId;
+ @JsonProperty("text")
+ private String text;
+ @JsonProperty("parse_mode")
+ private String parseMode = "html";
+ }
+
+ public NotificatorTelegram() {
+ url = String.format(
+ "https://api.telegram.org/bot%s/sendMessage",
+ Context.getConfig().getString("notificator.telegram.key"));
+ chatId = Context.getConfig().getString("notificator.telegram.chatId");
+ }
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) {
+
+ Message message = new Message();
+ message.chatId = chatId;
+ message.text = NotificationFormatter.formatShortMessage(userId, event, position);
+
+ 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);
+ }
+ });
+ }
+
+ @Override
+ public void sendAsync(long userId, Event event, Position position) {
+ sendSync(userId, event, position);
+ }
+
+}
diff --git a/src/org/traccar/DefaultDataHandler.java b/src/main/java/org/traccar/notificators/NotificatorWeb.java
index e32fe0e7d..1d11c0b46 100644
--- a/src/org/traccar/DefaultDataHandler.java
+++ b/src/main/java/org/traccar/notificators/NotificatorWeb.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,23 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar;
+package org.traccar.notificators;
-import org.traccar.helper.Log;
+import org.traccar.Context;
+import org.traccar.model.Event;
import org.traccar.model.Position;
-public class DefaultDataHandler extends BaseDataHandler {
+public final class NotificatorWeb extends Notificator {
@Override
- protected Position handlePosition(Position position) {
-
- try {
- Context.getDataManager().addPosition(position);
- } catch (Exception error) {
- Log.warning(error);
- }
-
- return position;
+ public void sendSync(long userId, Event event, Position position) {
+ Context.getConnectionManager().updateEvent(userId, event);
}
}
diff --git a/src/main/java/org/traccar/protocol/AdmProtocol.java b/src/main/java/org/traccar/protocol/AdmProtocol.java
new file mode 100644
index 000000000..93b1e355a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AdmProtocol.java
@@ -0,0 +1,44 @@
+/*
+ * 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+import java.nio.ByteOrder;
+
+public class AdmProtocol extends BaseProtocol {
+
+ public AdmProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_GET_DEVICE_STATUS,
+ Command.TYPE_CUSTOM);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 1, -3, 0, true));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new AdmProtocolEncoder(AdmProtocol.this));
+ pipeline.addLast(new AdmProtocolDecoder(AdmProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AdmProtocolDecoder.java b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
index f93c55e18..52d1439ed 100644
--- a/src/org/traccar/protocol/AdmProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,7 +30,7 @@ import java.util.Date;
public class AdmProtocolDecoder extends BaseProtocolDecoder {
- public AdmProtocolDecoder(AdmProtocol protocol) {
+ public AdmProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -38,7 +39,7 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_PHOTO = 0x0A;
public static final int MSG_ADM5 = 0x01;
- private Position decodeData(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int type) {
+ private Position decodeData(Channel channel, SocketAddress remoteAddress, ByteBuf buf, int type) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
@@ -46,30 +47,29 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.to(type, 2) == 0) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
- position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ position.set(Position.KEY_INDEX, buf.readUnsignedShortLE());
- int status = buf.readUnsignedShort();
+ int status = buf.readUnsignedShortLE();
position.set(Position.KEY_STATUS, status);
position.setValid(!BitUtil.check(status, 5));
- position.setLatitude(buf.readFloat());
- position.setLongitude(buf.readFloat());
- position.setCourse(buf.readUnsignedShort() * 0.1);
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+ position.setLatitude(buf.readFloatLE());
+ position.setLongitude(buf.readFloatLE());
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE() * 0.1));
position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte() * 0.1);
- position.setAltitude(buf.readUnsignedShort());
+ position.setAltitude(buf.readShortLE());
position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte() & 0x0f);
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
if (BitUtil.check(type, 2)) {
buf.readUnsignedByte(); // vib
@@ -85,19 +85,19 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(type, 3)) {
for (int i = 1; i <= 6; i++) {
- position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort() * 0.001);
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShortLE() * 0.001);
}
}
if (BitUtil.check(type, 4)) {
for (int i = 1; i <= 2; i++) {
- position.set(Position.PREFIX_COUNT + i, buf.readUnsignedInt());
+ position.set(Position.PREFIX_COUNT + i, buf.readUnsignedIntLE());
}
}
if (BitUtil.check(type, 5)) {
for (int i = 1; i <= 3; i++) {
- buf.readUnsignedShort(); // fuel level
+ buf.readUnsignedShortLE(); // fuel level
}
for (int i = 1; i <= 3; i++) {
position.set(Position.PREFIX_TEMP + i, buf.readUnsignedByte());
@@ -105,11 +105,47 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(type, 6)) {
- buf.skipBytes(buf.getUnsignedByte(buf.readerIndex()));
+ int endIndex = buf.readerIndex() + buf.readUnsignedByte();
+ while (buf.readerIndex() < endIndex) {
+ int mask = buf.readUnsignedByte();
+ long value;
+ switch (BitUtil.from(mask, 6)) {
+ case 3:
+ value = buf.readLongLE();
+ break;
+ case 2:
+ value = buf.readUnsignedIntLE();
+ break;
+ case 1:
+ value = buf.readUnsignedShortLE();
+ break;
+ default:
+ value = buf.readUnsignedByte();
+ break;
+ }
+ int index = BitUtil.to(mask, 6);
+ switch (index) {
+ case 1:
+ position.set(Position.PREFIX_TEMP + 1, value);
+ break;
+ case 2:
+ position.set("humidity", value);
+ break;
+ case 3:
+ position.set("illumination", value);
+ break;
+ case 4:
+ position.set(Position.KEY_BATTERY, value);
+ break;
+ default:
+ position.set("can" + index, value);
+ break;
+ }
+ }
}
if (BitUtil.check(type, 7)) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
}
return position;
@@ -118,14 +154,13 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Position parseCommandResponse(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Position parseCommandResponse(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
@@ -134,22 +169,22 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
if (responseTextLength < 0) {
responseTextLength = CMD_RESPONSE_SIZE - 3;
}
- position.set(Position.KEY_RESULT, buf.readBytes(responseTextLength).toString(StandardCharsets.UTF_8));
+ position.set(Position.KEY_RESULT, buf.readSlice(responseTextLength).toString(StandardCharsets.UTF_8));
return position;
}
@Override
protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- buf.readUnsignedShort(); // device id
+ buf.readUnsignedShortLE(); // device id
int size = buf.readUnsignedByte();
if (size != CMD_RESPONSE_SIZE) {
int type = buf.readUnsignedByte();
if (type == MSG_IMEI) {
- getDeviceSession(channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.UTF_8));
+ getDeviceSession(channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.UTF_8));
} else {
return decodeData(channel, remoteAddress, buf, type);
}
diff --git a/src/org/traccar/protocol/AdmProtocolEncoder.java b/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
index 8cbd8618d..c02fa4112 100644
--- a/src/org/traccar/protocol/AdmProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Anatoliy Golubev (darth.naihil@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,11 +17,15 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class AdmProtocolEncoder extends StringProtocolEncoder {
+ public AdmProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -30,13 +34,11 @@ public class AdmProtocolEncoder extends StringProtocolEncoder {
return formatCommand(command, "STATUS\r\n");
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}\r\n", Command.KEY_DATA);
+ return formatCommand(command, "%s\r\n", Command.KEY_DATA);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
+
}
diff --git a/src/org/traccar/protocol/AisProtocol.java b/src/main/java/org/traccar/protocol/AisProtocol.java
index 4b2e1719e..3b9cad7c8 100644
--- a/src/org/traccar/protocol/AisProtocol.java
+++ b/src/main/java/org/traccar/protocol/AisProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,27 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class AisProtocol extends BaseProtocol {
public AisProtocol() {
- super("ais");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new AisProtocolDecoder(AisProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new AisProtocolDecoder(AisProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/AisProtocolDecoder.java b/src/main/java/org/traccar/protocol/AisProtocolDecoder.java
index 1f7a12595..8970f3d4a 100644
--- a/src/org/traccar/protocol/AisProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AisProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitBuffer;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -34,7 +35,7 @@ import java.util.regex.Pattern;
public class AisProtocolDecoder extends BaseProtocolDecoder {
- public AisProtocolDecoder(AisProtocol protocol) {
+ public AisProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -61,8 +62,7 @@ public class AisProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date());
diff --git a/src/org/traccar/protocol/AlematicsFrameDecoder.java b/src/main/java/org/traccar/protocol/AlematicsFrameDecoder.java
index b8b3e3403..be7666657 100644
--- a/src/org/traccar/protocol/AlematicsFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/AlematicsFrameDecoder.java
@@ -15,10 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import org.traccar.NetworkMessage;
public class AlematicsFrameDecoder extends LineBasedFrameDecoder {
@@ -31,20 +31,20 @@ public class AlematicsFrameDecoder extends LineBasedFrameDecoder {
// example of heartbeat: FA F8 00 07 00 03 15 AD 4E 78 3A D2
@Override
- protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ protected Object decode(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
return null;
}
if (buf.getUnsignedShort(buf.readerIndex()) == 0xFAF8) {
- ChannelBuffer heartbeat = buf.readBytes(12);
- if (channel != null) {
- channel.write(heartbeat);
+ ByteBuf heartbeat = buf.readRetainedSlice(12);
+ if (ctx != null && ctx.channel() != null) {
+ ctx.channel().writeAndFlush(new NetworkMessage(heartbeat, ctx.channel().remoteAddress()));
}
}
- return super.decode(ctx, channel, buf);
+ return super.decode(ctx, buf);
}
}
diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocol.java b/src/main/java/org/traccar/protocol/AlematicsProtocol.java
new file mode 100644
index 000000000..8da2356b9
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AlematicsProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class AlematicsProtocol extends BaseProtocol {
+
+ public AlematicsProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new AlematicsFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new AlematicsProtocolDecoder(AlematicsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AlematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java
index da0704242..25ccf6856 100644
--- a/src/org/traccar/protocol/AlematicsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AlematicsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -29,7 +30,7 @@ import java.util.regex.Pattern;
public class AlematicsProtocolDecoder extends BaseProtocolDecoder {
- public AlematicsProtocolDecoder(AlematicsProtocol protocol) {
+ public AlematicsProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -60,6 +61,8 @@ public class AlematicsProtocolDecoder extends BaseProtocolDecoder {
.or()
.number("(d+),") // extra mask
.expression("(.*)") // extra data
+ .or()
+ .any()
.groupEnd()
.compile();
@@ -113,8 +116,7 @@ public class AlematicsProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_TYPE, parser.nextInt());
position.set(Position.KEY_INDEX, parser.nextInt());
@@ -145,7 +147,7 @@ public class AlematicsProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
position.set("text", parser.next());
- } else {
+ } else if (parser.hasNext()) {
decodeExtras(position, parser);
}
diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocol.java b/src/main/java/org/traccar/protocol/AnytrekProtocol.java
new file mode 100644
index 000000000..4ab5833f7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AnytrekProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class AnytrekProtocol extends BaseProtocol {
+
+ public AnytrekProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, 2, 0));
+ 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
new file mode 100644
index 000000000..c48f59c90
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AnytrekProtocolDecoder.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class AnytrekProtocolDecoder extends BaseProtocolDecoder {
+
+ public AnytrekProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int type) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(0x7878);
+ response.writeShortLE(1 + 1 + 2 + 1 + 2); // length
+ response.writeByte(type);
+ response.writeByte(0); // error
+ response.writeShortLE(0); // report interval
+ response.writeByte(0); // clear alarm
+ response.writeShortLE(0); // checksum
+ response.writeByte('\r');
+ response.writeByte('\n');
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ buf.readUnsignedShortLE(); // size
+ int type = buf.readUnsignedByte();
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(2);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_VERSION_FW, buf.readUnsignedShortLE());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01);
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ position.set(Position.KEY_SATELLITES, BitUtil.to(buf.readUnsignedByte(), 4));
+
+ double latitude = buf.readUnsignedIntLE() / 1800000.0;
+ double longitude = buf.readUnsignedIntLE() / 1800000.0;
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ int flags = buf.readUnsignedShortLE();
+ 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);
+
+ buf.readUnsignedIntLE(); // info index
+ buf.readUnsignedIntLE(); // setting index
+
+ flags = buf.readUnsignedByte();
+ position.set(Position.KEY_CHARGE, BitUtil.check(flags, 0));
+ position.set(Position.KEY_IGNITION, BitUtil.check(flags, 1));
+ position.set(Position.KEY_ALARM, BitUtil.check(flags, 4) ? Position.ALARM_GENERAL : null);
+
+ buf.readUnsignedShortLE(); // charge current
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+
+ sendResponse(channel, remoteAddress, type);
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ApelProtocol.java b/src/main/java/org/traccar/protocol/ApelProtocol.java
new file mode 100644
index 000000000..382aa16af
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ApelProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+public class ApelProtocol extends BaseProtocol {
+
+ public ApelProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true));
+ pipeline.addLast(new ApelProtocolDecoder(ApelProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ApelProtocolDecoder.java b/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java
index 6e16e02be..c95a0366a 100644
--- a/src/org/traccar/protocol/ApelProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ApelProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,17 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedList;
@@ -36,7 +37,7 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder {
private long lastIndex;
private long newIndex;
- public ApelProtocolDecoder(ApelProtocol protocol) {
+ public ApelProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -66,24 +67,24 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder {
public static final short MSG_GPRS_COMMAND = 180;
private void sendSimpleMessage(Channel channel, short type) {
- ChannelBuffer request = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 8);
- request.writeShort(type);
- request.writeShort(0);
- request.writeInt(Checksum.crc32(request.toByteBuffer(0, 4)));
- channel.write(request);
+ ByteBuf request = Unpooled.buffer(8);
+ request.writeShortLE(type);
+ request.writeShortLE(0);
+ request.writeIntLE(Checksum.crc32(request.nioBuffer(0, 4)));
+ channel.writeAndFlush(new NetworkMessage(request, channel.remoteAddress()));
}
private void requestArchive(Channel channel) {
if (lastIndex == 0) {
lastIndex = newIndex;
} else if (newIndex > lastIndex) {
- ChannelBuffer request = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 14);
- request.writeShort(MSG_REQUEST_LOG_RECORDS);
- request.writeShort(6);
- request.writeInt((int) lastIndex);
- request.writeShort(512);
- request.writeInt(Checksum.crc32(request.toByteBuffer(0, 10)));
- channel.write(request);
+ ByteBuf request = Unpooled.buffer(14);
+ request.writeShortLE(MSG_REQUEST_LOG_RECORDS);
+ request.writeShortLE(6);
+ request.writeIntLE((int) lastIndex);
+ request.writeShortLE(512);
+ request.writeIntLE(Checksum.crc32(request.nioBuffer(0, 10)));
+ channel.writeAndFlush(new NetworkMessage(request, channel.remoteAddress()));
}
}
@@ -91,11 +92,11 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
- int type = buf.readUnsignedShort();
+ ByteBuf buf = (ByteBuf) msg;
+ int type = buf.readUnsignedShortLE();
boolean alarm = (type & 0x8000) != 0;
type = type & 0x7FFF;
- buf.readUnsignedShort(); // length
+ buf.readUnsignedShortLE(); // length
if (alarm) {
sendSimpleMessage(channel, MSG_ACK_ALARM);
@@ -107,15 +108,15 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_TRACKER_ID_EXT) {
- buf.readUnsignedInt(); // id
- int length = buf.readUnsignedShort();
+ buf.readUnsignedIntLE(); // id
+ int length = buf.readUnsignedShortLE();
buf.skipBytes(length);
- length = buf.readUnsignedShort();
- getDeviceSession(channel, remoteAddress, buf.readBytes(length).toString(StandardCharsets.US_ASCII));
+ length = buf.readUnsignedShortLE();
+ getDeviceSession(channel, remoteAddress, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
} else if (type == MSG_LAST_LOG_INDEX) {
- long index = buf.readUnsignedInt();
+ long index = buf.readUnsignedIntLE();
if (index > 0) {
newIndex = index;
requestArchive(channel);
@@ -132,31 +133,30 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder {
int recordCount = 1;
if (type == MSG_LOG_RECORDS) {
- recordCount = buf.readUnsignedShort();
+ recordCount = buf.readUnsignedShortLE();
}
for (int j = 0; j < recordCount; j++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
int subtype = type;
if (type == MSG_LOG_RECORDS) {
position.set(Position.KEY_ARCHIVE, true);
- lastIndex = buf.readUnsignedInt() + 1;
+ lastIndex = buf.readUnsignedIntLE() + 1;
position.set(Position.KEY_INDEX, lastIndex);
- subtype = buf.readUnsignedShort();
+ subtype = buf.readUnsignedShortLE();
if (subtype != MSG_CURRENT_GPS_DATA && subtype != MSG_STATE_FULL_INFO_T104) {
- buf.skipBytes(buf.readUnsignedShort());
+ buf.skipBytes(buf.readUnsignedShortLE());
continue;
}
- buf.readUnsignedShort(); // length
+ buf.readUnsignedShortLE(); // length
}
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
- position.setLatitude(buf.readInt() * 180.0 / 0x7FFFFFFF);
- position.setLongitude(buf.readInt() * 180.0 / 0x7FFFFFFF);
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLatitude(buf.readIntLE() * 180.0 / 0x7FFFFFFF);
+ position.setLongitude(buf.readIntLE() * 180.0 / 0x7FFFFFFF);
if (subtype == MSG_STATE_FULL_INFO_T104) {
int speed = buf.readUnsignedByte();
@@ -164,36 +164,36 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(speed));
position.set(Position.KEY_HDOP, buf.readByte());
} else {
- int speed = buf.readShort();
+ int speed = buf.readShortLE();
position.setValid(speed != -1);
position.setSpeed(UnitsConverter.knotsFromKph(speed * 0.01));
}
- position.setCourse(buf.readShort() * 0.01);
- position.setAltitude(buf.readShort());
+ position.setCourse(buf.readShortLE() * 0.01);
+ position.setAltitude(buf.readShortLE());
if (subtype == MSG_STATE_FULL_INFO_T104) {
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- position.set(Position.KEY_EVENT, buf.readUnsignedShort());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
position.set(Position.KEY_INPUT, buf.readUnsignedByte());
position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
for (int i = 1; i <= 8; i++) {
- position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShortLE());
}
- position.set(Position.PREFIX_COUNT + 1, buf.readUnsignedInt());
- position.set(Position.PREFIX_COUNT + 2, buf.readUnsignedInt());
- position.set(Position.PREFIX_COUNT + 3, buf.readUnsignedInt());
+ position.set(Position.PREFIX_COUNT + 1, buf.readUnsignedIntLE());
+ position.set(Position.PREFIX_COUNT + 2, buf.readUnsignedIntLE());
+ position.set(Position.PREFIX_COUNT + 3, buf.readUnsignedIntLE());
}
positions.add(position);
}
- buf.readUnsignedInt(); // crc
+ buf.readUnsignedIntLE(); // crc
if (type == MSG_LOG_RECORDS) {
requestArchive(channel);
diff --git a/src/org/traccar/protocol/AplicomFrameDecoder.java b/src/main/java/org/traccar/protocol/AplicomFrameDecoder.java
index 24d55f1cf..56fca27c7 100644
--- a/src/org/traccar/protocol/AplicomFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/AplicomFrameDecoder.java
@@ -1,62 +1,62 @@
-/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-
-public class AplicomFrameDecoder extends FrameDecoder {
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
-
- // Skip Alive message
- while (buf.readable() && Character.isDigit(buf.getByte(buf.readerIndex()))) {
- buf.readByte();
- }
-
- // Check minimum length
- if (buf.readableBytes() < 11) {
- return null;
- }
-
- // Read flags
- int version = buf.getUnsignedByte(buf.readerIndex() + 1);
- int offset = 1 + 1 + 3;
- if ((version & 0x80) != 0) {
- offset += 4;
- }
-
- // Get data length
- int length = buf.getUnsignedShort(buf.readerIndex() + offset);
- offset += 2;
- if ((version & 0x40) != 0) {
- offset += 3;
- }
- length += offset; // add header
-
- // Return buffer
- if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
- }
-
- return null;
- }
-
-}
+/*
+ * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 AplicomFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ // Skip Alive message
+ while (buf.isReadable() && Character.isDigit(buf.getByte(buf.readerIndex()))) {
+ buf.readByte();
+ }
+
+ // Check minimum length
+ if (buf.readableBytes() < 11) {
+ return null;
+ }
+
+ // Read flags
+ int version = buf.getUnsignedByte(buf.readerIndex() + 1);
+ int offset = 1 + 1 + 3;
+ if ((version & 0x80) != 0) {
+ offset += 4;
+ }
+
+ // Get data length
+ int length = buf.getUnsignedShort(buf.readerIndex() + offset);
+ offset += 2;
+ if ((version & 0x40) != 0) {
+ offset += 3;
+ }
+ length += offset; // add header
+
+ // Return buffer
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/AplicomProtocol.java b/src/main/java/org/traccar/protocol/AplicomProtocol.java
index 80f6f528f..2b9dbf97c 100644
--- a/src/org/traccar/protocol/AplicomProtocol.java
+++ b/src/main/java/org/traccar/protocol/AplicomProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class AplicomProtocol extends BaseProtocol {
public AplicomProtocol() {
- super("aplicom");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new AplicomFrameDecoder());
- pipeline.addLast("objectDecoder", new AplicomProtocolDecoder(AplicomProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new AplicomFrameDecoder());
+ pipeline.addLast(new AplicomProtocolDecoder(AplicomProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java
index 154451b5b..215aa0211 100644
--- a/src/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AplicomProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,14 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+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.Protocol;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -33,7 +35,9 @@ import java.util.Date;
public class AplicomProtocolDecoder extends BaseProtocolDecoder {
- public AplicomProtocolDecoder(AplicomProtocol protocol) {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AplicomProtocolDecoder.class);
+
+ public AplicomProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -82,7 +86,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
private static final int EVENT_DATA = 119;
- private void decodeEventData(Position position, ChannelBuffer buf, int event) {
+ private void decodeEventData(Position position, ByteBuf buf, int event) {
switch (event) {
case 2:
case 40:
@@ -102,6 +106,10 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedInt();
buf.readUnsignedByte();
break;
+ case 119:
+ position.set("eventData", ByteBufUtil.hexDump(
+ buf, buf.readerIndex(), Math.min(buf.readableBytes(), 1024)));
+ break;
case 121:
case 142:
buf.readLong();
@@ -117,7 +125,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeCanData(ChannelBuffer buf, Position position) {
+ private void decodeCanData(ByteBuf buf, Position position) {
buf.readUnsignedMedium(); // packet identifier
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
@@ -128,76 +136,76 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(8);
- ArrayList<ChannelBuffer> values = new ArrayList<>(count);
+ ArrayList<ByteBuf> values = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
- values.add(buf.readBytes(8));
+ values.add(buf.readSlice(8));
}
for (int i = 0; i < count; i++) {
- ChannelBuffer value = values.get(i);
+ ByteBuf value = values.get(i);
switch (buf.readInt()) {
case 0x20D:
- position.set(Position.KEY_RPM, ChannelBuffers.swapShort(value.readShort()));
- position.set("dieselTemperature", ChannelBuffers.swapShort(value.readShort()) * 0.1);
- position.set("batteryVoltage", ChannelBuffers.swapShort(value.readShort()) * 0.01);
- position.set("supplyAirTempDep1", ChannelBuffers.swapShort(value.readShort()) * 0.1);
+ position.set(Position.KEY_RPM, value.readShortLE());
+ position.set("dieselTemperature", value.readShortLE() * 0.1);
+ position.set("batteryVoltage", value.readShortLE() * 0.01);
+ position.set("supplyAirTempDep1", value.readShortLE() * 0.1);
break;
case 0x30D:
- position.set("activeAlarm", ChannelBuffers.hexDump(value));
+ position.set("activeAlarm", ByteBufUtil.hexDump(value));
break;
case 0x40C:
- position.set("airTempDep1", ChannelBuffers.swapShort(value.readShort()) * 0.1);
- position.set("airTempDep2", ChannelBuffers.swapShort(value.readShort()) * 0.1);
+ position.set("airTempDep1", value.readShortLE() * 0.1);
+ position.set("airTempDep2", value.readShortLE() * 0.1);
break;
case 0x40D:
- position.set("coldUnitState", ChannelBuffers.hexDump(value));
+ position.set("coldUnitState", ByteBufUtil.hexDump(value));
break;
case 0x50C:
- position.set("defrostTempDep1", ChannelBuffers.swapShort(value.readShort()) * 0.1);
- position.set("defrostTempDep2", ChannelBuffers.swapShort(value.readShort()) * 0.1);
+ position.set("defrostTempDep1", value.readShortLE() * 0.1);
+ position.set("defrostTempDep2", value.readShortLE() * 0.1);
break;
case 0x50D:
- position.set("condenserPressure", ChannelBuffers.swapShort(value.readShort()) * 0.1);
- position.set("suctionPressure", ChannelBuffers.swapShort(value.readShort()) * 0.1);
+ position.set("condenserPressure", value.readShortLE() * 0.1);
+ position.set("suctionPressure", value.readShortLE() * 0.1);
break;
case 0x58C:
value.readByte();
value.readShort(); // index
switch (value.readByte()) {
case 0x01:
- position.set("setpointZone1", ChannelBuffers.swapInt(value.readInt()) * 0.1);
+ position.set("setpointZone1", value.readIntLE() * 0.1);
break;
case 0x02:
- position.set("setpointZone2", ChannelBuffers.swapInt(value.readInt()) * 0.1);
+ position.set("setpointZone2", value.readIntLE() * 0.1);
break;
case 0x05:
- position.set("unitType", ChannelBuffers.swapInt(value.readInt()));
+ position.set("unitType", value.readIntLE());
break;
case 0x13:
- position.set("dieselHours", ChannelBuffers.swapInt(value.readInt()) / 60 / 60);
+ position.set("dieselHours", value.readIntLE() / 60 / 60);
break;
case 0x14:
- position.set("electricHours", ChannelBuffers.swapInt(value.readInt()) / 60 / 60);
+ position.set("electricHours", value.readIntLE() / 60 / 60);
break;
case 0x17:
- position.set("serviceIndicator", ChannelBuffers.swapInt(value.readInt()));
+ position.set("serviceIndicator", value.readIntLE());
break;
case 0x18:
- position.set("softwareVersion", ChannelBuffers.swapInt(value.readInt()) * 0.01);
+ position.set("softwareVersion", value.readIntLE() * 0.01);
break;
default:
break;
}
break;
default:
- Log.warning(new UnsupportedOperationException());
+ LOGGER.warn("Aplicom CAN decoding error", new UnsupportedOperationException());
break;
}
}
}
- private void decodeD(Position position, ChannelBuffer buf, int selector, int event) {
+ private void decodeD(Position position, ByteBuf buf, int selector, int event) {
if ((selector & 0x0008) != 0) {
position.setValid((buf.readUnsignedByte() & 0x40) != 0);
@@ -206,11 +214,14 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0004) != 0) {
- buf.skipBytes(4); // snapshot time
+ position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
}
if ((selector & 0x0008) != 0) {
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.setFixTime(new Date(buf.readUnsignedInt() * 1000));
+ if (position.getDeviceTime() == null) {
+ position.setDeviceTime(position.getFixTime());
+ }
position.setLatitude(buf.readInt() / 1000000.0);
position.setLongitude(buf.readInt() / 1000000.0);
position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
@@ -292,12 +303,12 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if (Context.getConfig().getBoolean(getProtocolName() + ".can")
- && buf.readable() && (selector & 0x1000) != 0 && event == EVENT_DATA) {
+ && buf.isReadable() && (selector & 0x1000) != 0 && event == EVENT_DATA) {
decodeCanData(buf, position);
}
}
- private void decodeE(Position position, ChannelBuffer buf, int selector) {
+ private void decodeE(Position position, ByteBuf buf, int selector) {
if ((selector & 0x0008) != 0) {
position.set("tachographEvent", buf.readUnsignedShort());
@@ -349,13 +360,13 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0800) != 0) {
- position.set(Position.KEY_VIN, buf.readBytes(18).toString(StandardCharsets.US_ASCII).trim());
+ position.set(Position.KEY_VIN, buf.readSlice(18).toString(StandardCharsets.US_ASCII).trim());
}
if ((selector & 0x2000) != 0) {
buf.readUnsignedByte(); // card 1 type
buf.readUnsignedByte(); // card 1 country code
- String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim();
+ String card = buf.readSlice(20).toString(StandardCharsets.US_ASCII).trim();
if (!card.isEmpty()) {
position.set("card1", card);
}
@@ -364,7 +375,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
if ((selector & 0x4000) != 0) {
buf.readUnsignedByte(); // card 2 type
buf.readUnsignedByte(); // card 2 country code
- String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim();
+ String card = buf.readSlice(20).toString(StandardCharsets.US_ASCII).trim();
if (!card.isEmpty()) {
position.set("card2", card);
}
@@ -373,13 +384,13 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
if ((selector & 0x10000) != 0) {
int count = buf.readUnsignedByte();
for (int i = 1; i <= count; i++) {
- position.set("driver" + i, buf.readBytes(22).toString(StandardCharsets.US_ASCII).trim());
+ position.set("driver" + i, buf.readSlice(22).toString(StandardCharsets.US_ASCII).trim());
position.set("driverTime" + i, buf.readUnsignedInt());
}
}
}
- private void decodeH(Position position, ChannelBuffer buf, int selector) {
+ private void decodeH(Position position, ByteBuf buf, int selector) {
if ((selector & 0x0004) != 0) {
getLastLocation(position, new Date(buf.readUnsignedInt() * 1000));
@@ -457,7 +468,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeEB(Position position, ChannelBuffer buf) {
+ private void decodeEB(Position position, ByteBuf buf) {
if (buf.readByte() != (byte) 'E' || buf.readByte() != (byte) 'B') {
return;
@@ -477,7 +488,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
switch (type) {
case 0x01:
- position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length)));
+ position.set("brakeFlags", ByteBufUtil.hexDump(buf.readSlice(length)));
break;
case 0x02:
position.set("wheelSpeed", buf.readUnsignedShort() / 256.0);
@@ -486,7 +497,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0);
break;
case 0x03:
- position.set("axleLoadSum", buf.readUnsignedShort() * 2);
+ position.set(Position.KEY_AXLE_WEIGHT, buf.readUnsignedShort() * 2);
break;
case 0x04:
position.set("tyrePressure", buf.readUnsignedByte() * 10);
@@ -507,10 +518,10 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set("vdcActiveCounter", buf.readUnsignedShort());
break;
case 0x0B:
- position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length)));
+ position.set("brakeMinMaxData", ByteBufUtil.hexDump(buf.readSlice(length)));
break;
case 0x0C:
- position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length)));
+ position.set("missingPgn", ByteBufUtil.hexDump(buf.readSlice(length)));
break;
case 0x0D:
buf.readUnsignedByte();
@@ -526,14 +537,16 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeF(Position position, ChannelBuffer buf, int selector) {
+ private void decodeF(Position position, ByteBuf buf, int selector) {
- getLastLocation(position, null);
+ Date deviceTime = null;
if ((selector & 0x0004) != 0) {
- buf.skipBytes(4); // snapshot time
+ deviceTime = new Date(buf.readUnsignedInt() * 1000);
}
+ getLastLocation(position, deviceTime);
+
buf.readUnsignedByte(); // data validity
if ((selector & 0x0008) != 0) {
@@ -549,7 +562,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0020) != 0) {
- position.set(Position.KEY_HOURS, buf.readUnsignedInt());
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(buf.readUnsignedInt()));
position.set("serviceDistance", buf.readInt());
position.set("driverActivity", buf.readUnsignedByte());
position.set(Position.KEY_THROTTLE, buf.readUnsignedByte());
@@ -557,7 +570,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0040) != 0) {
- position.set("totalFuelUsed", buf.readUnsignedInt());
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt());
}
if ((selector & 0x0080) != 0) {
@@ -579,12 +592,64 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set("overspeedCount", buf.readUnsignedByte());
}
+ if ((selector & 0x0800) != 0) {
+ position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05);
+ position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125);
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() / 256.0);
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.5);
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4);
+ }
+
+ if ((selector & 0x1000) != 0) {
+ position.set("ambientTemperature", buf.readUnsignedShort() * 0.03125 - 273);
+ buf.readUnsignedShort(); // fuel rate
+ position.set("fuelEconomy", buf.readUnsignedShort() / 512.0);
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt() * 0.001);
+ buf.readUnsignedByte(); // pto drive engagement
+ }
+
+ if ((selector & 0x2000) != 0) {
+ buf.skipBytes(buf.readUnsignedByte()); // driver identification
+ }
+
+ if ((selector & 0x4000) != 0) {
+ position.set("torque", buf.readUnsignedByte());
+ position.set("brakePressure1", buf.readUnsignedByte() * 8);
+ position.set("brakePressure2", buf.readUnsignedByte() * 8);
+ position.set("grossWeight", buf.readUnsignedShort() * 10);
+ position.set("exhaustFluid", buf.readUnsignedByte() * 0.4);
+ buf.readUnsignedByte(); // retarder torque mode
+ position.set("retarderTorque", buf.readUnsignedByte());
+ position.set("retarderSelection", buf.readUnsignedByte() * 0.4);
+ buf.skipBytes(8); // tell tale status block 1
+ buf.skipBytes(8); // tell tale status block 2
+ buf.skipBytes(8); // tell tale status block 3
+ buf.skipBytes(8); // tell tale status block 4
+ }
+
+ if ((selector & 0x8000) != 0) {
+ position.set("parkingBrakeStatus", buf.readUnsignedByte());
+ position.set("doorStatus", buf.readUnsignedByte());
+ buf.skipBytes(8); // status per door
+ position.set("alternatorStatus", buf.readUnsignedByte());
+ position.set("selectedGear", buf.readUnsignedByte());
+ position.set("currentGear", buf.readUnsignedByte());
+ buf.skipBytes(4 * 2); // air suspension pressure
+ }
+
+ if ((selector & 0x0400) != 0) {
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ position.set("axle" + i, buf.readUnsignedShort());
+ }
+ }
+
}
@Override
protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
char protocol = (char) buf.readByte();
int version = buf.readUnsignedByte();
@@ -608,8 +673,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
selector = buf.readUnsignedMedium();
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
diff --git a/src/main/java/org/traccar/protocol/AppelloProtocol.java b/src/main/java/org/traccar/protocol/AppelloProtocol.java
new file mode 100644
index 000000000..1ca4168e4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AppelloProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class AppelloProtocol extends BaseProtocol {
+
+ public AppelloProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new AppelloProtocolDecoder(AppelloProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AppelloProtocolDecoder.java b/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java
index 30dec3941..47e329234 100644
--- a/src/org/traccar/protocol/AppelloProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AppelloProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class AppelloProtocolDecoder extends BaseProtocolDecoder {
- public AppelloProtocolDecoder(AppelloProtocol protocol) {
+ public AppelloProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,8 +66,7 @@ public class AppelloProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.hasNext(6)) {
diff --git a/src/main/java/org/traccar/protocol/AppletProtocol.java b/src/main/java/org/traccar/protocol/AppletProtocol.java
new file mode 100644
index 000000000..2297dca03
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AppletProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class AppletProtocol extends BaseProtocol {
+
+ public AppletProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new AppletProtocolDecoder(AppletProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/AppletProtocolDecoder.java b/src/main/java/org/traccar/protocol/AppletProtocolDecoder.java
new file mode 100644
index 000000000..7a995cc24
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AppletProtocolDecoder.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+
+import java.net.SocketAddress;
+
+public class AppletProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public AppletProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, request.headers().get("From"));
+ if (deviceSession != null) {
+ sendResponse(channel, HttpResponseStatus.OK);
+ } else {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/AquilaProtocol.java b/src/main/java/org/traccar/protocol/AquilaProtocol.java
new file mode 100644
index 000000000..5ca1ec091
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AquilaProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class AquilaProtocol extends BaseProtocol {
+
+ public AquilaProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new AquilaProtocolDecoder(AquilaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AquilaProtocolDecoder.java b/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java
index 773210b04..3c43ddf2a 100644
--- a/src/org/traccar/protocol/AquilaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AquilaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,11 +29,11 @@ import java.util.regex.Pattern;
public class AquilaProtocolDecoder extends BaseProtocolDecoder {
- public AquilaProtocolDecoder(AquilaProtocol protocol) {
+ public AquilaProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
+ private static final Pattern PATTERN_A = new PatternBuilder()
.text("$$")
.expression("[^,]*,") // client
.number("(d+),") // device serial number
@@ -42,6 +43,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)(dd)(dd)") // date (yymmdd)
.number("(dd)(dd)(dd),") // time (hhmmss)
.expression("([AV]),") // validity
+ .groupBegin()
.number("(d+),") // gsm
.number("(d+),") // speed
.number("(d+),") // distance
@@ -120,6 +122,10 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // external voltage
.number("(d+),") // internal voltage
.groupEnd()
+ .or()
+ .number("(d+),") // sensor id
+ .expression("([^,]+),") // sensor data
+ .groupEnd()
.text("*")
.number("xx") // checksum
.compile();
@@ -128,7 +134,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- Parser parser = new Parser(PATTERN, (String) msg);
+ Parser parser = new Parser(PATTERN_A, (String) msg);
if (!parser.matches()) {
return null;
}
@@ -138,8 +144,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_EVENT, parser.nextInt(0));
@@ -151,11 +156,11 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
position.setValid(parser.next().equals("A"));
- position.set(Position.KEY_RSSI, parser.nextInt(0));
-
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
-
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ if (parser.hasNext(3)) {
+ position.set(Position.KEY_RSSI, parser.nextInt(0));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ }
if (parser.hasNext(9)) {
@@ -187,7 +192,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
String dtcs = parser.next();
position.set(Position.KEY_DTCS, dtcs.substring(1, dtcs.length() - 1).replace('|', ' '));
- } else {
+ } else if (parser.hasNext(10)) {
position.setCourse(parser.nextInt(0));
@@ -201,6 +206,11 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, parser.nextInt(0));
position.set(Position.KEY_BATTERY, parser.nextInt(0));
+ } else if (parser.hasNext(2)) {
+
+ position.set("sensorId", parser.nextInt());
+ position.set("sensorData", parser.next());
+
}
return position;
diff --git a/src/main/java/org/traccar/protocol/Ardi01Protocol.java b/src/main/java/org/traccar/protocol/Ardi01Protocol.java
new file mode 100644
index 000000000..f7826430f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Ardi01Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Ardi01Protocol extends BaseProtocol {
+
+ public Ardi01Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Ardi01ProtocolDecoder(Ardi01Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Ardi01ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java
index 7a5bd217c..85e9ecfde 100644
--- a/src/org/traccar/protocol/Ardi01ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Ardi01ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class Ardi01ProtocolDecoder extends BaseProtocolDecoder {
- public Ardi01ProtocolDecoder(Ardi01Protocol protocol) {
+ public Ardi01ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -56,8 +57,7 @@ public class Ardi01ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/ArknavProtocol.java b/src/main/java/org/traccar/protocol/ArknavProtocol.java
new file mode 100644
index 000000000..3b485e4a5
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArknavProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class ArknavProtocol extends BaseProtocol {
+
+ public ArknavProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new ArknavProtocolDecoder(ArknavProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ArknavProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java
index 927c50c29..4982e02fc 100644
--- a/src/org/traccar/protocol/ArknavProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArknavProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class ArknavProtocolDecoder extends BaseProtocolDecoder {
- public ArknavProtocolDecoder(ArknavProtocol protocol) {
+ public ArknavProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -58,8 +59,7 @@ public class ArknavProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
new file mode 100644
index 000000000..a29bc1ad3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class ArknavX8Protocol extends BaseProtocol {
+
+ public ArknavX8Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new ArknavX8ProtocolDecoder(ArknavX8Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ArknavX8ProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java
index 8a80901b5..b570f5423 100644
--- a/src/org/traccar/protocol/ArknavX8ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArknavX8ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,11 +28,11 @@ import java.util.regex.Pattern;
public class ArknavX8ProtocolDecoder extends BaseProtocolDecoder {
- public ArknavX8ProtocolDecoder(ArknavX8Protocol protocol) {
+ public ArknavX8ProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
+ private static final Pattern PATTERN_1G = new PatternBuilder()
.expression("(..),") // type
.number("(dd)(dd)(dd)") // date (yymmdd)
.number("(dd)(dd)(dd),") // time (hhmmss)
@@ -44,6 +45,17 @@ public class ArknavX8ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)") // status
.compile();
+ private static final Pattern PATTERN_2G = new PatternBuilder()
+ .expression("..,") // type
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+),") // satellites
+ .number("(d+.d+),") // altitude
+ .number("(d+.d+),") // power
+ .number("(d+.d+),") // battery
+ .number("(d+.d+)") // odometer
+ .compile();
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -55,7 +67,21 @@ public class ArknavX8ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Parser parser = new Parser(PATTERN, sentence);
+ switch (sentence.substring(0, 2)) {
+ case "1G":
+ case "1R":
+ case "1M":
+ return decode1G(channel, remoteAddress, sentence);
+ case "2G":
+ return decode2G(channel, remoteAddress, sentence);
+ default:
+ return null;
+ }
+ }
+
+ private Position decode1G(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_1G, sentence);
if (!parser.matches()) {
return null;
}
@@ -65,8 +91,7 @@ public class ArknavX8ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_TYPE, parser.next());
@@ -85,4 +110,30 @@ public class ArknavX8ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decode2G(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_2G, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, parser.nextDateTime());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.setAltitude(parser.nextDouble());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1852 / 3600);
+
+ return position;
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
new file mode 100644
index 000000000..afe491865
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class ArnaviProtocol extends BaseProtocol {
+
+ public ArnaviProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new ArnaviProtocolDecoder(ArnaviProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
index 9c4ab5d05..7996cf429 100644
--- a/src/org/traccar/protocol/ArnaviProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
- public ArnaviProtocolDecoder(ArnaviProtocol protocol) {
+ public ArnaviProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -47,6 +48,10 @@ public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
.number("d+,d+,").optional() // input 2
.expression("[01],") // fix type
.number("(d+),") // satellites
+ .groupBegin()
+ .number("(d+.d+)?,") // altitude
+ .number("(?:d+.d+)?,") // geoid height
+ .groupEnd("?")
.number("(dd)(dd)(dd),") // time (hhmmss)
.number("(dd)(dd.d+)([NS]),") // latitude
.number("(ddd)(dd.d+)([EW]),") // longitude
@@ -65,8 +70,7 @@ public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -74,23 +78,25 @@ public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_INDEX, parser.nextInt(0));
- position.set(Position.KEY_POWER, parser.nextInt(0) * 0.01);
- position.set(Position.KEY_BATTERY, parser.nextInt(0) * 0.01);
- position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1);
- position.set(Position.KEY_INPUT, parser.nextInt(0));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
+ position.set(Position.KEY_INDEX, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ position.set(Position.KEY_INPUT, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setAltitude(parser.nextDouble(0));
DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setValid(true);
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
return position;
diff --git a/src/main/java/org/traccar/protocol/AstraProtocol.java b/src/main/java/org/traccar/protocol/AstraProtocol.java
new file mode 100644
index 000000000..12b0dfb68
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AstraProtocol.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class AstraProtocol extends BaseProtocol {
+
+ public AstraProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -3, 0));
+ pipeline.addLast(new AstraProtocolDecoder(AstraProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new AstraProtocolDecoder(AstraProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AstraProtocolDecoder.java b/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java
index 8d86cd2be..e6f546b9f 100644
--- a/src/org/traccar/protocol/AstraProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AstraProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,14 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Log;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -33,7 +36,9 @@ import java.util.List;
public class AstraProtocolDecoder extends BaseProtocolDecoder {
- public AstraProtocolDecoder(AstraProtocol protocol) {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AstraProtocolDecoder.class);
+
+ public AstraProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -44,10 +49,10 @@ public class AstraProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (channel != null) {
- channel.write(ChannelBuffers.wrappedBuffer(new byte[] {0x06}), remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(new byte[] {0x06}), remoteAddress));
}
buf.readUnsignedByte(); // protocol
@@ -63,8 +68,7 @@ public class AstraProtocolDecoder extends BaseProtocolDecoder {
while (buf.readableBytes() > 2) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
buf.readUnsignedByte(); // index
@@ -105,13 +109,13 @@ public class AstraProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // geofence events
if (BitUtil.check(status, 8)) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, buf.readBytes(7).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, buf.readSlice(7).toString(StandardCharsets.US_ASCII));
position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium() * 1000);
- position.set(Position.KEY_HOURS, buf.readUnsignedShort());
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(buf.readUnsignedShort()));
}
if (BitUtil.check(status, 6)) {
- Log.warning("Extension data is not supported");
+ LOGGER.warn("Extension data is not supported");
return position;
}
diff --git a/src/org/traccar/protocol/At2000FrameDecoder.java b/src/main/java/org/traccar/protocol/At2000FrameDecoder.java
index af257d0fb..5fa82a5f7 100644
--- a/src/org/traccar/protocol/At2000FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/At2000FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,38 +15,37 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.NetworkMessage;
-import java.nio.ByteOrder;
-
-public class At2000FrameDecoder extends FrameDecoder {
+public class At2000FrameDecoder extends BaseFrameDecoder {
private static final int BLOCK_LENGTH = 16;
private static final int ACK_LENGTH = 496;
private boolean firstPacket = true;
- private ChannelBuffer currentBuffer;
+ private ByteBuf currentBuffer;
private int acknowledgedBytes;
private void sendResponse(Channel channel) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 2 * BLOCK_LENGTH);
+ ByteBuf response = Unpooled.buffer(2 * BLOCK_LENGTH);
response.writeByte(At2000ProtocolDecoder.MSG_ACKNOWLEDGEMENT);
- response.writeMedium(ChannelBuffers.swapMedium(1));
+ response.writeMedium(1);
response.writeByte(0x00); // success
response.writerIndex(2 * BLOCK_LENGTH);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 5) {
return null;
@@ -55,9 +54,9 @@ public class At2000FrameDecoder extends FrameDecoder {
int length;
if (firstPacket) {
firstPacket = false;
- length = buf.getUnsignedMedium(buf.readerIndex() + 2);
+ length = buf.getUnsignedMediumLE(buf.readerIndex() + 2);
} else {
- length = buf.getUnsignedMedium(buf.readerIndex() + 1);
+ length = buf.getUnsignedMediumLE(buf.readerIndex() + 1);
}
length += BLOCK_LENGTH;
@@ -73,7 +72,7 @@ public class At2000FrameDecoder extends FrameDecoder {
}
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/At2000Protocol.java b/src/main/java/org/traccar/protocol/At2000Protocol.java
new file mode 100644
index 000000000..5894f3eab
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/At2000Protocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class At2000Protocol extends BaseProtocol {
+
+ public At2000Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new At2000FrameDecoder());
+ pipeline.addLast(new At2000ProtocolDecoder(At2000Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/At2000ProtocolDecoder.java b/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java
index 70840c37d..43798eb67 100644
--- a/src/org/traccar/protocol/At2000ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/At2000ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,20 +15,21 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import javax.xml.bind.DatatypeConverter;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedList;
@@ -38,7 +39,7 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder {
private static final int BLOCK_LENGTH = 16;
- public At2000ProtocolDecoder(At2000Protocol protocol) {
+ public At2000ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -52,11 +53,11 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder {
private static void sendRequest(Channel channel) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, BLOCK_LENGTH);
+ ByteBuf response = Unpooled.buffer(BLOCK_LENGTH);
response.writeByte(MSG_TRACK_REQUEST);
- response.writeMedium(ChannelBuffers.swapMedium(0));
+ response.writeMedium(0);
response.writerIndex(BLOCK_LENGTH);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -64,19 +65,19 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (buf.getUnsignedByte(buf.readerIndex()) == 0x01) {
buf.readUnsignedByte(); // codec id
}
int type = buf.readUnsignedByte();
- buf.readUnsignedMedium(); // length
+ buf.readUnsignedMediumLE(); // length
buf.skipBytes(BLOCK_LENGTH - 1 - 3);
if (type == MSG_DEVICE_ID) {
- String imei = buf.readBytes(15).toString(StandardCharsets.US_ASCII);
+ String imei = buf.readSlice(15).toString(StandardCharsets.US_ASCII);
if (getDeviceSession(channel, remoteAddress, imei) != null) {
byte[] iv = new byte[BLOCK_LENGTH];
@@ -84,7 +85,7 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder {
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec keySpec = new SecretKeySpec(
- DatatypeConverter.parseHexBinary("000102030405060708090a0b0c0d0e0f"), "AES");
+ DataConverter.parseHex("000102030405060708090a0b0c0d0e0f"), "AES");
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
@@ -106,52 +107,54 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder {
return null; // empty message
}
- byte[] data = new byte[buf.capacity() - BLOCK_LENGTH];
- buf.readBytes(data);
- buf = ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, cipher.update(data));
-
List<Position> positions = new LinkedList<>();
- while (buf.readableBytes() >= 63) {
+ byte[] data = new byte[buf.capacity() - BLOCK_LENGTH];
+ buf.readBytes(data);
+ buf = Unpooled.wrappedBuffer(cipher.update(data));
+ try {
+ while (buf.readableBytes() >= 63) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
- buf.readUnsignedShort(); // index
- buf.readUnsignedShort(); // reserved
+ buf.readUnsignedShortLE(); // index
+ buf.readUnsignedShortLE(); // reserved
- position.setValid(true);
+ position.setValid(true);
- position.setTime(new Date(buf.readLong() * 1000));
+ position.setTime(new Date(buf.readLongLE() * 1000));
- position.setLatitude(buf.readFloat());
- position.setLongitude(buf.readFloat());
- position.setAltitude(buf.readFloat());
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloat()));
- position.setCourse(buf.readFloat());
+ position.setLatitude(buf.readFloatLE());
+ position.setLongitude(buf.readFloatLE());
+ position.setAltitude(buf.readFloatLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE()));
+ position.setCourse(buf.readFloatLE());
- buf.readUnsignedInt(); // geozone event
- buf.readUnsignedInt(); // io events
- buf.readUnsignedInt(); // geozone value
- buf.readUnsignedInt(); // io values
- buf.readUnsignedShort(); // operator
+ buf.readUnsignedIntLE(); // geozone event
+ buf.readUnsignedIntLE(); // io events
+ buf.readUnsignedIntLE(); // geozone value
+ buf.readUnsignedIntLE(); // io values
+ buf.readUnsignedShortLE(); // operator
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE());
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
- buf.readUnsignedShort(); // cid
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- buf.readUnsignedByte(); // current profile
+ buf.readUnsignedShortLE(); // cid
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // current profile
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
- position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte());
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- positions.add(position);
+ positions.add(position);
+ }
+ } finally {
+ buf.release();
}
return positions;
diff --git a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java
new file mode 100644
index 000000000..f071e2d97
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
+
+import java.nio.charset.StandardCharsets;
+
+public class AtrackFrameDecoder extends BaseFrameDecoder {
+
+ private static final int KEEPALIVE_LENGTH = 12;
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() >= 2) {
+
+ if (buf.getUnsignedShort(buf.readerIndex()) == 0xfe02) {
+
+ if (buf.readableBytes() >= KEEPALIVE_LENGTH) {
+ return buf.readRetainedSlice(KEEPALIVE_LENGTH);
+ }
+
+ } else if (buf.getUnsignedShort(buf.readerIndex()) == 0x4050 && buf.getByte(buf.readerIndex() + 2) != ',') {
+
+ if (buf.readableBytes() > 6) {
+ int length = buf.getUnsignedShort(buf.readerIndex() + 4) + 4 + 2;
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+
+ } else {
+
+ int lengthStart = buf.indexOf(buf.readerIndex() + 3, buf.writerIndex(), (byte) ',') + 1;
+ if (lengthStart > 0) {
+ int lengthEnd = buf.indexOf(lengthStart, buf.writerIndex(), (byte) ',');
+ if (lengthEnd > 0) {
+ int length = lengthEnd + Integer.parseInt(buf.toString(
+ lengthStart, lengthEnd - lengthStart, StandardCharsets.US_ASCII));
+ if (buf.readableBytes() > length && buf.getByte(buf.readerIndex() + length) == '\n') {
+ length += 1;
+ }
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+ } else {
+ int endIndex = BufferUtil.indexOf("\r\n", buf);
+ if (endIndex > 0) {
+ return buf.readRetainedSlice(endIndex - buf.readerIndex() + 2);
+ }
+ }
+
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocol.java b/src/main/java/org/traccar/protocol/AtrackProtocol.java
new file mode 100644
index 000000000..429708b26
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AtrackProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class AtrackProtocol extends BaseProtocol {
+
+ public AtrackProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new AtrackFrameDecoder());
+ pipeline.addLast(new AtrackProtocolEncoder(AtrackProtocol.this));
+ pipeline.addLast(new AtrackProtocolDecoder(AtrackProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..53f04234d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright 2013 - 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+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.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AtrackProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final int MIN_DATA_LENGTH = 40;
+
+ private boolean longDate;
+ private boolean decimalFuel;
+ private boolean custom;
+ private String form;
+
+ private final Map<Integer, String> alarmMap = new HashMap<>();
+
+ public AtrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+
+ longDate = Context.getConfig().getBoolean(getProtocolName() + ".longDate");
+ decimalFuel = Context.getConfig().getBoolean(getProtocolName() + ".decimalFuel");
+
+ custom = Context.getConfig().getBoolean(getProtocolName() + ".custom");
+ form = Context.getConfig().getString(getProtocolName() + ".form");
+ if (form != null) {
+ custom = true;
+ }
+
+ for (String pair : Context.getConfig().getString(getProtocolName() + ".alarmMap", "").split(",")) {
+ if (!pair.isEmpty()) {
+ alarmMap.put(
+ Integer.parseInt(pair.substring(0, pair.indexOf('='))), pair.substring(pair.indexOf('=') + 1));
+ }
+ }
+ }
+
+ public void setLongDate(boolean longDate) {
+ this.longDate = longDate;
+ }
+
+ public void setCustom(boolean custom) {
+ this.custom = custom;
+ }
+
+ public void setForm(String form) {
+ this.form = form;
+ }
+
+ private static void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer(12);
+ response.writeShort(0xfe02);
+ response.writeLong(rawId);
+ response.writeShort(index);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private static String readString(ByteBuf buf) {
+ String result = null;
+ int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
+ if (index > buf.readerIndex()) {
+ result = buf.readSlice(index - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
+ }
+ buf.readByte();
+ return result;
+ }
+
+ private void readTextCustomData(Position position, String data, String form) {
+ CellTower cellTower = new CellTower();
+ String[] keys = form.substring(1).split("%");
+ String[] values = data.split(",|\r\n");
+ for (int i = 0; i < Math.min(keys.length, values.length); i++) {
+ switch (keys[i]) {
+ case "SA":
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[i]));
+ break;
+ case "MV":
+ position.set(Position.KEY_POWER, Integer.parseInt(values[i]) * 0.1);
+ break;
+ case "BV":
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[i]) * 0.1);
+ break;
+ case "GQ":
+ cellTower.setSignalStrength(Integer.parseInt(values[i]));
+ break;
+ case "CE":
+ cellTower.setCellId(Long.parseLong(values[i]));
+ break;
+ case "LC":
+ cellTower.setLocationAreaCode(Integer.parseInt(values[i]));
+ break;
+ case "CN":
+ if (values[i].length() > 3) {
+ cellTower.setMobileCountryCode(Integer.parseInt(values[i].substring(0, 3)));
+ cellTower.setMobileNetworkCode(Integer.parseInt(values[i].substring(3)));
+ }
+ break;
+ case "PC":
+ position.set(Position.PREFIX_COUNT + 1, Integer.parseInt(values[i]));
+ break;
+ case "AT":
+ position.setAltitude(Integer.parseInt(values[i]));
+ break;
+ case "RP":
+ position.set(Position.KEY_RPM, Integer.parseInt(values[i]));
+ break;
+ case "GS":
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[i]));
+ break;
+ case "DT":
+ position.set(Position.KEY_ARCHIVE, Integer.parseInt(values[i]) == 1);
+ break;
+ case "VN":
+ position.set(Position.KEY_VIN, values[i]);
+ break;
+ case "TR":
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(values[i]));
+ break;
+ case "ET":
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[i]));
+ break;
+ case "FL":
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(values[i]));
+ break;
+ case "FC":
+ position.set(Position.KEY_FUEL_CONSUMPTION, Integer.parseInt(values[i]));
+ break;
+ case "AV1":
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[i]));
+ break;
+ case "CD":
+ position.set(Position.KEY_ICCID, values[i]);
+ break;
+ case "EH":
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Integer.parseInt(values[i]) * 0.1));
+ break;
+ case "IA":
+ position.set("intakeTemp", Integer.parseInt(values[i]));
+ break;
+ case "EL":
+ position.set(Position.KEY_ENGINE_LOAD, Integer.parseInt(values[i]));
+ break;
+ case "HA":
+ if (Integer.parseInt(values[i]) > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ }
+ break;
+ case "HB":
+ if (Integer.parseInt(values[i]) > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ }
+ break;
+ case "HC":
+ if (Integer.parseInt(values[i]) > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ }
+ break;
+ case "MT":
+ position.set(Position.KEY_MOTION, Integer.parseInt(values[i]) > 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cellTower.getMobileCountryCode() != null
+ && cellTower.getMobileNetworkCode() != null
+ && cellTower.getCellId() != null
+ && cellTower.getLocationAreaCode() != null) {
+ position.setNetwork(new Network(cellTower));
+ } else if (cellTower.getSignalStrength() != null) {
+ position.set(Position.KEY_RSSI, cellTower.getSignalStrength());
+ }
+ }
+
+ private void readBinaryCustomData(Position position, ByteBuf buf, String form) {
+ CellTower cellTower = new CellTower();
+ String[] keys = form.substring(1).split("%");
+ for (String key : keys) {
+ switch (key) {
+ case "SA":
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case "MV":
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
+ break;
+ case "BV":
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.1);
+ break;
+ case "GQ":
+ cellTower.setSignalStrength((int) buf.readUnsignedByte());
+ break;
+ case "CE":
+ cellTower.setCellId(buf.readUnsignedInt());
+ break;
+ case "LC":
+ cellTower.setLocationAreaCode(buf.readUnsignedShort());
+ break;
+ case "CN":
+ int combinedMobileCodes = (int) (buf.readUnsignedInt() % 100000); // cccnn
+ cellTower.setMobileCountryCode(combinedMobileCodes / 100);
+ cellTower.setMobileNetworkCode(combinedMobileCodes % 100);
+ break;
+ case "RL":
+ buf.readUnsignedByte(); // rxlev
+ break;
+ case "PC":
+ position.set(Position.PREFIX_COUNT + 1, buf.readUnsignedInt());
+ break;
+ case "AT":
+ position.setAltitude(buf.readUnsignedInt());
+ break;
+ case "RP":
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ break;
+ case "GS":
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ break;
+ case "DT":
+ position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() == 1);
+ break;
+ case "VN":
+ position.set(Position.KEY_VIN, readString(buf));
+ break;
+ case "MF":
+ buf.readUnsignedShort(); // mass air flow rate
+ break;
+ case "EL":
+ buf.readUnsignedByte(); // engine load
+ break;
+ case "TR":
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedByte());
+ break;
+ case "ET":
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort());
+ break;
+ case "FL":
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
+ break;
+ case "ML":
+ buf.readUnsignedByte(); // mil status
+ break;
+ case "FC":
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
+ break;
+ case "CI":
+ readString(buf); // format string
+ break;
+ case "AV1":
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ break;
+ case "NC":
+ readString(buf); // gsm neighbor cell info
+ break;
+ case "SM":
+ buf.readUnsignedShort(); // max speed between reports
+ break;
+ case "GL":
+ readString(buf); // google link
+ break;
+ case "MA":
+ readString(buf); // mac address
+ break;
+ case "PD":
+ buf.readUnsignedByte(); // pending code status
+ break;
+ case "CD":
+ position.set(Position.KEY_ICCID, readString(buf));
+ break;
+ case "CM":
+ buf.readLong(); // imsi
+ break;
+ case "GN":
+ buf.skipBytes(60); // g sensor data
+ break;
+ case "GV":
+ buf.skipBytes(6); // maximum g force
+ break;
+ case "ME":
+ buf.readLong(); // imei
+ break;
+ case "IA":
+ buf.readUnsignedByte(); // intake air temperature
+ break;
+ case "MP":
+ buf.readUnsignedByte(); // manifold absolute pressure
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cellTower.getMobileCountryCode() != null
+ && cellTower.getMobileNetworkCode() != null
+ && cellTower.getCellId() != null && cellTower.getCellId() != 0
+ && cellTower.getLocationAreaCode() != null) {
+ position.setNetwork(new Network(cellTower));
+ } else if (cellTower.getSignalStrength() != null) {
+ position.set(Position.KEY_RSSI, cellTower.getSignalStrength());
+ }
+ }
+
+ private static final Pattern PATTERN_INFO = new PatternBuilder()
+ .text("$INFO=")
+ .number("(d+),") // unit id
+ .expression("([^,]+),") // model
+ .expression("([^,]+),") // firmware version
+ .number("d+,") // imei
+ .number("d+,") // imsi
+ .number("d+,") // sim card id
+ .number("(d+),") // power
+ .number("(d+),") // battery
+ .number("(d+),") // satellites
+ .number("d+,") // gsm status
+ .number("(d+),") // rssi
+ .number("d+,") // connection status
+ .number("d+") // antenna status
+ .any()
+ .compile();
+
+ private Position decodeInfo(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Position position = new Position(getProtocolName());
+
+ getLastLocation(position, null);
+
+ DeviceSession deviceSession;
+
+ if (sentence.startsWith("$INFO")) {
+
+ Parser parser = new Parser(PATTERN_INFO, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+
+ position.set("model", parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.next());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.1);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ } else {
+
+ deviceSession = getDeviceSession(channel, remoteAddress);
+
+ position.set(Position.KEY_RESULT, sentence);
+
+ }
+
+ if (deviceSession == null) {
+ return null;
+ } else {
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .number("(d+),") // date and time
+ .number("d+,") // rtc date and time
+ .number("d+,") // device date and time
+ .number("(-?d+),") // longitude
+ .number("(-?d+),") // latitude
+ .number("(d+),") // course
+ .number("(d+),") // report id
+ .number("(d+.?d*),") // odometer
+ .number("(d+),") // hdop
+ .number("(d+),") // inputs
+ .number("(d+),") // speed
+ .number("(d+),") // outputs
+ .number("(d+),") // adc
+ .number("([^,]+)?,") // driver
+ .number("(d+),") // temp1
+ .number("(d+),") // temp2
+ .expression("[^,]*,") // text message
+ .expression("(.*)") // custom data
+ .optional(2)
+ .compile();
+
+ private List<Position> decodeText(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ int startIndex = 0;
+ for (int i = 0; i < 4; i++) {
+ startIndex = sentence.indexOf(',', startIndex + 1);
+ }
+ int endIndex = sentence.indexOf(',', startIndex + 1);
+
+ String imei = sentence.substring(startIndex + 1, endIndex);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+ String[] lines = sentence.substring(endIndex + 1).split("\r\n");
+
+ for (String line : lines) {
+ Position position = decodeTextLine(deviceSession, line);
+ if (position != null) {
+ positions.add(position);
+ }
+ }
+
+ return positions;
+ }
+
+
+ private Position decodeTextLine(DeviceSession deviceSession, String sentence) {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+
+ String time = parser.next();
+ if (time.length() >= 14) {
+ try {
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(time));
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ position.setTime(new Date(Long.parseLong(time) * 1000));
+ }
+
+ position.setLongitude(parser.nextInt() * 0.000001);
+ position.setLatitude(parser.nextInt() * 0.000001);
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.KEY_EVENT, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 100);
+ position.set(Position.KEY_HDOP, parser.nextInt() * 0.1);
+ position.set(Position.KEY_INPUT, parser.nextInt());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+
+ position.set(Position.KEY_OUTPUT, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextInt());
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ }
+
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 2, parser.nextInt());
+
+ if (custom) {
+ String data = parser.next();
+ String form = this.form;
+ if (form == null) {
+ form = data.substring(0, data.indexOf(',')).substring("%CI".length());
+ data = data.substring(data.indexOf(',') + 1);
+ }
+ readTextCustomData(position, data, form);
+ }
+
+ return position;
+ }
+
+ private List<Position> decodeBinary(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ buf.skipBytes(2); // prefix
+ buf.readUnsignedShort(); // checksum
+ buf.readUnsignedShort(); // length
+ int index = buf.readUnsignedShort();
+
+ long id = buf.readLong();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ sendResponse(channel, remoteAddress, id, index);
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.readableBytes() >= MIN_DATA_LENGTH) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (longDate) {
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ buf.skipBytes(7 + 7);
+
+ } else {
+ position.setFixTime(new Date(buf.readUnsignedInt() * 1000));
+ position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
+ buf.readUnsignedInt(); // send time
+ }
+
+ position.setValid(true);
+ position.setLongitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readInt() * 0.000001);
+ position.setCourse(buf.readUnsignedShort());
+
+ int type = buf.readUnsignedByte();
+ position.set(Position.KEY_TYPE, type);
+ position.set(Position.KEY_ALARM, alarmMap.get(type));
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
+ position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001);
+
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, readString(buf));
+
+ position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1);
+ position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1);
+
+ String message = readString(buf);
+ if (message != null && !message.isEmpty()) {
+ Pattern pattern = Pattern.compile("FULS:F=(\\p{XDigit}+) t=(\\p{XDigit}+) N=(\\p{XDigit}+)");
+ Matcher matcher = pattern.matcher(message);
+ if (matcher.find()) {
+ int value = Integer.parseInt(matcher.group(3), decimalFuel ? 10 : 16);
+ position.set(Position.KEY_FUEL_LEVEL, value * 0.1);
+ } else {
+ position.set("message", message);
+ }
+ }
+
+ if (custom) {
+ String form = this.form;
+ if (form == null) {
+ form = readString(buf).trim().substring("%CI".length());
+ }
+ readBinaryCustomData(position, buf, form);
+ }
+
+ positions.add(position);
+
+ }
+
+ return positions;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (buf.getUnsignedShort(buf.readerIndex()) == 0xfe02) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(buf.retain(), remoteAddress)); // keep-alive message
+ }
+ return null;
+ } else if (buf.getByte(buf.readerIndex()) == '$') {
+ return decodeInfo(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim());
+ } else if (buf.getByte(buf.readerIndex() + 2) == ',') {
+ return decodeText(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim());
+ } else {
+ return decodeBinary(channel, remoteAddress, buf);
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/AtrackProtocolEncoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolEncoder.java
index 77f16527f..d803ae391 100644
--- a/src/org/traccar/protocol/AtrackProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,28 +15,29 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
import java.nio.charset.StandardCharsets;
public class AtrackProtocolEncoder extends BaseProtocolEncoder {
+ public AtrackProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return ChannelBuffers.copiedBuffer(
+ return Unpooled.copiedBuffer(
command.getString(Command.KEY_DATA) + "\r\n", StandardCharsets.US_ASCII);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/AuroProtocol.java b/src/main/java/org/traccar/protocol/AuroProtocol.java
new file mode 100644
index 000000000..b8ebdaa75
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AuroProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class AuroProtocol extends BaseProtocol {
+
+ public AuroProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new AuroProtocolDecoder(AuroProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AuroProtocolDecoder.java b/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java
index a45d14709..d7916147b 100644
--- a/src/org/traccar/protocol/AuroProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AuroProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class AuroProtocolDecoder extends BaseProtocolDecoder {
- public AuroProtocolDecoder(AuroProtocol protocol) {
+ public AuroProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -62,8 +63,7 @@ public class AuroProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_INDEX, parser.nextInt(0));
diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocol.java b/src/main/java/org/traccar/protocol/AustinNbProtocol.java
new file mode 100644
index 000000000..32bfc0aae
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AustinNbProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class AustinNbProtocol extends BaseProtocol {
+
+ public AustinNbProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..dc6f3d280
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AustinNbProtocolDecoder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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 {
+
+ public AustinNbProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .number("(d+);") // imei
+ .number("(dddd)-(dd)-(dd) ") // date
+ .number("(dd):(dd):(dd);") // time
+ .number("(-?d+,d+);") // latitude
+ .number("(-?d+,d+);") // longitude
+ .number("(d+);") // azimuth
+ .number("(d+);") // angle
+ .number("(d+);") // range
+ .number("(d+);") // out of range
+ .expression("(.*)") // operator
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS, TimeZone.getDefault().getID()));
+
+ position.setValid(true);
+ position.setLatitude(Double.parseDouble(parser.next().replace(',', '.')));
+ position.setLongitude(Double.parseDouble(parser.next().replace(',', '.')));
+ position.setCourse(parser.nextInt());
+ position.set("angle", parser.nextInt());
+ position.set("range", parser.nextInt());
+ position.set("outOfRange", parser.nextInt());
+ position.set("carrier", parser.next());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/AutoFonFrameDecoder.java b/src/main/java/org/traccar/protocol/AutoFonFrameDecoder.java
index 2fa1b4072..69f28133f 100644
--- a/src/org/traccar/protocol/AutoFonFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/AutoFonFrameDecoder.java
@@ -16,18 +16,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class AutoFonFrameDecoder extends FrameDecoder {
+public class AutoFonFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
// Check minimum length
if (buf.readableBytes() < 12) {
@@ -58,7 +56,7 @@ public class AutoFonFrameDecoder extends FrameDecoder {
// Check length and return buffer
if (length != 0 && buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
return null;
diff --git a/src/org/traccar/protocol/AutoFonProtocol.java b/src/main/java/org/traccar/protocol/AutoFonProtocol.java
index 927bda120..08b5edc7d 100644
--- a/src/org/traccar/protocol/AutoFonProtocol.java
+++ b/src/main/java/org/traccar/protocol/AutoFonProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class AutoFonProtocol extends BaseProtocol {
public AutoFonProtocol() {
- super("autofon");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new AutoFonFrameDecoder());
- pipeline.addLast("objectDecoder", new AutoFonProtocolDecoder(AutoFonProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new AutoFonFrameDecoder());
+ pipeline.addLast(new AutoFonProtocolDecoder(AutoFonProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/AutoFonProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java
index 32b4bfa74..aa05ca2d7 100644
--- a/src/org/traccar/protocol/AutoFonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AutoFonProtocolDecoder.java
@@ -16,11 +16,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.model.CellTower;
@@ -34,7 +37,7 @@ import java.util.List;
public class AutoFonProtocolDecoder extends BaseProtocolDecoder {
- public AutoFonProtocolDecoder(AutoFonProtocol protocol) {
+ public AutoFonProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -60,10 +63,9 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder {
}
}
- private Position decodePosition(DeviceSession deviceSession, ChannelBuffer buf, boolean history) {
+ private Position decodePosition(DeviceSession deviceSession, ByteBuf buf, boolean history) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (!history) {
@@ -119,7 +121,7 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
int type = buf.readUnsignedByte();
@@ -130,14 +132,14 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // software version
}
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession != null && channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeBytes("resp_crc=".getBytes(StandardCharsets.US_ASCII));
response.writeByte(buf.getByte(buf.writerIndex() - 1));
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
return null;
@@ -167,8 +169,7 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_45_LOCATION) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
short status = buf.readUnsignedByte();
diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
new file mode 100644
index 000000000..c6dbb681e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class AutoGradeProtocol extends BaseProtocol {
+
+ public AutoGradeProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new AutoGradeProtocolDecoder(AutoGradeProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AutoGradeProtocolDecoder.java b/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java
index 753509f31..5052450b5 100644
--- a/src/org/traccar/protocol/AutoGradeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AutoGradeProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -29,7 +30,7 @@ import java.util.regex.Pattern;
public class AutoGradeProtocolDecoder extends BaseProtocolDecoder {
- public AutoGradeProtocolDecoder(AutoGradeProtocol protocol) {
+ public AutoGradeProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -72,8 +73,7 @@ public class AutoGradeProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
new file mode 100644
index 000000000..6aa7558bf
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 java.nio.ByteOrder;
+public class AutoTrackProtocol extends BaseProtocol {
+
+ public AutoTrackProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..da7f6b5a6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AutoTrackProtocolDecoder.java
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class AutoTrackProtocolDecoder extends BaseProtocolDecoder {
+
+ public AutoTrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN_REQUEST = 51;
+ public static final int MSG_LOGIN_CONFIRM = 101;
+ public static final int MSG_TELEMETRY_1 = 52;
+ public static final int MSG_TELEMETRY_2 = 66;
+ public static final int MSG_TELEMETRY_3 = 67;
+ public static final int MSG_KEEP_ALIVE = 114;
+ public static final int MSG_TELEMETRY_CONFIRM = 123;
+
+ private Position decodeTelemetry(
+ Channel channel, SocketAddress remoteAddress, DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date(1009843200000L + buf.readUnsignedIntLE() * 1000)); // seconds since 2002
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ buf.readUnsignedShortLE(); // max speed
+
+ position.set(Position.KEY_INPUT, buf.readUnsignedShortLE());
+ buf.readUnsignedIntLE(); // di 3 count
+ buf.readUnsignedIntLE(); // di 4 count
+
+ for (int i = 0; i < 5; i++) {
+ position.set(Position.PREFIX_ADC + (i + 1), buf.readUnsignedShortLE());
+ }
+
+ position.setCourse(buf.readUnsignedShortLE());
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedShortLE());
+ position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, buf.readLongLE());
+
+ int index = buf.readUnsignedShortLE();
+
+ buf.readUnsignedShortLE(); // checksum
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeInt(0xF1F1F1F1); // sync
+ response.writeByte(MSG_TELEMETRY_CONFIRM);
+ response.writeShortLE(2); // length
+ response.writeShortLE(index);
+ response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(4); // sync
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShortLE(); // length
+
+ switch (type) {
+ case MSG_LOGIN_REQUEST:
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8));
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+ int fuelConst = buf.readUnsignedShortLE();
+ int tripConst = buf.readUnsignedShortLE();
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeInt(0xF1F1F1F1); // sync
+ response.writeByte(MSG_LOGIN_CONFIRM);
+ response.writeShortLE(12); // length
+ response.writeBytes(ByteBufUtil.decodeHexDump(imei));
+ response.writeShortLE(fuelConst);
+ response.writeShortLE(tripConst);
+ response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.nioBuffer()));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ return null;
+ case MSG_TELEMETRY_1:
+ case MSG_TELEMETRY_2:
+ case MSG_TELEMETRY_3:
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+ return decodeTelemetry(channel, remoteAddress, deviceSession, buf);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/AvemaProtocol.java b/src/main/java/org/traccar/protocol/AvemaProtocol.java
new file mode 100644
index 000000000..dbfab4dea
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AvemaProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class AvemaProtocol extends BaseProtocol {
+
+ public AvemaProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new AvemaProtocolDecoder(AvemaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java b/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java
new file mode 100644
index 000000000..37836ad5f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/AvemaProtocolDecoder.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018 - 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class AvemaProtocolDecoder extends BaseProtocolDecoder {
+
+ public AvemaProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .number("(d+),") // device id
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(-?d+.d+),") // longitude
+ .number("(-?d+.d+),") // latitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(-?d+),") // altitude
+ .number("(d+),") // satellites
+ .number("(d+),") // event
+ .number("(d+.d+),") // odometer
+ .number("(d+),") // input
+ .number("(d+.d+)V?,") // adc 1
+ .number("(d+.d+)V?,") // adc 2
+ .number("(d+),") // output
+ .number("(d),") // roaming
+ .number("(d+),") // rssi
+ .number("d,") // communication system
+ .number("(ddd)-?") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+),") // cid
+ .number("(d+.d+),").optional() // battery
+ .number("([^,]+)?") // rfid
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(parser.nextDateTime());
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_EVENT, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ position.set(Position.KEY_INPUT, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
+ position.set(Position.KEY_OUTPUT, parser.nextInt());
+ position.set(Position.KEY_ROAMING, parser.nextInt() == 1);
+
+ int rssi = parser.nextInt();
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Avl301Protocol.java b/src/main/java/org/traccar/protocol/Avl301Protocol.java
new file mode 100644
index 000000000..71fc7cb26
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Avl301Protocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Avl301Protocol extends BaseProtocol {
+
+ public Avl301Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, -3, 0));
+ pipeline.addLast(new Avl301ProtocolDecoder(Avl301Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Avl301ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java
index cc61be91b..f6b7db2d6 100644
--- a/src/org/traccar/protocol/Avl301ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Avl301ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
@@ -29,11 +31,11 @@ import java.net.SocketAddress;
public class Avl301ProtocolDecoder extends BaseProtocolDecoder {
- public Avl301ProtocolDecoder(Avl301Protocol protocol) {
+ public Avl301ProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private String readImei(ChannelBuffer buf) {
+ private String readImei(ByteBuf buf) {
int b = buf.readUnsignedByte();
StringBuilder imei = new StringBuilder();
imei.append(b & 0x0F);
@@ -51,12 +53,12 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder {
private void sendResponse(Channel channel, int type) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(5);
+ ByteBuf response = Unpooled.buffer(5);
response.writeByte('$');
response.writeByte(type);
response.writeByte('#');
response.writeByte('\r'); response.writeByte('\n');
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -64,7 +66,7 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(1); // header
int type = buf.readUnsignedByte();
@@ -88,9 +90,8 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
DateBuilder dateBuilder = new DateBuilder()
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
diff --git a/src/org/traccar/protocol/BceFrameDecoder.java b/src/main/java/org/traccar/protocol/BceFrameDecoder.java
index 4fac79f85..381a97696 100644
--- a/src/org/traccar/protocol/BceFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/BceFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,18 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class BceFrameDecoder extends FrameDecoder {
+public class BceFrameDecoder extends BaseFrameDecoder {
private static final int HANDSHAKE_LENGTH = 7; // "#BCE#\r\n"
private boolean header = true;
- private static byte checksum(ChannelBuffer buf, int end) {
+ private static byte checksum(ByteBuf buf, int end) {
byte result = 0;
for (int i = 0; i < end; i++) {
result += buf.getByte(buf.readerIndex() + i);
@@ -36,9 +36,7 @@ public class BceFrameDecoder extends FrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (header && buf.readableBytes() >= HANDSHAKE_LENGTH) {
buf.skipBytes(HANDSHAKE_LENGTH);
@@ -48,10 +46,10 @@ public class BceFrameDecoder extends FrameDecoder {
int end = 8; // IMEI
while (buf.readableBytes() >= end + 2 + 1 + 1 + 1) {
- end += buf.getUnsignedShort(buf.readerIndex() + end) + 2;
+ end += buf.getUnsignedShortLE(buf.readerIndex() + end) + 2;
if (buf.readableBytes() > end && checksum(buf, end) == buf.getByte(buf.readerIndex() + end)) {
- return buf.readBytes(end + 1);
+ return buf.readRetainedSlice(end + 1);
}
}
diff --git a/src/main/java/org/traccar/protocol/BceProtocol.java b/src/main/java/org/traccar/protocol/BceProtocol.java
new file mode 100644
index 000000000..c5e1dd04c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BceProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class BceProtocol extends BaseProtocol {
+
+ public BceProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_OUTPUT_CONTROL);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..c71cdffd9
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
@@ -0,0 +1,336 @@
+/*
+ * 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class BceProtocolDecoder extends BaseProtocolDecoder {
+
+ public BceProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final int DATA_TYPE = 7;
+
+ public static final int MSG_ASYNC_STACK = 0xA5;
+ public static final int MSG_STACK_COFIRM = 0x19;
+ public static final int MSG_TIME_TRIGGERED = 0xA0;
+ public static final int MSG_OUTPUT_CONTROL = 0x41;
+ public static final int MSG_OUTPUT_CONTROL_ACK = 0xC1;
+
+ private void decodeMask1(ByteBuf buf, int mask, Position position) {
+
+ if (BitUtil.check(mask, 0)) {
+ position.setValid(true);
+ position.setLongitude(buf.readFloatLE());
+ position.setLatitude(buf.readFloatLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_SATELLITES, BitUtil.to(status, 4));
+ position.set(Position.KEY_HDOP, BitUtil.from(status, 4));
+
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readUnsignedShortLE());
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ }
+
+ if (BitUtil.check(mask, 1)) {
+ position.set(Position.KEY_INPUT, buf.readUnsignedShortLE());
+ }
+
+ for (int i = 1; i <= 8; i++) {
+ if (BitUtil.check(mask, i + 1)) {
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShortLE());
+ }
+ }
+
+ if (BitUtil.check(mask, 10)) {
+ buf.skipBytes(4);
+ }
+ if (BitUtil.check(mask, 11)) {
+ buf.skipBytes(4);
+ }
+ if (BitUtil.check(mask, 12)) {
+ position.set("fuel1", buf.readUnsignedShort());
+ }
+ if (BitUtil.check(mask, 13)) {
+ position.set("fuel2", buf.readUnsignedShort());
+ }
+
+ if (BitUtil.check(mask, 14)) {
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShortLE(), buf.readUnsignedByte(),
+ buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
+ buf.readUnsignedByte())));
+ buf.readUnsignedByte();
+ }
+ }
+
+ private void decodeMask2(ByteBuf buf, int mask, Position position) {
+
+ if (BitUtil.check(mask, 0)) {
+ buf.readUnsignedShortLE(); // wheel speed
+ }
+ if (BitUtil.check(mask, 1)) {
+ buf.readUnsignedByte(); // acceleration pedal
+ }
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE());
+ }
+ if (BitUtil.check(mask, 3)) {
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
+ }
+ if (BitUtil.check(mask, 4)) {
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(mask, 5)) {
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE());
+ }
+ if (BitUtil.check(mask, 6)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ }
+ if (BitUtil.check(mask, 7)) {
+ position.set(Position.KEY_COOLANT_TEMP, (int) buf.readByte());
+ }
+ if (BitUtil.check(mask, 8)) {
+ position.set("fuel2", buf.readUnsignedByte());
+ }
+ if (BitUtil.check(mask, 9)) {
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte());
+ }
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_ODOMETER_SERVICE, buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(mask, 11)) {
+ buf.skipBytes(8); // sensors
+ }
+ if (BitUtil.check(mask, 12)) {
+ buf.readUnsignedShortLE(); // ambient air temperature
+ }
+ if (BitUtil.check(mask, 13)) {
+ buf.skipBytes(8); // trailer id
+ }
+ if (BitUtil.check(mask, 14)) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShortLE());
+ }
+ }
+
+ private void decodeMask3(ByteBuf buf, int mask, Position position) {
+
+ if (BitUtil.check(mask, 0)) {
+ buf.readUnsignedShortLE(); // fuel economy
+ }
+ if (BitUtil.check(mask, 1)) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE());
+ }
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_AXLE_WEIGHT, buf.readUnsignedMediumLE());
+ }
+ if (BitUtil.check(mask, 3)) {
+ buf.readUnsignedByte(); // mil status
+ }
+ if (BitUtil.check(mask, 4)) {
+ buf.skipBytes(20); // dtc
+ }
+ if (BitUtil.check(mask, 5)) {
+ buf.readUnsignedShortLE();
+ }
+ if (BitUtil.check(mask, 6)) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readLongLE()));
+ }
+ if (BitUtil.check(mask, 7)) {
+ buf.readUnsignedShortLE(); // dallas temperature
+ }
+ if (BitUtil.check(mask, 8)) {
+ buf.readUnsignedShortLE(); // dallas humidity
+ }
+ if (BitUtil.check(mask, 9)) {
+ buf.skipBytes(6); // lls group 1
+ }
+ if (BitUtil.check(mask, 10)) {
+ buf.skipBytes(6); // lls group 2
+ }
+ if (BitUtil.check(mask, 11)) {
+ buf.skipBytes(21); // j1979 group 1
+ }
+ if (BitUtil.check(mask, 12)) {
+ buf.skipBytes(20); // j1979 dtc
+ }
+ if (BitUtil.check(mask, 13)) {
+ buf.skipBytes(9); // j1708 group 1
+ }
+ if (BitUtil.check(mask, 14)) {
+ buf.skipBytes(21); // driving quality
+ }
+ }
+
+ private void decodeMask4(ByteBuf buf, int mask, Position position) {
+
+ if (BitUtil.check(mask, 0)) {
+ buf.readUnsignedIntLE();
+ }
+ if (BitUtil.check(mask, 1)) {
+ buf.skipBytes(30); // lls group 3
+ }
+ if (BitUtil.check(mask, 2)) {
+ buf.readUnsignedIntLE(); // instant fuel consumption
+ }
+ if (BitUtil.check(mask, 3)) {
+ buf.skipBytes(10); // axle weight group
+ }
+ if (BitUtil.check(mask, 4)) {
+ buf.readUnsignedByte();
+ }
+ if (BitUtil.check(mask, 5)) {
+ buf.readUnsignedShortLE();
+ }
+ if (BitUtil.check(mask, 6)) {
+ buf.readUnsignedByte(); // maximum acceleration
+ buf.readUnsignedByte(); // maximum deceleration
+ buf.readUnsignedByte(); // maximum cornering
+ }
+ if (BitUtil.check(mask, 7)) {
+ buf.skipBytes(16);
+ }
+ if (BitUtil.check(mask, 8)) {
+ buf.skipBytes(40); // temperature sensors
+ }
+ if (BitUtil.check(mask, 9)) {
+ position.set("driver1", buf.readCharSequence(16, StandardCharsets.US_ASCII).toString().trim());
+ position.set("driver2", buf.readCharSequence(16, StandardCharsets.US_ASCII).toString().trim());
+ }
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ String imei = String.format("%015d", buf.readLongLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.readableBytes() > 1) {
+
+ int dataEnd = buf.readUnsignedShortLE() + buf.readerIndex();
+ int type = buf.readUnsignedByte();
+
+ if (type != MSG_ASYNC_STACK && type != MSG_TIME_TRIGGERED) {
+ return null;
+ }
+
+ int confirmKey = buf.readUnsignedByte() & 0x7F;
+
+ while (buf.readerIndex() < dataEnd) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int structEnd = buf.readUnsignedByte() + buf.readerIndex();
+
+ long time = buf.readUnsignedIntLE();
+ if ((time & 0x0f) == DATA_TYPE) {
+
+ time = time >> 4 << 1;
+ time += 0x47798280; // 01/01/2008
+ position.setTime(new Date(time * 1000));
+
+ // Read masks
+ int mask;
+ List<Integer> masks = new LinkedList<>();
+ do {
+ mask = buf.readUnsignedShortLE();
+ masks.add(mask);
+ } while (BitUtil.check(mask, 15));
+
+ mask = masks.get(0);
+ decodeMask1(buf, mask, position);
+
+ if (masks.size() >= 2) {
+ mask = masks.get(1);
+ decodeMask2(buf, mask, position);
+ }
+
+ if (masks.size() >= 3) {
+ mask = masks.get(2);
+ decodeMask3(buf, mask, position);
+ }
+
+ if (masks.size() >= 4) {
+ mask = masks.get(3);
+ decodeMask4(buf, mask, position);
+ }
+ }
+
+ buf.readerIndex(structEnd);
+
+ if (position.getValid()) {
+ positions.add(position);
+ } else if (!position.getAttributes().isEmpty()) {
+ getLastLocation(position, null);
+ positions.add(position);
+ }
+ }
+
+ // Send response
+ if (type == MSG_ASYNC_STACK && channel != null) {
+ ByteBuf response = Unpooled.buffer(8 + 2 + 2 + 1);
+ response.writeLongLE(Long.parseLong(imei));
+ response.writeShortLE(2);
+ response.writeByte(MSG_STACK_COFIRM);
+ response.writeByte(confirmKey);
+
+ int checksum = 0;
+ for (int i = 0; i < response.writerIndex(); i++) {
+ checksum += response.getUnsignedByte(i);
+ }
+ response.writeByte(checksum);
+
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BceProtocolEncoder.java b/src/main/java/org/traccar/protocol/BceProtocolEncoder.java
new file mode 100644
index 000000000..53ac51581
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BceProtocolEncoder.java
@@ -0,0 +1,52 @@
+/*
+ * 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class BceProtocolEncoder extends BaseProtocolEncoder {
+
+ public BceProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ if (command.getType().equals(Command.TYPE_OUTPUT_CONTROL)) {
+ ByteBuf buf = Unpooled.buffer();
+
+ buf.writeLongLE(Long.parseLong(getUniqueId(command.getDeviceId())));
+ buf.writeShortLE(1 + 1 + 3 + 1); // length
+ buf.writeByte(BceProtocolDecoder.MSG_OUTPUT_CONTROL);
+ buf.writeByte(command.getInteger(Command.KEY_INDEX) == 1 ? 0x0A : 0x0B);
+ buf.writeByte(0xFF); // index
+ buf.writeByte(0x00); // form id
+ buf.writeShortLE(Integer.parseInt(command.getString(Command.KEY_DATA)) > 0 ? 0x0055 : 0x0000);
+ buf.writeByte(Checksum.sum(buf.nioBuffer()));
+
+ return buf;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
new file mode 100644
index 000000000..617a24d7a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Vijay Kumar (vijaykumar@zilogic.com)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class BlackKiteProtocol extends BaseProtocol {
+
+ public BlackKiteProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new GalileoFrameDecoder());
+ pipeline.addLast(new BlackKiteProtocolDecoder(BlackKiteProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/BlackKiteProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java
index 561d7c18b..474ceabdc 100644
--- a/src/org/traccar/protocol/BlackKiteProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BlackKiteProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
* Copyright 2015 Vijay Kumar (vijaykumar@zilogic.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +16,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashSet;
@@ -35,7 +36,7 @@ import java.util.Set;
public class BlackKiteProtocolDecoder extends BaseProtocolDecoder {
- public BlackKiteProtocolDecoder(BlackKiteProtocol protocol) {
+ public BlackKiteProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -55,12 +56,12 @@ public class BlackKiteProtocolDecoder extends BaseProtocolDecoder {
private static final int TAG_XT2 = 0x61;
private static final int TAG_XT3 = 0x62;
- private void sendReply(Channel channel, int checksum) {
- ChannelBuffer reply = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 3);
- reply.writeByte(0x02);
- reply.writeShort((short) checksum);
+ private void sendResponse(Channel channel, int checksum) {
if (channel != null) {
- channel.write(reply);
+ ByteBuf reply = Unpooled.buffer(3);
+ reply.writeByte(0x02);
+ reply.writeShortLE((short) checksum);
+ channel.writeAndFlush(new NetworkMessage(reply, channel.remoteAddress()));
}
}
@@ -68,16 +69,15 @@ public class BlackKiteProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedByte(); // header
- int length = (buf.readUnsignedShort() & 0x7fff) + 3;
+ int length = (buf.readUnsignedShortLE() & 0x7fff) + 3;
List<Position> positions = new LinkedList<>();
Set<Integer> tags = new HashSet<>();
boolean hasLocation = false;
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
while (buf.readerIndex() < length) {
@@ -89,38 +89,38 @@ public class BlackKiteProtocolDecoder extends BaseProtocolDecoder {
}
tags.clear();
hasLocation = false;
- position = new Position();
+ position = new Position(getProtocolName());
}
tags.add(tag);
switch (tag) {
case TAG_IMEI:
- getDeviceSession(channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
+ getDeviceSession(channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII));
break;
case TAG_DATE:
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
break;
case TAG_COORDINATES:
hasLocation = true;
position.setValid((buf.readUnsignedByte() & 0xf0) == 0x00);
- position.setLatitude(buf.readInt() / 1000000.0);
- position.setLongitude(buf.readInt() / 1000000.0);
+ position.setLatitude(buf.readIntLE() / 1000000.0);
+ position.setLongitude(buf.readIntLE() / 1000000.0);
break;
case TAG_SPEED_COURSE:
- position.setSpeed(buf.readUnsignedShort() * 0.0539957);
- position.setCourse(buf.readUnsignedShort() * 0.1);
+ position.setSpeed(buf.readUnsignedShortLE() * 0.0539957);
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
break;
case TAG_ALTITUDE:
- position.setAltitude(buf.readShort());
+ position.setAltitude(buf.readShortLE());
break;
case TAG_STATUS:
- int status = buf.readUnsignedShort();
+ int status = buf.readUnsignedShortLE();
position.set(Position.KEY_IGNITION, BitUtil.check(status, 9));
if (BitUtil.check(status, 15)) {
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
@@ -129,33 +129,33 @@ public class BlackKiteProtocolDecoder extends BaseProtocolDecoder {
break;
case TAG_DIGITAL_INPUTS:
- int input = buf.readUnsignedShort();
+ int input = buf.readUnsignedShortLE();
for (int i = 0; i < 16; i++) {
position.set(Position.PREFIX_IO + (i + 1), BitUtil.check(input, i));
}
break;
case TAG_DIGITAL_OUTPUTS:
- int output = buf.readUnsignedShort();
+ int output = buf.readUnsignedShortLE();
for (int i = 0; i < 16; i++) {
position.set(Position.PREFIX_IO + (i + 17), BitUtil.check(output, i));
}
break;
case TAG_INPUT_VOLTAGE1:
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() / 1000.0);
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE() / 1000.0);
break;
case TAG_INPUT_VOLTAGE2:
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort() / 1000.0);
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE() / 1000.0);
break;
case TAG_INPUT_VOLTAGE3:
- position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShort() / 1000.0);
+ position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE() / 1000.0);
break;
case TAG_INPUT_VOLTAGE4:
- position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShort() / 1000.0);
+ position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShortLE() / 1000.0);
break;
case TAG_XT1:
@@ -179,7 +179,7 @@ public class BlackKiteProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- sendReply(channel, buf.readUnsignedShort());
+ sendResponse(channel, buf.readUnsignedShortLE());
for (Position p : positions) {
p.setDeviceId(deviceSession.getDeviceId());
diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java
new file mode 100644
index 000000000..d5dc5c421
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BlueProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class BlueProtocol extends BaseProtocol {
+
+ public BlueProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..61edcd101
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2019 - 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.protocol;
+
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class BlueProtocolDecoder extends BaseProtocolDecoder {
+
+ public BlueProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private double readCoordinate(ByteBuf buf, boolean negative) {
+
+ int value = buf.readUnsignedShort();
+ int degrees = value / 100;
+ double minutes = value % 100 + buf.readUnsignedShort() * 0.0001;
+ double coordinate = degrees + minutes / 60;
+ return negative ? -coordinate : coordinate;
+ }
+
+ private void sendResponse(Channel channel, int deviceIndex) {
+ if (channel != null) {
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xaa);
+ response.writeShort(2 + 1 + 1 + 6 + 1);
+ response.writeByte(0x86); // version
+ response.writeByte(0);
+
+ response.writeByte(6); // data length
+ response.writeByte(0xa4); // type
+ response.writeByte(0); // server index
+ response.writeByte(deviceIndex);
+ response.writeByte(0);
+ response.writeByte(0);
+
+ response.writeByte(Checksum.xor(response.nioBuffer(1, response.writerIndex() - 1)));
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // version
+ buf.readUnsignedByte();
+
+ String id = String.valueOf(buf.readUnsignedInt());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ while (buf.readableBytes() > 1) {
+
+ int frameEnd = buf.readerIndex() + buf.readUnsignedByte();
+
+ int type = buf.readUnsignedByte();
+ int index = buf.readUnsignedByte();
+ buf.readUnsignedByte();
+ buf.readUnsignedByte(); // flags
+
+ if (type == 0x01) {
+
+ buf.readUnsignedByte(); // reserved
+ int flags = buf.readUnsignedByte();
+
+ position.setValid(BitUtil.check(flags, 7));
+ position.setLatitude(readCoordinate(buf, BitUtil.check(flags, 6)));
+ position.setLongitude(readCoordinate(buf, BitUtil.check(flags, 5)));
+ position.setSpeed(buf.readUnsignedShort() + buf.readUnsignedShort() * 0.001);
+ position.setCourse(buf.readUnsignedShort() + buf.readUnsignedByte() * 0.01);
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ buf.readUnsignedShort(); // lac
+ buf.readUnsignedShort(); // cid
+
+ } else if (type == 0x12) {
+
+ int status;
+
+ status = buf.readUnsignedByte(); // status 1
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 1) ? Position.ALARM_VIBRATION : null);
+
+ buf.readUnsignedByte(); // status 2
+ buf.readUnsignedByte(); // status 3
+ buf.readUnsignedByte(); // status 4
+ buf.readUnsignedByte(); // status 5
+ buf.readUnsignedByte(); // status 6
+
+ } else if (type == 0x84) {
+
+ sendResponse(channel, index);
+
+ }
+
+ buf.readerIndex(frameEnd);
+ }
+
+ return position.getFixTime() != null ? position : null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BoxProtocol.java b/src/main/java/org/traccar/protocol/BoxProtocol.java
new file mode 100644
index 000000000..dfea15938
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BoxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class BoxProtocol extends BaseProtocol {
+
+ public BoxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new BoxProtocolDecoder(BoxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/BoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
index 562731657..853fa8f81 100644
--- a/src/org/traccar/protocol/BoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BoxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +31,7 @@ import java.util.regex.Pattern;
public class BoxProtocolDecoder extends BaseProtocolDecoder {
- public BoxProtocolDecoder(BoxProtocol protocol) {
+ public BoxProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -44,7 +47,10 @@ public class BoxProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*),") // distance
.number("(d+),") // event
.number("(d+)") // status
- .any()
+ .groupBegin()
+ .text(";")
+ .expression("(.+)")
+ .groupEnd("?")
.compile();
@Override
@@ -62,7 +68,7 @@ public class BoxProtocolDecoder extends BaseProtocolDecoder {
} else if (sentence.startsWith("E,")) {
if (channel != null) {
- channel.write("A," + sentence.substring(2) + "\r");
+ channel.writeAndFlush(new NetworkMessage("A," + sentence.substring(2) + "\r", remoteAddress));
}
} else if (sentence.startsWith("L,")) {
@@ -77,24 +83,33 @@ public class BoxProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
position.setTime(parser.nextDateTime());
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
- position.set(Position.KEY_ODOMETER_TRIP, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_ODOMETER_TRIP, parser.nextDouble() * 1000);
position.set(Position.KEY_EVENT, parser.next());
- int status = parser.nextInt(0);
- position.setValid((status & 0x04) == 0);
+ int status = parser.nextInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_MOTION, BitUtil.check(status, 1));
+ position.setValid(!BitUtil.check(status, 2));
position.set(Position.KEY_STATUS, status);
+ if (parser.hasNext()) {
+ String[] data = parser.next().split(";");
+ for (String item : data) {
+ int valueIndex = item.indexOf(',');
+ position.set(item.substring(0, valueIndex).toLowerCase(), item.substring(valueIndex + 1));
+ }
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/C2stekProtocol.java b/src/main/java/org/traccar/protocol/C2stekProtocol.java
new file mode 100644
index 000000000..804621fd3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/C2stekProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class C2stekProtocol extends BaseProtocol {
+
+ public C2stekProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, false, "$AP"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new C2stekProtocolDecoder(C2stekProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
new file mode 100644
index 000000000..6a31cb2f4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+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 C2stekProtocolDecoder extends BaseProtocolDecoder {
+
+ public C2stekProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("PA$")
+ .number("(d+)") // imei
+ .text("$")
+ .expression(".#") // data type
+ .number("(dd)(dd)(dd)#") // date (yymmdd)
+ .number("(dd)(dd)(dd)#") // time (hhmmss)
+ .number("([01])#") // valid
+ .number("([+-]?d+.d+)#") // latitude
+ .number("([+-]?d+.d+)#") // longitude
+ .number("(d+.d+)#") // speed
+ .number("(d+.d+)#") // course
+ .number("(-?d+.d+)#") // altitude
+ .number("(d+)#") // battery
+ .number("d+#") // geo area alarm
+ .number("(x+)#") // alarm
+ .number("([01])") // armed
+ .number("([01])") // door
+ .number("([01])#") // ignition
+ .any()
+ .text("$AP")
+ .compile();
+
+ private String decodeAlarm(int alarm) {
+ switch (alarm) {
+ case 0x2:
+ return Position.ALARM_SHOCK;
+ case 0x3:
+ return Position.ALARM_POWER_CUT;
+ case 0x4:
+ return Position.ALARM_OVERSPEED;
+ case 0x5:
+ return Position.ALARM_SOS;
+ case 0x6:
+ return Position.ALARM_DOOR;
+ case 0xA:
+ return Position.ALARM_LOW_BATTERY;
+ case 0xB:
+ return Position.ALARM_FAULT;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ if (sentence.contains("$20$") && channel != null) {
+ channel.writeAndFlush(new NetworkMessage(sentence, remoteAddress));
+ }
+
+ 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.setTime(parser.nextDateTime());
+ position.setValid(parser.nextInt() > 0);
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+
+ 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);
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/CalAmpProtocol.java b/src/main/java/org/traccar/protocol/CalAmpProtocol.java
index a3577f10c..232e72a8c 100644
--- a/src/org/traccar/protocol/CalAmpProtocol.java
+++ b/src/main/java/org/traccar/protocol/CalAmpProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,25 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class CalAmpProtocol extends BaseProtocol {
public CalAmpProtocol() {
- super("calamp");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new CalAmpProtocolDecoder(CalAmpProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CalAmpProtocolDecoder(CalAmpProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/CalAmpProtocolDecoder.java b/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java
index 557e2d6e6..dad0ae774 100644
--- a/src/org/traccar/protocol/CalAmpProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CalAmpProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,11 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,7 +32,7 @@ import java.util.Date;
public class CalAmpProtocolDecoder extends BaseProtocolDecoder {
- public CalAmpProtocolDecoder(CalAmpProtocol protocol) {
+ public CalAmpProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -52,7 +55,7 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder {
private void sendResponse(Channel channel, SocketAddress remoteAddress, int type, int index, int result) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(10);
+ ByteBuf response = Unpooled.buffer(10);
response.writeByte(SERVICE_RESPONSE);
response.writeByte(MSG_ACK);
response.writeShort(index);
@@ -60,15 +63,14 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder {
response.writeByte(result);
response.writeByte(0);
response.writeMedium(0);
- channel.write(response, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
- private Position decodePosition(DeviceSession deviceSession, int type, ChannelBuffer buf) {
+ private Position decodePosition(DeviceSession deviceSession, int type, ByteBuf buf) {
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
position.setTime(new Date(buf.readUnsignedInt() * 1000));
if (type != MSG_MINI_EVENT_REPORT) {
@@ -144,14 +146,14 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (BitUtil.check(buf.getByte(buf.readerIndex()), 7)) {
int content = buf.readUnsignedByte();
if (BitUtil.check(content, 0)) {
- String id = ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedByte()));
+ String id = ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())).replace("f", "");
getDeviceSession(channel, remoteAddress, id);
}
diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocol.java b/src/main/java/org/traccar/protocol/CarTrackProtocol.java
new file mode 100644
index 000000000..e340fba25
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CarTrackProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class CarTrackProtocol extends BaseProtocol {
+
+ public CarTrackProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new CarTrackProtocolDecoder(CarTrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/CarTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java
index 3064bbb35..ce3345826 100644
--- a/src/org/traccar/protocol/CarTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CarTrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
* Copyright 2014 Rohit
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,9 +16,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -29,7 +30,7 @@ import java.util.regex.Pattern;
public class CarTrackProtocolDecoder extends BaseProtocolDecoder {
- public CarTrackProtocolDecoder(CarTrackProtocol protocol) {
+ public CarTrackProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,8 +66,7 @@ public class CarTrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/CarcellProtocol.java b/src/main/java/org/traccar/protocol/CarcellProtocol.java
index c9fedad65..f08ab3bd9 100644
--- a/src/org/traccar/protocol/CarcellProtocol.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,36 +15,28 @@
*/
package org.traccar.protocol;
-import java.util.List;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+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.model.Command;
public class CarcellProtocol extends BaseProtocol {
public CarcellProtocol() {
- super("carcell");
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new CarcellProtocolEncoder());
- pipeline.addLast("objectDecoder", new CarcellProtocolDecoder(CarcellProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\r'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new CarcellProtocolEncoder(CarcellProtocol.this));
+ pipeline.addLast(new CarcellProtocolDecoder(CarcellProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/CarcellProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java
index fc5710f09..ec640ba71 100644
--- a/src/org/traccar/protocol/CarcellProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -18,9 +18,10 @@ package org.traccar.protocol;
import java.net.SocketAddress;
import java.util.regex.Pattern;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.Parser.CoordinateFormat;
import org.traccar.helper.PatternBuilder;
@@ -29,7 +30,7 @@ import org.traccar.model.Position;
public class CarcellProtocolDecoder extends BaseProtocolDecoder {
- public CarcellProtocolDecoder(CarcellProtocol protocol) {
+ public CarcellProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -85,8 +86,7 @@ public class CarcellProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_ARCHIVE, parser.next().equals("%"));
position.setValid(true);
@@ -155,7 +155,7 @@ public class CarcellProtocolDecoder extends BaseProtocolDecoder {
Double mainVoltage = parser.nextDouble(0) / 100d;
position.set(Position.KEY_POWER, mainVoltage);
- position.set("iccid", parser.next());
+ position.set(Position.KEY_ICCID, parser.next());
}
return position;
diff --git a/src/org/traccar/protocol/CarcellProtocolEncoder.java b/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
index 0846949c4..78dbe7e91 100644
--- a/src/org/traccar/protocol/CarcellProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -16,25 +16,26 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class CarcellProtocolEncoder extends StringProtocolEncoder {
+ public CarcellProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "$SRVCMD,{%s},BA#\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "$SRVCMD,%s,BA#\r\n", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "$SRVCMD,{%s},BD#\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "$SRVCMD,%s,BD#\r\n", Command.KEY_UNIQUE_ID);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/CarscopProtocol.java b/src/main/java/org/traccar/protocol/CarscopProtocol.java
new file mode 100644
index 000000000..2c754a97f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CarscopProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class CarscopProtocol extends BaseProtocol {
+
+ public CarscopProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '^'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new CarscopProtocolDecoder(CarscopProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/CarscopProtocolDecoder.java b/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java
index 2a081bcdd..161666adc 100644
--- a/src/org/traccar/protocol/CarscopProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CarscopProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class CarscopProtocolDecoder extends BaseProtocolDecoder {
- public CarscopProtocolDecoder(CarscopProtocol protocol) {
+ public CarscopProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -73,9 +74,8 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
DateBuilder dateBuilder = new DateBuilder()
.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
diff --git a/src/main/java/org/traccar/protocol/CastelProtocol.java b/src/main/java/org/traccar/protocol/CastelProtocol.java
new file mode 100644
index 000000000..44c52d68f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CastelProtocol.java
@@ -0,0 +1,48 @@
+/*
+ * 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+import java.nio.ByteOrder;
+public class CastelProtocol extends BaseProtocol {
+
+ public CastelProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..23401b5ee
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
@@ -0,0 +1,609 @@
+/*
+ * 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.ObdDecoder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class CastelProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final Map<Integer, Integer> PID_LENGTH_MAP = new HashMap<>();
+
+ static {
+ int[] l1 = {
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0b, 0x0d,
+ 0x0e, 0x0f, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x2c,
+ 0x2d, 0x2e, 0x2f, 0x30, 0x33, 0x43, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x51, 0x52,
+ 0x5a
+ };
+ int[] l2 = {
+ 0x02, 0x03, 0x0a, 0x0c, 0x10, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1f, 0x21, 0x22,
+ 0x23, 0x31, 0x32, 0x3c, 0x3d, 0x3e, 0x3f, 0x42,
+ 0x44, 0x4d, 0x4e, 0x50, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59
+ };
+ int[] l4 = {
+ 0x00, 0x01, 0x20, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x40, 0x41, 0x4f
+ };
+ for (int i : l1) {
+ PID_LENGTH_MAP.put(i, 1);
+ }
+ for (int i : l2) {
+ PID_LENGTH_MAP.put(i, 2);
+ }
+ for (int i : l4) {
+ PID_LENGTH_MAP.put(i, 4);
+ }
+ }
+
+ public CastelProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final short MSG_SC_LOGIN = 0x1001;
+ public static final short MSG_SC_LOGIN_RESPONSE = (short) 0x9001;
+ public static final short MSG_SC_LOGOUT = 0x1002;
+ public static final short MSG_SC_HEARTBEAT = 0x1003;
+ public static final short MSG_SC_HEARTBEAT_RESPONSE = (short) 0x9003;
+ public static final short MSG_SC_GPS = 0x4001;
+ public static final short MSG_SC_PID_DATA = 0x4002;
+ public static final short MSG_SC_G_SENSOR = 0x4003;
+ public static final short MSG_SC_SUPPORTED_PID = 0x4004;
+ public static final short MSG_SC_OBD_DATA = 0x4005;
+ public static final short MSG_SC_DTCS_PASSENGER = 0x4006;
+ public static final short MSG_SC_DTCS_COMMERCIAL = 0x400B;
+ public static final short MSG_SC_ALARM = 0x4007;
+ public static final short MSG_SC_CELL = 0x4008;
+ public static final short MSG_SC_GPS_SLEEP = 0x4009;
+ public static final short MSG_SC_FUEL = 0x400E;
+ public static final short MSG_SC_AGPS_REQUEST = 0x5101;
+ public static final short MSG_SC_QUERY_RESPONSE = (short) 0xA002;
+ public static final short MSG_SC_CURRENT_LOCATION = (short) 0xB001;
+
+ public static final short MSG_CC_LOGIN = 0x4001;
+ public static final short MSG_CC_LOGIN_RESPONSE = (short) 0x8001;
+ public static final short MSG_CC_HEARTBEAT = 0x4206;
+ public static final short MSG_CC_PETROL_CONTROL = 0x4583;
+ public static final short MSG_CC_HEARTBEAT_RESPONSE = (short) 0x8206;
+
+ private Position readPosition(DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ double lat = buf.readUnsignedIntLE() / 3600000.0;
+ double lon = buf.readUnsignedIntLE() / 3600000.0;
+ position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
+
+ int flags = buf.readUnsignedByte();
+ if ((flags & 0x02) == 0) {
+ lat = -lat;
+ }
+ if ((flags & 0x01) == 0) {
+ lon = -lon;
+ }
+ position.setLatitude(lat);
+ position.setLongitude(lon);
+ position.setValid((flags & 0x0C) > 0);
+ position.set(Position.KEY_SATELLITES, flags >> 4);
+
+ return position;
+ }
+
+ private Position createPosition(DeviceSession deviceSession) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ return position;
+ }
+
+ private void decodeObd(Position position, ByteBuf buf, boolean groups) {
+
+ int count = buf.readUnsignedByte();
+
+ int[] pids = new int[count];
+ for (int i = 0; i < count; i++) {
+ pids[i] = buf.readUnsignedShortLE() & 0xff;
+ }
+
+ if (groups) {
+ buf.readUnsignedByte(); // group count
+ buf.readUnsignedByte(); // group size
+ }
+
+ for (int i = 0; i < count; i++) {
+ int value;
+ switch (PID_LENGTH_MAP.get(pids[i])) {
+ case 1:
+ value = buf.readUnsignedByte();
+ break;
+ case 2:
+ value = buf.readUnsignedShortLE();
+ break;
+ case 4:
+ value = buf.readIntLE();
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ position.add(ObdDecoder.decodeData(pids[i], value, false));
+ }
+ }
+
+ private void decodeStat(Position position, ByteBuf buf) {
+
+ buf.readUnsignedIntLE(); // ACC ON time
+ buf.readUnsignedIntLE(); // UTC time
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedIntLE());
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE());
+ buf.readUnsignedShortLE(); // current fuel consumption
+
+ long state = buf.readUnsignedIntLE();
+ position.set(Position.KEY_ALARM, BitUtil.check(state, 4) ? Position.ALARM_ACCELERATION : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(state, 5) ? Position.ALARM_BRAKING : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(state, 6) ? Position.ALARM_IDLE : null);
+ position.set(Position.KEY_IGNITION, BitUtil.check(state, 2 * 8 + 2));
+ position.set(Position.KEY_STATUS, state);
+
+ buf.skipBytes(8);
+ }
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress,
+ int version, ByteBuf id, short type, ByteBuf content) {
+
+ if (channel != null) {
+ int length = 2 + 2 + 1 + id.readableBytes() + 2 + 2 + 2;
+ if (content != null) {
+ length += content.readableBytes();
+ }
+
+ ByteBuf response = Unpooled.buffer(length);
+ response.writeByte('@'); response.writeByte('@');
+ response.writeShortLE(length);
+ response.writeByte(version);
+ response.writeBytes(id);
+ response.writeShort(type);
+ if (content != null) {
+ response.writeBytes(content);
+ content.release();
+ }
+ response.writeShortLE(
+ Checksum.crc16(Checksum.CRC16_X25, response.nioBuffer(0, response.writerIndex())));
+ response.writeByte(0x0D); response.writeByte(0x0A);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, ByteBuf id, short type) {
+
+ if (channel != null) {
+ int length = 2 + 2 + id.readableBytes() + 2 + 4 + 8 + 2 + 2;
+
+ ByteBuf response = Unpooled.buffer(length);
+ response.writeByte('@'); response.writeByte('@');
+ response.writeShortLE(length);
+ response.writeBytes(id);
+ response.writeShort(type);
+ response.writeIntLE(0);
+ for (int i = 0; i < 8; i++) {
+ response.writeByte(0xff);
+ }
+ response.writeShortLE(
+ Checksum.crc16(Checksum.CRC16_X25, response.nioBuffer(0, response.writerIndex())));
+ response.writeByte(0x0D); response.writeByte(0x0A);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ private void decodeAlarm(Position position, int alarm) {
+ switch (alarm) {
+ case 0x01:
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ case 0x02:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ break;
+ case 0x03:
+ position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
+ break;
+ case 0x04:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 0x05:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 0x06:
+ position.set(Position.KEY_ALARM, Position.ALARM_IDLE);
+ break;
+ case 0x07:
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ break;
+ case 0x08:
+ position.set(Position.KEY_ALARM, Position.ALARM_HIGH_RPM);
+ break;
+ case 0x09:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
+ break;
+ case 0x0B:
+ position.set(Position.KEY_ALARM, Position.ALARM_LANE_CHANGE);
+ break;
+ case 0x0C:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ case 0x0E:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
+ break;
+ case 0x16:
+ position.set(Position.KEY_IGNITION, true);
+ break;
+ case 0x17:
+ position.set(Position.KEY_IGNITION, false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private Object decodeSc(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf,
+ int version, ByteBuf id, short type, DeviceSession deviceSession) {
+
+ Position position;
+ int count;
+
+ switch (type) {
+
+ case MSG_SC_HEARTBEAT:
+ sendResponse(channel, remoteAddress, version, id, MSG_SC_HEARTBEAT_RESPONSE, null);
+ return null;
+
+ case MSG_SC_LOGIN:
+ case MSG_SC_LOGOUT:
+ case MSG_SC_GPS:
+ case MSG_SC_ALARM:
+ case MSG_SC_CURRENT_LOCATION:
+ case MSG_SC_FUEL:
+ if (type == MSG_SC_LOGIN) {
+ ByteBuf response = Unpooled.buffer(10);
+ response.writeIntLE(0xFFFFFFFF);
+ response.writeShortLE(0);
+ response.writeIntLE((int) (System.currentTimeMillis() / 1000));
+ sendResponse(channel, remoteAddress, version, id, MSG_SC_LOGIN_RESPONSE, response);
+ }
+
+ if (type == MSG_SC_GPS) {
+ buf.readUnsignedByte(); // historical
+ } else if (type == MSG_SC_ALARM) {
+ buf.readUnsignedIntLE(); // alarm
+ } else if (type == MSG_SC_CURRENT_LOCATION) {
+ buf.readUnsignedShortLE();
+ }
+
+ buf.readUnsignedIntLE(); // ACC ON time
+ buf.readUnsignedIntLE(); // UTC time
+ long odometer = buf.readUnsignedIntLE();
+ long tripOdometer = buf.readUnsignedIntLE();
+ long fuelConsumption = buf.readUnsignedIntLE();
+ buf.readUnsignedShortLE(); // current fuel consumption
+ long status = buf.readUnsignedIntLE();
+ buf.skipBytes(8);
+
+ count = buf.readUnsignedByte();
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < count; i++) {
+ position = readPosition(deviceSession, buf);
+ position.set(Position.KEY_ODOMETER, odometer);
+ position.set(Position.KEY_ODOMETER_TRIP, tripOdometer);
+ position.set(Position.KEY_FUEL_CONSUMPTION, fuelConsumption);
+ position.set(Position.KEY_STATUS, status);
+ positions.add(position);
+ }
+
+ if (type == MSG_SC_ALARM) {
+ int alarmCount = buf.readUnsignedByte();
+ for (int i = 0; i < alarmCount; i++) {
+ if (buf.readUnsignedByte() != 0) {
+ int alarm = buf.readUnsignedByte();
+ for (Position p : positions) {
+ decodeAlarm(p, alarm);
+ }
+ buf.readUnsignedShortLE(); // description
+ buf.readUnsignedShortLE(); // threshold
+ }
+ }
+ } else if (type == MSG_SC_FUEL) {
+ for (Position p : positions) {
+ p.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE());
+ }
+ }
+
+ return positions.isEmpty() ? null : positions;
+
+ case MSG_SC_GPS_SLEEP:
+ buf.readUnsignedIntLE(); // device time
+ return readPosition(deviceSession, buf);
+
+ case MSG_SC_AGPS_REQUEST:
+ return readPosition(deviceSession, buf);
+
+ case MSG_SC_PID_DATA:
+ position = createPosition(deviceSession);
+
+ decodeStat(position, buf);
+
+ buf.readUnsignedShortLE(); // sample rate
+ decodeObd(position, buf, true);
+
+ return position;
+
+ case MSG_SC_G_SENSOR:
+ position = createPosition(deviceSession);
+
+ decodeStat(position, buf);
+
+ buf.readUnsignedShortLE(); // sample rate
+
+ count = buf.readUnsignedByte();
+
+ StringBuilder data = new StringBuilder("[");
+ for (int i = 0; i < count; i++) {
+ if (i > 0) {
+ data.append(",");
+ }
+ data.append("[");
+ data.append(buf.readShortLE() * 0.015625);
+ data.append(",");
+ data.append(buf.readShortLE() * 0.015625);
+ data.append(",");
+ data.append(buf.readShortLE() * 0.015625);
+ data.append("]");
+ }
+ data.append("]");
+
+ position.set(Position.KEY_G_SENSOR, data.toString());
+
+ return position;
+
+ case MSG_SC_DTCS_PASSENGER:
+ position = createPosition(deviceSession);
+
+ decodeStat(position, buf);
+
+ buf.readUnsignedByte(); // flag
+ position.add(ObdDecoder.decodeCodes(ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte()))));
+
+ return position;
+
+ case MSG_SC_OBD_DATA:
+ position = createPosition(deviceSession);
+
+ decodeStat(position, buf);
+
+ buf.readUnsignedByte(); // flag
+ decodeObd(position, buf, false);
+
+ return position;
+
+ case MSG_SC_CELL:
+ position = createPosition(deviceSession);
+
+ decodeStat(position, buf);
+
+ position.setNetwork(new Network(
+ CellTower.fromLacCid(buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
+
+ return position;
+
+ case MSG_SC_QUERY_RESPONSE:
+ position = createPosition(deviceSession);
+
+ buf.readUnsignedShortLE(); // index
+ buf.readUnsignedByte(); // response count
+ buf.readUnsignedByte(); // response index
+
+ int failureCount = buf.readUnsignedByte();
+ for (int i = 0; i < failureCount; i++) {
+ buf.readUnsignedShortLE(); // tag
+ }
+
+ int successCount = buf.readUnsignedByte();
+ for (int i = 0; i < successCount; i++) {
+ buf.readUnsignedShortLE(); // tag
+ position.set(Position.KEY_RESULT,
+ buf.readSlice(buf.readUnsignedShortLE()).toString(StandardCharsets.US_ASCII));
+ }
+
+ return position;
+
+ default:
+ return null;
+
+ }
+ }
+
+ private Object decodeCc(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf,
+ int version, ByteBuf id, short type, DeviceSession deviceSession) {
+
+ if (type == MSG_CC_HEARTBEAT) {
+
+ sendResponse(channel, remoteAddress, version, id, MSG_CC_HEARTBEAT_RESPONSE, null);
+
+ buf.readUnsignedByte(); // 0x01 for history
+ int count = buf.readUnsignedByte();
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < count; i++) {
+ Position position = readPosition(deviceSession, buf);
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedIntLE());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+
+ buf.readUnsignedByte(); // geo-fencing id
+ buf.readUnsignedByte(); // geo-fencing flags
+ buf.readUnsignedByte(); // additional flags
+
+ position.setNetwork(new Network(
+ CellTower.fromLacCid(buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
+
+ positions.add(position);
+ }
+
+ return positions;
+
+ } else if (type == MSG_CC_LOGIN) {
+
+ sendResponse(channel, remoteAddress, version, id, MSG_CC_LOGIN_RESPONSE, null);
+
+ Position position = readPosition(deviceSession, buf);
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedIntLE());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+
+ buf.readUnsignedByte(); // geo-fencing id
+ buf.readUnsignedByte(); // geo-fencing flags
+ buf.readUnsignedByte(); // additional flags
+
+ // GSM_CELL_CODE
+ // STR_Z - firmware version
+ // STR_Z - hardware version
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+ private Object decodeMpip(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf,
+ int version, ByteBuf id, short type, DeviceSession deviceSession) {
+
+ if (type == 0x4001) {
+
+ sendResponse(channel, remoteAddress, version, id, (short) type, null);
+
+ return readPosition(deviceSession, buf);
+
+ } else if (type == 0x2001) {
+
+ sendResponse(channel, remoteAddress, id, (short) 0x1001);
+
+ buf.readUnsignedIntLE(); // index
+ buf.readUnsignedIntLE(); // unix time
+ buf.readUnsignedByte();
+
+ return readPosition(deviceSession, buf);
+
+ } else if (type == 0x4201 || type == 0x4202 || type == 0x4206) {
+
+ return readPosition(deviceSession, buf);
+
+ } else if (type == 0x4204) {
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < 8; i++) {
+ Position position = readPosition(deviceSession, buf);
+ buf.skipBytes(31);
+ positions.add(position);
+ }
+
+ return positions;
+
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int header = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // length
+
+ int version = -1;
+ if (header == 0x4040) {
+ version = buf.readUnsignedByte();
+ }
+
+ ByteBuf id = buf.readSlice(20);
+ short type = buf.readShort();
+
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, id.toString(StandardCharsets.US_ASCII).trim());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ switch (version) {
+ case -1:
+ return decodeMpip(channel, remoteAddress, buf, version, id, type, deviceSession);
+ case 3:
+ case 4:
+ return decodeSc(channel, remoteAddress, buf, version, id, type, deviceSession);
+ default:
+ return decodeCc(channel, remoteAddress, buf, version, id, type, deviceSession);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java b/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java
new file mode 100644
index 000000000..dc694da28
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CastelProtocolEncoder.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 - 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.Context;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import java.nio.charset.StandardCharsets;
+
+public class CastelProtocolEncoder extends BaseProtocolEncoder {
+
+ public CastelProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(long deviceId, short type, ByteBuf content) {
+
+ ByteBuf buf = Unpooled.buffer(0);
+ String uniqueId = Context.getIdentityManager().getById(deviceId).getUniqueId();
+
+ buf.writeByte('@');
+ buf.writeByte('@');
+
+ buf.writeShortLE(2 + 2 + 1 + 20 + 2 + content.readableBytes() + 2 + 2); // length
+
+ buf.writeByte(1); // protocol version
+
+ buf.writeBytes(uniqueId.getBytes(StandardCharsets.US_ASCII));
+ buf.writeZero(20 - uniqueId.length());
+
+ buf.writeShort(type);
+ buf.writeBytes(content);
+
+ buf.writeShortLE(Checksum.crc16(Checksum.CRC16_X25, buf.nioBuffer()));
+
+ buf.writeByte('\r');
+ buf.writeByte('\n');
+
+ return buf;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ ByteBuf content = Unpooled.buffer(0);
+ switch (command.getType()) {
+ case Command.TYPE_ENGINE_STOP:
+ content.writeByte(1);
+ return encodeContent(command.getDeviceId(), CastelProtocolDecoder.MSG_CC_PETROL_CONTROL, content);
+ case Command.TYPE_ENGINE_RESUME:
+ content.writeByte(0);
+ return encodeContent(command.getDeviceId(), CastelProtocolDecoder.MSG_CC_PETROL_CONTROL, content);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/CautelaProtocol.java b/src/main/java/org/traccar/protocol/CautelaProtocol.java
new file mode 100644
index 000000000..452bdf8d4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CautelaProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class CautelaProtocol extends BaseProtocol {
+
+ public CautelaProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new CautelaProtocolDecoder(CautelaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java
new file mode 100644
index 000000000..bddf19b41
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CautelaProtocolDecoder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+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 CautelaProtocolDecoder extends BaseProtocolDecoder {
+
+ public CautelaProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .number("(d+),") // type
+ .number("(d+),") // imei
+ .number("(dd),(dd),(dd),") // date (ddmmyy)
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+),") // longitude
+ .number("(dd)(dd),") // time (hhmm)
+ .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;
+ }
+
+ parser.next(); // type
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder();
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(true);
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+
+ dateBuilder.setHour(parser.nextInt()).setMinute(parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/CellocatorFrameDecoder.java b/src/main/java/org/traccar/protocol/CellocatorFrameDecoder.java
index b4708f5db..ee2adde6d 100644
--- a/src/org/traccar/protocol/CellocatorFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/CellocatorFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,29 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.Log;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class CellocatorFrameDecoder extends FrameDecoder {
+public class CellocatorFrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_MINIMUM_LENGTH = 15;
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- // Check minimum length
- int available = buf.readableBytes();
- if (available < MESSAGE_MINIMUM_LENGTH) {
+ if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
return null;
}
- // Size depending on message type
int length = 0;
int type = buf.getUnsignedByte(4);
switch (type) {
@@ -51,21 +45,22 @@ public class CellocatorFrameDecoder extends FrameDecoder {
length = 70;
break;
case CellocatorProtocolDecoder.MSG_CLIENT_SERIAL:
- if (available >= 19) {
- length = 19 + buf.getUnsignedShort(16);
+ if (buf.readableBytes() >= 19) {
+ length = 19 + buf.getUnsignedShortLE(buf.readerIndex() + 16);
}
break;
case CellocatorProtocolDecoder.MSG_CLIENT_MODULAR:
- length = 15 + buf.getUnsignedByte(13);
+ length = 15 + buf.getUnsignedByte(buf.readerIndex() + 13);
+ break;
+ case CellocatorProtocolDecoder.MSG_CLIENT_MODULAR_EXT:
+ length = 16 + buf.getUnsignedShortLE(buf.readerIndex() + 13);
break;
default:
- Log.warning(new UnsupportedOperationException(String.valueOf(type)));
break;
}
- // Read packet
- if (length > 0 && available >= length) {
- return buf.readBytes(length);
+ if (length > 0 && buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocol.java b/src/main/java/org/traccar/protocol/CellocatorProtocol.java
new file mode 100644
index 000000000..d910877cf
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class CellocatorProtocol extends BaseProtocol {
+
+ public CellocatorProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_OUTPUT_CONTROL);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CellocatorFrameDecoder());
+ pipeline.addLast(new CellocatorProtocolEncoder(CellocatorProtocol.this));
+ pipeline.addLast(new CellocatorProtocolDecoder(CellocatorProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..aa13a0aa2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2013 - 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class CellocatorProtocolDecoder extends BaseProtocolDecoder {
+
+ public CellocatorProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ static final int MSG_CLIENT_STATUS = 0;
+ static final int MSG_CLIENT_PROGRAMMING = 3;
+ static final int MSG_CLIENT_SERIAL_LOG = 7;
+ static final int MSG_CLIENT_SERIAL = 8;
+ static final int MSG_CLIENT_MODULAR = 9;
+ static final int MSG_CLIENT_MODULAR_EXT = 11;
+
+ public static final int MSG_SERVER_ACKNOWLEDGE = 4;
+
+ public static ByteBuf encodeContent(int type, int uniqueId, int packetNumber, ByteBuf content) {
+
+ ByteBuf buf = Unpooled.buffer();
+ buf.writeByte('M');
+ buf.writeByte('C');
+ buf.writeByte('G');
+ buf.writeByte('P');
+ buf.writeByte(type);
+ buf.writeIntLE(uniqueId);
+ buf.writeByte(packetNumber);
+ buf.writeIntLE(0); // authentication code
+ buf.writeBytes(content);
+
+ byte checksum = 0;
+ for (int i = 4; i < buf.writerIndex(); i++) {
+ checksum += buf.getByte(i);
+ }
+ buf.writeByte(checksum);
+
+ return buf;
+ }
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, long deviceId, byte packetNumber) {
+ if (channel != null) {
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(0);
+ content.writeByte(packetNumber);
+ content.writeZero(11);
+
+ ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, packetNumber, content);
+ channel.writeAndFlush(new NetworkMessage(reply, remoteAddress));
+ }
+ }
+
+ private void sendModuleResponse(Channel channel, SocketAddress remoteAddress, long deviceId, byte packetNumber) {
+ if (channel != null) {
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(0x80);
+ content.writeShortLE(10); // modules length
+ content.writeIntLE(0); // reserved
+ content.writeByte(9); // ack module type
+ content.writeShortLE(3); // module length
+ content.writeByte(0); // ack
+ content.writeShortLE(0); // reserved
+
+ ByteBuf reply = encodeContent(MSG_CLIENT_MODULAR_EXT, (int) deviceId, packetNumber, content);
+ channel.writeAndFlush(new NetworkMessage(reply, remoteAddress));
+ }
+ }
+
+ private String decodeAlarm(short reason) {
+ switch (reason) {
+ case 70:
+ return Position.ALARM_SOS;
+ case 80:
+ return Position.ALARM_POWER_CUT;
+ case 81:
+ return Position.ALARM_LOW_POWER;
+ default:
+ return null;
+ }
+ }
+
+ private Position decodeStatus(ByteBuf buf, DeviceSession deviceSession, boolean alternative) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_VERSION_HW, buf.readUnsignedByte());
+ position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // protocol version
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedByte() & 0x0f);
+
+ buf.readUnsignedByte(); // operator / configuration flags
+ buf.readUnsignedByte(); // reason data
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+
+ position.set("mode", buf.readUnsignedByte());
+ position.set(Position.KEY_INPUT, buf.readUnsignedIntLE());
+
+ if (alternative) {
+ buf.readUnsignedByte(); // input
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE());
+ } else {
+ buf.readUnsignedByte(); // operator
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedIntLE());
+ }
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedMediumLE());
+
+ buf.skipBytes(6); // multi-purpose data
+ buf.readUnsignedShortLE(); // fix time
+ buf.readUnsignedByte(); // location status
+ buf.readUnsignedByte(); // mode 1
+ buf.readUnsignedByte(); // mode 2
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ position.setValid(true);
+
+ if (alternative) {
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+ } else {
+ position.setLongitude(buf.readIntLE() / Math.PI * 180 / 100000000);
+ position.setLatitude(buf.readIntLE() / Math.PI * 180 / 100000000);
+ }
+
+ position.setAltitude(buf.readIntLE() * 0.01);
+
+ if (alternative) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedIntLE()));
+ position.setCourse(buf.readUnsignedShortLE() / 1000.0);
+ } else {
+ position.setSpeed(UnitsConverter.knotsFromMps(buf.readUnsignedIntLE() * 0.01));
+ position.setCourse(buf.readUnsignedShortLE() / Math.PI * 180.0 / 1000.0);
+ }
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTimeReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedShortLE());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+ }
+
+ private Position decodeModular(ByteBuf buf, DeviceSession deviceSession) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedByte(); // packet control
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // reserved
+ buf.readUnsignedShortLE(); // reserved
+
+ while (buf.readableBytes() > 3) {
+
+ int moduleType = buf.readUnsignedByte();
+ int endIndex = buf.readUnsignedShortLE() + buf.readerIndex();
+
+ switch (moduleType) {
+ case 2:
+ buf.readUnsignedShortLE(); // operator id
+ buf.readUnsignedIntLE(); // pl signature
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ int id = buf.readUnsignedShortLE();
+ buf.readUnsignedByte(); // variable length
+ position.set(Position.PREFIX_IO + id, buf.readUnsignedIntLE());
+ }
+ break;
+ case 6:
+ buf.readUnsignedByte(); // hdop
+ buf.readUnsignedByte(); // mode 1
+ buf.readUnsignedByte(); // mode 2
+ buf.readUnsignedByte(); // satellites
+ position.setLongitude(buf.readIntLE() / Math.PI * 180 / 100000000);
+ position.setLatitude(buf.readIntLE() / Math.PI * 180 / 100000000);
+ position.setAltitude(buf.readIntLE() * 0.01);
+ position.setSpeed(UnitsConverter.knotsFromMps(buf.readUnsignedByte() * 0.01));
+ position.setCourse(buf.readUnsignedShortLE() / Math.PI * 180.0 / 1000.0);
+ break;
+ case 7:
+ buf.readUnsignedByte(); // valid
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTimeReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+ break;
+ default:
+ break;
+ }
+
+ buf.readerIndex(endIndex);
+
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ boolean alternative = buf.getByte(buf.readerIndex() + 3) != 'P';
+
+ buf.skipBytes(4); // system code
+ int type = buf.readUnsignedByte();
+
+ long deviceUniqueId = buf.readUnsignedIntLE();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceUniqueId));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (type != MSG_CLIENT_SERIAL) {
+ buf.readUnsignedShortLE(); // communication control
+ }
+ byte packetNumber = buf.readByte();
+
+ if (type == MSG_CLIENT_MODULAR_EXT) {
+ sendModuleResponse(channel, remoteAddress, deviceUniqueId, packetNumber);
+ } else {
+ sendResponse(channel, remoteAddress, deviceUniqueId, packetNumber);
+ }
+
+ if (type == MSG_CLIENT_STATUS) {
+ return decodeStatus(buf, deviceSession, alternative);
+ } else if (type == MSG_CLIENT_MODULAR_EXT) {
+ return decodeModular(buf, deviceSession);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/CellocatorProtocolEncoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolEncoder.java
index bb143d349..76fa67686 100644
--- a/src/org/traccar/protocol/CellocatorProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,34 +15,30 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
-
-import java.nio.ByteOrder;
+import org.traccar.Protocol;
public class CellocatorProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeContent(long deviceId, int command, int data1, int data2) {
+ public CellocatorProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static ByteBuf encodeContent(int type, int uniqueId, int packetNumber, ByteBuf content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
+ ByteBuf buf = Unpooled.buffer();
buf.writeByte('M');
buf.writeByte('C');
buf.writeByte('G');
buf.writeByte('P');
- buf.writeByte(0);
- buf.writeInt(Integer.parseInt(getUniqueId(deviceId)));
- buf.writeByte(0); // command numerator
- buf.writeInt(0); // authentication code
- buf.writeByte(command);
- buf.writeByte(command);
- buf.writeByte(data1);
- buf.writeByte(data1);
- buf.writeByte(data2);
- buf.writeByte(data2);
- buf.writeInt(0); // command specific data
+ buf.writeByte(type);
+ buf.writeIntLE(uniqueId);
+ buf.writeByte(packetNumber);
+ buf.writeIntLE(0); // authentication code
+ buf.writeBytes(content);
byte checksum = 0;
for (int i = 4; i < buf.writerIndex(); i++) {
@@ -53,6 +49,23 @@ public class CellocatorProtocolEncoder extends BaseProtocolEncoder {
return buf;
}
+ private ByteBuf encodeCommand(long deviceId, int command, int data1, int data2) {
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(command);
+ content.writeByte(command);
+ content.writeByte(data1);
+ content.writeByte(data1);
+ content.writeByte(data2);
+ content.writeByte(data2);
+ content.writeIntLE(0); // command specific data
+
+ ByteBuf buf = encodeContent(0, Integer.parseInt(getUniqueId(deviceId)), 0, content);
+ content.release();
+
+ return buf;
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -60,13 +73,10 @@ public class CellocatorProtocolEncoder extends BaseProtocolEncoder {
case Command.TYPE_OUTPUT_CONTROL:
int data = Integer.parseInt(command.getString(Command.KEY_DATA)) << 4
+ command.getInteger(Command.KEY_INDEX);
- return encodeContent(command.getDeviceId(), 0x03, data, 0);
+ return encodeCommand(command.getDeviceId(), 0x03, data, 0);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/CguardProtocol.java b/src/main/java/org/traccar/protocol/CguardProtocol.java
new file mode 100644
index 000000000..9157ca35c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CguardProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class CguardProtocol extends BaseProtocol {
+
+ public CguardProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new CguardProtocolDecoder(CguardProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/CguardProtocolDecoder.java b/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java
index 54f83fb73..d934921f1 100644
--- a/src/org/traccar/protocol/CguardProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CguardProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class CguardProtocolDecoder extends BaseProtocolDecoder {
- public CguardProtocolDecoder(CguardProtocol protocol) {
+ public CguardProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -58,8 +59,7 @@ public class CguardProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime());
@@ -84,8 +84,7 @@ public class CguardProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, parser.nextDateTime());
@@ -102,7 +101,11 @@ public class CguardProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
break;
case "BAT1":
- position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(value));
+ if (value.contains(".")) {
+ position.set(Position.KEY_BATTERY, Double.parseDouble(value));
+ } else {
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(value));
+ }
break;
case "PWR1":
position.set(Position.KEY_POWER, Double.parseDouble(value));
diff --git a/src/org/traccar/protocol/CityeasyProtocol.java b/src/main/java/org/traccar/protocol/CityeasyProtocol.java
index 7e5ca0ba0..8ab4ce93a 100644
--- a/src/org/traccar/protocol/CityeasyProtocol.java
+++ b/src/main/java/org/traccar/protocol/CityeasyProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,34 +15,26 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class CityeasyProtocol extends BaseProtocol {
public CityeasyProtocol() {
- super("cityeasy");
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_POSITION_STOP,
Command.TYPE_SET_TIMEZONE);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
- pipeline.addLast("objectEncoder", new CityeasyProtocolEncoder());
- pipeline.addLast("objectDecoder", new CityeasyProtocolDecoder(CityeasyProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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/org/traccar/protocol/CityeasyProtocolDecoder.java b/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java
index 9a614730f..9c4c7e11d 100644
--- a/src/org/traccar/protocol/CityeasyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CityeasyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -33,7 +34,7 @@ import java.util.regex.Pattern;
public class CityeasyProtocolDecoder extends BaseProtocolDecoder {
- public CityeasyProtocolDecoder(CityeasyProtocol protocol) {
+ public CityeasyProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -69,12 +70,12 @@ public class CityeasyProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
buf.readUnsignedShort(); // length
- String imei = ChannelBuffers.hexDump(buf.readBytes(7));
+ String imei = ByteBufUtil.hexDump(buf.readSlice(7));
DeviceSession deviceSession = getDeviceSession(
channel, remoteAddress, imei, imei + Checksum.luhn(Long.parseLong(imei)));
if (deviceSession == null) {
@@ -91,8 +92,7 @@ public class CityeasyProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.hasNext(15)) {
diff --git a/src/org/traccar/protocol/CityeasyProtocolEncoder.java b/src/main/java/org/traccar/protocol/CityeasyProtocolEncoder.java
index 387926e03..934105862 100644
--- a/src/org/traccar/protocol/CityeasyProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/CityeasyProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,18 +17,22 @@ package org.traccar.protocol;
import java.util.TimeZone;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class CityeasyProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeContent(int type, ChannelBuffer content) {
+ public CityeasyProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(int type, ByteBuf content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
buf.writeByte('S');
buf.writeByte('S');
@@ -36,7 +40,7 @@ public class CityeasyProtocolEncoder extends BaseProtocolEncoder {
buf.writeShort(type);
buf.writeBytes(content);
buf.writeInt(0x0B);
- buf.writeShort(Checksum.crc16(Checksum.CRC16_KERMIT, buf.toByteBuffer()));
+ buf.writeShort(Checksum.crc16(Checksum.CRC16_KERMIT, buf.nioBuffer()));
buf.writeByte('\r');
buf.writeByte('\n');
@@ -46,7 +50,7 @@ public class CityeasyProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- ChannelBuffer content = ChannelBuffers.dynamicBuffer();
+ ByteBuf content = Unpooled.buffer();
switch (command.getType()) {
case Command.TYPE_POSITION_SINGLE:
@@ -67,11 +71,8 @@ public class CityeasyProtocolEncoder extends BaseProtocolEncoder {
content.writeShort(Math.abs(timezone));
return encodeContent(CityeasyProtocolDecoder.MSG_TIMEZONE, content);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocol.java b/src/main/java/org/traccar/protocol/ContinentalProtocol.java
new file mode 100644
index 000000000..bc7928fba
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ContinentalProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class ContinentalProtocol extends BaseProtocol {
+
+ public ContinentalProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..471afa0d6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ContinentalProtocolDecoder.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+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;
+
+public class ContinentalProtocolDecoder extends BaseProtocolDecoder {
+
+ public ContinentalProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_KEEPALIVE = 0x00;
+ public static final int MSG_STATUS = 0x02;
+ public static final int MSG_ACK = 0x06;
+ public static final int MSG_NACK = 0x15;
+
+ private double readCoordinate(ByteBuf buf, boolean extended) {
+ long value = buf.readUnsignedInt();
+ if (extended ? (value & 0x08000000) != 0 : (value & 0x00800000) != 0) {
+ value |= extended ? 0xF0000000 : 0xFF000000;
+ }
+ return (int) value / (extended ? 360000.0 : 3600.0);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // software version
+
+ long serialNumber = buf.readUnsignedInt();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(serialNumber));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // product
+
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_STATUS) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setFixTime(new Date(buf.readUnsignedInt() * 1000L));
+
+ boolean extended = buf.getUnsignedByte(buf.readerIndex()) != 0;
+ position.setLatitude(readCoordinate(buf, extended));
+ position.setLongitude(readCoordinate(buf, extended));
+
+ position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+
+ position.setValid(buf.readUnsignedByte() > 0);
+
+ position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000L));
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedShort());
+
+ int input = buf.readUnsignedShort();
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
+
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedShort());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
+ position.set(Position.KEY_DEVICE_TEMP, buf.readByte());
+
+ buf.readUnsignedShort(); // reserved
+
+ if (buf.readableBytes() > 4) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
+
+ if (buf.readableBytes() > 4) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(buf.readUnsignedInt()));
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocol.java b/src/main/java/org/traccar/protocol/CradlepointProtocol.java
new file mode 100644
index 000000000..4a09e0311
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/CradlepointProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class CradlepointProtocol extends BaseProtocol {
+
+ public CradlepointProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new CradlepointProtocolDecoder(CradlepointProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/CradlepointProtocolDecoder.java b/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java
index e8f95a60c..a282131ce 100644
--- a/src/org/traccar/protocol/CradlepointProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CradlepointProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -29,7 +30,7 @@ import java.util.regex.Pattern;
public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
- public CradlepointProtocolDecoder(CradlepointProtocol protocol) {
+ public CradlepointProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,8 +66,7 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
int time = parser.nextInt();
@@ -84,9 +84,9 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
position.set("carrid", parser.next());
position.set("serdis", parser.next());
- position.set("rsrp", parser.next());
- position.set("dbm", parser.next());
- position.set("rsrq", parser.next());
+ position.set("rsrp", parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set("rsrq", parser.nextInt());
position.set("ecio", parser.next());
return position;
diff --git a/src/main/java/org/traccar/protocol/DishaProtocol.java b/src/main/java/org/traccar/protocol/DishaProtocol.java
new file mode 100644
index 000000000..38f49cc05
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DishaProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class DishaProtocol extends BaseProtocol {
+
+ public DishaProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new DishaProtocolDecoder(DishaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/DishaProtocolDecoder.java b/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java
index ab0a63215..3223988ab 100644
--- a/src/org/traccar/protocol/DishaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DishaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class DishaProtocolDecoder extends BaseProtocolDecoder {
- public DishaProtocolDecoder(DishaProtocol protocol) {
+ public DishaProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,8 +66,7 @@ public class DishaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
new file mode 100644
index 000000000..34568128f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class DmtHttpProtocol extends BaseProtocol {
+
+ public DmtHttpProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new DmtHttpProtocolDecoder(DmtHttpProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
index 1bd808a1f..987361baf 100644
--- a/src/org/traccar/protocol/DmtHttpProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,15 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpHeaders;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
-import org.traccar.BaseProtocolDecoder;
+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.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -40,27 +37,19 @@ import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
-public class DmtHttpProtocolDecoder extends BaseProtocolDecoder {
+public class DmtHttpProtocolDecoder extends BaseHttpProtocolDecoder {
- public DmtHttpProtocolDecoder(DmtHttpProtocol protocol) {
+ public DmtHttpProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private void sendResponse(Channel channel, HttpResponseStatus status) {
- if (channel != null) {
- HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
- response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0);
- channel.write(response);
- }
- }
-
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- HttpRequest request = (HttpRequest) msg;
+ FullHttpRequest request = (FullHttpRequest) msg;
JsonObject root = Json.createReader(
- new StringReader(request.getContent().toString(StandardCharsets.US_ASCII))).readObject();
+ new StringReader(request.content().toString(StandardCharsets.US_ASCII))).readObject();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -76,8 +65,7 @@ public class DmtHttpProtocolDecoder extends BaseProtocolDecoder {
JsonArray records = root.getJsonArray("Records");
for (int i = 0; i < records.size(); i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
JsonObject record = records.getJsonObject(i);
diff --git a/src/main/java/org/traccar/protocol/DmtProtocol.java b/src/main/java/org/traccar/protocol/DmtProtocol.java
new file mode 100644
index 000000000..78a5243c0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DmtProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+public class DmtProtocol extends BaseProtocol {
+
+ public DmtProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..58a5a88e3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
@@ -0,0 +1,300 @@
+/*
+ * 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.protocol;
+
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class DmtProtocolDecoder extends BaseProtocolDecoder {
+
+ public DmtProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_HELLO = 0x00;
+ public static final int MSG_HELLO_RESPONSE = 0x01;
+ public static final int MSG_DATA_RECORD = 0x04;
+ public static final int MSG_COMMIT = 0x05;
+ public static final int MSG_COMMIT_RESPONSE = 0x06;
+ public static final int MSG_DATA_RECORD_64 = 0x10;
+
+ public static final int MSG_CANNED_REQUEST_1 = 0x14;
+ public static final int MSG_CANNED_RESPONSE_1 = 0x15;
+ public static final int MSG_CANNED_REQUEST_2 = 0x22;
+ public static final int MSG_CANNED_RESPONSE_2 = 0x23;
+
+ private void sendResponse(Channel channel, int type, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x02); response.writeByte(0x55); // header
+ response.writeByte(type);
+ response.writeShortLE(content != null ? content.readableBytes() : 0);
+ if (content != null) {
+ response.writeBytes(content);
+ content.release();
+ }
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private List<Position> decodeFixed64(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.readableBytes() >= 64) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readByte(); // type
+
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
+
+ long time = buf.readUnsignedIntLE();
+ position.setTime(new DateBuilder()
+ .setYear((int) (2000 + (time & 0x3F)))
+ .setMonth((int) (time >> 6) & 0xF)
+ .setDay((int) (time >> 10) & 0x1F)
+ .setHour((int) (time >> 15) & 0x1F)
+ .setMinute((int) (time >> 20) & 0x3F)
+ .setSecond((int) (time >> 26) & 0x3F)
+ .getDate());
+
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readShortLE());
+
+ buf.readUnsignedShortLE(); // position accuracy
+ buf.readUnsignedByte(); // speed accuracy
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+
+ position.setValid(BitUtil.check(buf.readByte(), 0));
+
+ position.set(Position.KEY_INPUT, buf.readUnsignedIntLE());
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedShortLE());
+
+ for (int i = 1; i <= 5; i++) {
+ position.set(Position.PREFIX_ADC + i, buf.readShortLE());
+ }
+
+ position.set(Position.KEY_DEVICE_TEMP, buf.readByte());
+
+ buf.readShortLE(); // accelerometer x
+ buf.readShortLE(); // accelerometer y
+ buf.readShortLE(); // accelerometer z
+
+ buf.skipBytes(8); // device id
+
+ position.set(Position.KEY_PDOP, buf.readUnsignedShortLE() * 0.01);
+
+ buf.skipBytes(2); // reserved
+
+ buf.readUnsignedShortLE(); // checksum
+
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private List<Position> decodeStandard(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.isReadable()) {
+ int recordEnd = buf.readerIndex() + buf.readUnsignedShortLE();
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
+
+ position.setDeviceTime(new Date(1356998400000L + buf.readUnsignedIntLE() * 1000)); // since 1 Jan 2013
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+
+ while (buf.readerIndex() < recordEnd) {
+
+ int fieldId = buf.readUnsignedByte();
+ int fieldLength = buf.readUnsignedByte();
+ int fieldEnd = buf.readerIndex() + (fieldLength == 255 ? buf.readUnsignedShortLE() : fieldLength);
+
+ if (fieldId == 0) {
+
+ position.setFixTime(new Date(1356998400000L + buf.readUnsignedIntLE() * 1000));
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setAltitude(buf.readShortLE());
+ position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShortLE()));
+
+ buf.readUnsignedByte(); // speed accuracy
+
+ position.setCourse(buf.readUnsignedByte() * 2);
+
+ position.set(Position.KEY_PDOP, buf.readUnsignedByte() * 0.1);
+
+ position.setAccuracy(buf.readUnsignedByte());
+ position.setValid(buf.readUnsignedByte() != 0);
+
+ } else if (fieldId == 2) {
+
+ int input = buf.readIntLE();
+ int output = buf.readUnsignedShortLE();
+ int status = buf.readUnsignedShortLE();
+
+ 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);
+
+ } else if (fieldId == 6) {
+
+ while (buf.readerIndex() < fieldEnd) {
+ switch (buf.readUnsignedByte()) {
+ case 1:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+ break;
+ case 2:
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01);
+ break;
+ case 3:
+ position.set(Position.KEY_DEVICE_TEMP, buf.readShortLE() * 0.01);
+ break;
+ case 4:
+ position.set(Position.KEY_RSSI, buf.readUnsignedShortLE());
+ break;
+ case 5:
+ position.set("solarPower", buf.readUnsignedShortLE() * 0.001);
+ break;
+ default:
+ buf.readUnsignedShortLE(); // other
+ break;
+ }
+ }
+
+ } else if (fieldId == 26) {
+
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedIntLE());
+ position.set("tripHours", buf.readUnsignedIntLE() * 1000);
+
+ } else if (fieldId == 27) {
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000);
+
+ }
+
+ buf.readerIndex(fieldEnd);
+
+ }
+
+ if (position.getFixTime() == null) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+
+ int type = buf.readUnsignedByte();
+ int length = buf.readUnsignedShortLE();
+
+ if (type == MSG_HELLO) {
+
+ buf.readUnsignedIntLE(); // device serial number
+
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII));
+
+ ByteBuf response = Unpooled.buffer();
+ if (length == 51) {
+ response.writeByte(0); // reserved
+ response.writeIntLE(0); // reserved
+ } else {
+ response.writeIntLE((int) ((System.currentTimeMillis() - 1356998400000L) / 1000));
+ response.writeIntLE(deviceSession != null ? 0 : 1); // flags
+ }
+
+ sendResponse(channel, MSG_HELLO_RESPONSE, response);
+
+ } else if (type == MSG_COMMIT) {
+
+ ByteBuf response = Unpooled.buffer(0);
+ response.writeByte(1); // flags (success)
+ sendResponse(channel, MSG_COMMIT_RESPONSE, response);
+
+ } else if (type == MSG_CANNED_REQUEST_1) {
+
+ ByteBuf response = Unpooled.buffer(0);
+ response.writeBytes(new byte[12]);
+ sendResponse(channel, MSG_CANNED_RESPONSE_1, response);
+
+ } else if (type == MSG_CANNED_REQUEST_2) {
+
+ sendResponse(channel, MSG_CANNED_RESPONSE_2, null);
+
+ } else if (type == MSG_DATA_RECORD_64) {
+
+ return decodeFixed64(channel, remoteAddress, buf);
+
+ } else if (type == MSG_DATA_RECORD) {
+
+ return decodeStandard(channel, remoteAddress, buf);
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/DwayProtocol.java b/src/main/java/org/traccar/protocol/DwayProtocol.java
new file mode 100644
index 000000000..05fd8b6e7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DwayProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class DwayProtocol extends BaseProtocol {
+
+ public DwayProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new DwayProtocolDecoder(DwayProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/DwayProtocolDecoder.java b/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java
index 767b35c72..9b02c898e 100644
--- a/src/org/traccar/protocol/DwayProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DwayProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +30,7 @@ import java.util.regex.Pattern;
public class DwayProtocolDecoder extends BaseProtocolDecoder {
- public DwayProtocolDecoder(DwayProtocol protocol) {
+ public DwayProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -59,9 +61,9 @@ public class DwayProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
- if (sentence.startsWith(">H")) {
+ if (sentence.equals("AA55,HB")) {
if (channel != null) {
- channel.write(">ALIVE\r\n");
+ channel.writeAndFlush(new NetworkMessage("55AA,HB,OK\r\n", remoteAddress));
}
return null;
}
@@ -76,8 +78,7 @@ public class DwayProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setValid(true);
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
new file mode 100644
index 000000000..74c636d06
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class EasyTrackProtocol extends BaseProtocol {
+
+ public EasyTrackProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "#", "\r\n"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new EasyTrackProtocolDecoder(EasyTrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
index 799254b65..2ddb24f5c 100644
--- a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -30,7 +31,7 @@ import java.util.regex.Pattern;
public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
- public EasyTrackProtocolDecoder(EasyTrackProtocol protocol) {
+ public EasyTrackProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -54,6 +55,31 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private String decodeAlarm(long status) {
+ if ((status & 0x02000000) != 0) {
+ return Position.ALARM_GEOFENCE_ENTER;
+ }
+ if ((status & 0x04000000) != 0) {
+ return Position.ALARM_GEOFENCE_EXIT;
+ }
+ if ((status & 0x08000000) != 0) {
+ return Position.ALARM_LOW_BATTERY;
+ }
+ if ((status & 0x20000000) != 0) {
+ return Position.ALARM_VIBRATION;
+ }
+ if ((status & 0x80000000) != 0) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if ((status & 0x00010000) != 0) {
+ return Position.ALARM_SOS;
+ }
+ if ((status & 0x00040000) != 0) {
+ return Position.ALARM_POWER_CUT;
+ }
+ return null;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -63,8 +89,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -96,7 +121,10 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt(0) / 100.0));
position.setCourse(parser.nextHexInt(0) / 100.0);
- position.set(Position.KEY_STATUS, parser.next());
+ long status = parser.nextHexLong();
+ position.set(Position.KEY_STATUS, status);
+ position.set(Position.KEY_ALARM, decodeAlarm(status));
+
position.set("signal", parser.next());
position.set(Position.KEY_POWER, parser.nextDouble(0));
position.set("oil", parser.nextHexInt(0));
diff --git a/src/org/traccar/protocol/EelinkProtocol.java b/src/main/java/org/traccar/protocol/EelinkProtocol.java
index 5499094d9..8a055d643 100644
--- a/src/org/traccar/protocol/EelinkProtocol.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,34 +15,34 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class EelinkProtocol extends BaseProtocol {
public EelinkProtocol() {
- super("eelink");
setSupportedDataCommands(
Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_REBOOT_DEVICE);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2));
- pipeline.addLast("objectEncoder", new EelinkProtocolEncoder());
- pipeline.addLast("objectDecoder", new EelinkProtocolDecoder(EelinkProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new EelinkProtocolEncoder(EelinkProtocol.this, true));
+ pipeline.addLast(new EelinkProtocolDecoder(EelinkProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index 8d0f8016a..41d76f37f 100644
--- a/src/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,23 +15,31 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
+import java.util.regex.Pattern;
public class EelinkProtocolDecoder extends BaseProtocolDecoder {
- public EelinkProtocolDecoder(EelinkProtocol protocol) {
+ public EelinkProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -55,17 +63,6 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_CAMERA_INFO = 0x1E;
public static final int MSG_CAMERA_DATA = 0x1F;
- private void sendResponse(Channel channel, int type, int index) {
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.buffer(7);
- response.writeByte(0x67); response.writeByte(0x67); // header
- response.writeByte(type);
- response.writeShort(2); // length
- response.writeShort(index);
- channel.write(response);
- }
- }
-
private String decodeAlarm(Short value) {
switch (value) {
case 0x01:
@@ -112,11 +109,10 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_STATUS, status);
}
- private Position decodeOld(DeviceSession deviceSession, ChannelBuffer buf, int type, int index) {
+ private Position decodeOld(DeviceSession deviceSession, ByteBuf buf, int type, int index) {
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
position.set(Position.KEY_INDEX, index);
@@ -170,11 +166,10 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private Position decodeNew(DeviceSession deviceSession, ChannelBuffer buf, int index) {
+ private Position decodeNew(DeviceSession deviceSession, ByteBuf buf, int type, int index) {
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
position.set(Position.KEY_INDEX, index);
@@ -189,6 +184,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
position.setCourse(buf.readUnsignedShort());
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ } else {
+ getLastLocation(position, position.getDeviceTime());
}
if (BitUtil.check(flags, 1)) {
@@ -217,43 +214,142 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(7); // bss2
}
- if (buf.readableBytes() >= 2) {
+ if (type == MSG_WARNING) {
+
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+
+ } else if (type == MSG_REPORT) {
+
+ buf.readUnsignedByte(); // report type
+
+ }
+
+ if (type == MSG_NORMAL || type == MSG_WARNING || type == MSG_REPORT) {
+
int status = buf.readUnsignedShort();
position.setValid(BitUtil.check(status, 0));
if (BitUtil.check(status, 1)) {
position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
}
position.set(Position.KEY_STATUS, status);
- }
- if (buf.readableBytes() >= 2) {
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
}
- if (buf.readableBytes() >= 4) {
- position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- }
+ if (type == MSG_NORMAL) {
- if (buf.readableBytes() >= 4) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
+ if (buf.readableBytes() >= 2) {
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
+
+ if (buf.readableBytes() >= 4) {
+ buf.readUnsignedShort(); // gsm counter
+ buf.readUnsignedShort(); // gps counter
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.KEY_STEPS, buf.readUnsignedShort());
+ buf.readUnsignedShort(); // walking time
+ }
+
+ if (buf.readableBytes() >= 12) {
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0);
+ position.set("humidity", buf.readUnsignedShort() * 0.1);
+ position.set("illuminance", buf.readUnsignedInt() / 256.0);
+ position.set("co2", buf.readUnsignedInt());
+ }
+
+ if (buf.readableBytes() >= 2) {
+ position.set(Position.PREFIX_TEMP + 2, buf.readShort() / 16.0);
+ }
- if (buf.readableBytes() >= 4) {
- buf.readUnsignedShort(); // gsm counter
- buf.readUnsignedShort(); // gps counter
}
- if (buf.readableBytes() >= 4) {
- position.set(Position.KEY_STEPS, buf.readUnsignedShort());
- buf.readUnsignedShort(); // walking time
+ return position;
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("Lat:")
+ .number("([NS])(d+.d+)") // latitude
+ .any()
+ .text("Lon:")
+ .number("([EW])(d+.d+)") // longitude
+ .any()
+ .text("Course:")
+ .number("(d+.d+)") // course
+ .any()
+ .text("Speed:")
+ .number("(d+.d+)") // speed
+ .any()
+ .expression("Date ?Time:")
+ .number("(dddd)-(dd)-(dd) ") // date
+ .number("(dd):(dd):(dd)") // time
+ .compile();
+
+ private Position decodeResult(DeviceSession deviceSession, ByteBuf buf, int index) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, index);
+
+ buf.readUnsignedByte(); // type
+ buf.readUnsignedInt(); // uid
+
+ String sentence = buf.toString(StandardCharsets.UTF_8);
+
+ Parser parser = new Parser(PATTERN, sentence);
+ 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(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setTime(parser.nextDateTime());
+
+ } else {
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RESULT, sentence);
+
}
- if (buf.readableBytes() >= 12) {
- position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0);
- position.set("humidity", buf.readUnsignedShort() * 0.1);
- position.set("illuminance", buf.readUnsignedInt() / 256.0);
- position.set("co2", buf.readUnsignedInt());
+ return position;
+ }
+
+ private Position decodeObd(DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, new Date(buf.readUnsignedInt() * 1000));
+
+ while (buf.readableBytes() > 0) {
+ int pid = buf.readUnsignedByte();
+ int value = buf.readInt();
+ switch (pid) {
+ case 0x89:
+ position.set(Position.KEY_FUEL_CONSUMPTION, value);
+ break;
+ case 0x8a:
+ position.set(Position.KEY_ODOMETER, value * 1000L);
+ break;
+ case 0x8b:
+ position.set(Position.KEY_FUEL_LEVEL, value / 10);
+ break;
+ default:
+ break;
+ }
}
return position;
@@ -263,7 +359,18 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
+
+ String uniqueId = null;
+ DeviceSession deviceSession;
+
+ if (buf.getByte(0) == 'E' && buf.getByte(1) == 'L') {
+ buf.skipBytes(2 + 2 + 2); // udp header
+ uniqueId = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
+ deviceSession = getDeviceSession(channel, remoteAddress, uniqueId);
+ } else {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ }
buf.skipBytes(2); // header
int type = buf.readUnsignedByte();
@@ -271,29 +378,45 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
int index = buf.readUnsignedShort();
if (type != MSG_GPS && type != MSG_DATA) {
- sendResponse(channel, type, index);
+ ByteBuf content = Unpooled.buffer();
+ if (type == MSG_LOGIN) {
+ content.writeInt((int) (System.currentTimeMillis() / 1000));
+ content.writeByte(1); // protocol version
+ content.writeByte(0); // action mask
+ }
+ ByteBuf response = EelinkProtocolEncoder.encodeContent(
+ channel instanceof DatagramChannel, uniqueId, type, index, content);
+ content.release();
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
}
if (type == MSG_LOGIN) {
- getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(8)).substring(1));
+ if (deviceSession == null) {
+ getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(buf.readSlice(8)).substring(1));
+ }
} else {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
}
if (type == MSG_GPS || type == MSG_ALARM || type == MSG_STATE || type == MSG_SMS) {
+
return decodeOld(deviceSession, buf, type, index);
+
} else if (type >= MSG_NORMAL && type <= MSG_OBD_CODE) {
- return decodeNew(deviceSession, buf, index);
- } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) {
- Position position = new Position();
+ return decodeNew(deviceSession, buf, type, index);
+
+ } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2
+ || type == MSG_OBD && buf.readableBytes() == 4) {
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
getLastLocation(position, null);
@@ -301,6 +424,14 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == MSG_OBD) {
+
+ return decodeObd(deviceSession, buf);
+
+ } else if (type == MSG_DOWNLINK) {
+
+ return decodeResult(deviceSession, buf, index);
+
}
}
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolEncoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolEncoder.java
new file mode 100644
index 000000000..3673c35b3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolEncoder.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 - 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+public class EelinkProtocolEncoder extends BaseProtocolEncoder {
+
+ private boolean connectionless;
+
+ public EelinkProtocolEncoder(Protocol protocol, boolean connectionless) {
+ super(protocol);
+ this.connectionless = connectionless;
+ }
+
+ public static int checksum(ByteBuffer buf) {
+ int sum = 0;
+ while (buf.hasRemaining()) {
+ sum = (((sum << 1) | (sum >> 15)) + (buf.get() & 0xFF)) & 0xFFFF;
+ }
+ return sum;
+ }
+
+ public static ByteBuf encodeContent(
+ boolean connectionless, String uniqueId, int type, int index, ByteBuf content) {
+
+ ByteBuf buf = Unpooled.buffer();
+
+ if (connectionless) {
+ buf.writeBytes(DataConverter.parseHex('0' + uniqueId));
+ }
+
+ buf.writeByte(0x67);
+ buf.writeByte(0x67);
+ buf.writeByte(type);
+ buf.writeShort(2 + (content != null ? content.readableBytes() : 0)); // length
+ buf.writeShort(index);
+
+ if (content != null) {
+ buf.writeBytes(content);
+ }
+
+ ByteBuf result = Unpooled.buffer();
+
+ if (connectionless) {
+ result.writeByte('E');
+ result.writeByte('L');
+ result.writeShort(2 + buf.readableBytes()); // length
+ result.writeShort(checksum(buf.nioBuffer()));
+ }
+
+ result.writeBytes(buf);
+ buf.release();
+
+ return result;
+ }
+
+ private ByteBuf encodeContent(long deviceId, String content) {
+
+ ByteBuf buf = Unpooled.buffer();
+
+ buf.writeByte(0x01); // command
+ buf.writeInt(0); // server id
+ buf.writeBytes(content.getBytes(StandardCharsets.UTF_8));
+
+ return encodeContent(connectionless, getUniqueId(deviceId), EelinkProtocolDecoder.MSG_DOWNLINK, 0, buf);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return encodeContent(command.getDeviceId(), command.getString(Command.KEY_DATA));
+ case Command.TYPE_POSITION_SINGLE:
+ return encodeContent(command.getDeviceId(), "WHERE#");
+ case Command.TYPE_ENGINE_STOP:
+ return encodeContent(command.getDeviceId(), "RELAY,1#");
+ case Command.TYPE_ENGINE_RESUME:
+ return encodeContent(command.getDeviceId(), "RELAY,0#");
+ case Command.TYPE_REBOOT_DEVICE:
+ return encodeContent(command.getDeviceId(), "RESET#");
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EgtsFrameDecoder.java b/src/main/java/org/traccar/protocol/EgtsFrameDecoder.java
new file mode 100644
index 000000000..84f1f11a7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EgtsFrameDecoder.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 EgtsFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ int headerLength = buf.getUnsignedByte(buf.readerIndex() + 3);
+ int frameLength = buf.getUnsignedShortLE(buf.readerIndex() + 5);
+
+ int length = headerLength + frameLength + (frameLength > 0 ? 2 : 0);
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EgtsProtocol.java b/src/main/java/org/traccar/protocol/EgtsProtocol.java
new file mode 100644
index 000000000..5d4638f37
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EgtsProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class EgtsProtocol extends BaseProtocol {
+
+ public EgtsProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..9f0baf6b2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EgtsProtocolDecoder.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2018 - 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 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.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 java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class EgtsProtocolDecoder extends BaseProtocolDecoder {
+
+ public EgtsProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private boolean useObjectIdAsDeviceId = true;
+
+ public static final int PT_RESPONSE = 0;
+ public static final int PT_APPDATA = 1;
+ public static final int PT_SIGNED_APPDATA = 2;
+
+ public static final int SERVICE_AUTH = 1;
+ public static final int SERVICE_TELEDATA = 2;
+ public static final int SERVICE_COMMANDS = 4;
+ public static final int SERVICE_FIRMWARE = 9;
+ public static final int SERVICE_ECALL = 10;
+
+ public static final int MSG_RECORD_RESPONSE = 0;
+ public static final int MSG_TERM_IDENTITY = 1;
+ public static final int MSG_MODULE_DATA = 2;
+ public static final int MSG_VEHICLE_DATA = 3;
+ public static final int MSG_AUTH_PARAMS = 4;
+ public static final int MSG_AUTH_INFO = 5;
+ public static final int MSG_SERVICE_INFO = 6;
+ public static final int MSG_RESULT_CODE = 7;
+ public static final int MSG_POS_DATA = 16;
+ public static final int MSG_EXT_POS_DATA = 17;
+ public static final int MSG_AD_SENSORS_DATA = 18;
+ public static final int MSG_COUNTERS_DATA = 19;
+ public static final int MSG_STATE_DATA = 20;
+ public static final int MSG_LOOPIN_DATA = 22;
+ public static final int MSG_ABS_DIG_SENS_DATA = 23;
+ public static final int MSG_ABS_AN_SENS_DATA = 24;
+ public static final int MSG_ABS_CNTR_DATA = 25;
+ public static final int MSG_ABS_LOOPIN_DATA = 26;
+ public static final int MSG_LIQUID_LEVEL_SENSOR = 27;
+ public static final int MSG_PASSENGERS_COUNTERS = 28;
+
+ private int packetId;
+
+ private void sendResponse(
+ Channel channel, int packetType, int index, int serviceType, int type, ByteBuf content) {
+ if (channel != null) {
+
+ ByteBuf data = Unpooled.buffer();
+ data.writeByte(type);
+ data.writeShortLE(content.readableBytes());
+ data.writeBytes(content);
+ content.release();
+
+ ByteBuf record = Unpooled.buffer();
+ if (packetType == PT_RESPONSE) {
+ record.writeShortLE(index);
+ record.writeByte(0); // success
+ }
+ record.writeShortLE(data.readableBytes());
+ record.writeShortLE(0);
+ record.writeByte(0); // flags (possibly 1 << 6)
+ record.writeByte(serviceType);
+ record.writeByte(serviceType);
+ record.writeBytes(data);
+ data.release();
+ int recordChecksum = Checksum.crc16(Checksum.CRC16_CCITT_FALSE, record.nioBuffer());
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(1); // protocol version
+ response.writeByte(0); // security key id
+ response.writeByte(0); // flags
+ response.writeByte(5 + 2 + 2 + 2); // header length
+ response.writeByte(0); // encoding
+ response.writeShortLE(record.readableBytes());
+ response.writeShortLE(packetId++);
+ response.writeByte(packetType);
+ response.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, response.nioBuffer()));
+ response.writeBytes(record);
+ record.release();
+ response.writeShortLE(recordChecksum);
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ List<Position> positions = new LinkedList<>();
+
+ short headerLength = buf.getUnsignedByte(buf.readerIndex() + 3);
+ int index = buf.getUnsignedShort(buf.readerIndex() + 5 + 2);
+ short packetType = buf.getUnsignedByte(buf.readerIndex() + 5 + 2 + 2);
+ buf.skipBytes(headerLength);
+
+ if (packetType == PT_RESPONSE) {
+ return null;
+ }
+
+ long objectId = 0L;
+ while (buf.readableBytes() > 2) {
+
+ int length = buf.readUnsignedShortLE();
+ int recordIndex = buf.readUnsignedShortLE();
+ int recordFlags = buf.readUnsignedByte();
+
+ if (BitUtil.check(recordFlags, 0)) {
+ objectId = buf.readUnsignedIntLE();
+ }
+
+ if (BitUtil.check(recordFlags, 1)) {
+ buf.readUnsignedIntLE(); // event id
+ }
+ if (BitUtil.check(recordFlags, 2)) {
+ buf.readUnsignedIntLE(); // time
+ }
+
+ int serviceType = buf.readUnsignedByte();
+ buf.readUnsignedByte(); // recipient service type
+
+ int recordEnd = buf.readerIndex() + length;
+
+ Position position = new Position(getProtocolName());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeShortLE(recordIndex);
+ response.writeByte(0); // success
+ sendResponse(channel, PT_RESPONSE, index, serviceType, MSG_RECORD_RESPONSE, response);
+
+ while (buf.readerIndex() < recordEnd) {
+ int type = buf.readUnsignedByte();
+ int end = buf.readUnsignedShortLE() + buf.readerIndex();
+
+ if (type == MSG_TERM_IDENTITY) {
+ useObjectIdAsDeviceId = false;
+
+ buf.readUnsignedIntLE(); // object id
+ int flags = buf.readUnsignedByte();
+
+ if (BitUtil.check(flags, 0)) {
+ buf.readUnsignedShortLE(); // home dispatcher identifier
+ }
+ if (BitUtil.check(flags, 1)) {
+ getDeviceSession(
+ channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII).trim());
+ }
+ if (BitUtil.check(flags, 2)) {
+ getDeviceSession(
+ channel, remoteAddress, buf.readSlice(16).toString(StandardCharsets.US_ASCII).trim());
+ }
+ if (BitUtil.check(flags, 3)) {
+ buf.skipBytes(3); // language identifier
+ }
+ if (BitUtil.check(flags, 5)) {
+ buf.skipBytes(3); // network identifier
+ }
+ if (BitUtil.check(flags, 6)) {
+ buf.readUnsignedShortLE(); // buffer size
+ }
+ if (BitUtil.check(flags, 7)) {
+ getDeviceSession(
+ channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII).trim());
+ }
+
+ response = Unpooled.buffer();
+ response.writeByte(0); // success
+ sendResponse(channel, PT_APPDATA, 0, serviceType, MSG_RESULT_CODE, response);
+
+ } else if (type == MSG_POS_DATA) {
+
+ position.setTime(new Date((buf.readUnsignedIntLE() + 1262304000) * 1000)); // since 2010-01-01
+ position.setLatitude(buf.readUnsignedIntLE() * 90.0 / 0xFFFFFFFFL);
+ position.setLongitude(buf.readUnsignedIntLE() * 180.0 / 0xFFFFFFFFL);
+
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 0));
+ if (BitUtil.check(flags, 5)) {
+ position.setLatitude(-position.getLatitude());
+ }
+ if (BitUtil.check(flags, 6)) {
+ position.setLongitude(-position.getLongitude());
+ }
+
+ int speed = buf.readUnsignedShortLE();
+ position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speed, 14) * 0.1));
+ position.setCourse(buf.readUnsignedByte() + (BitUtil.check(speed, 15) ? 0x100 : 0));
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedMediumLE() * 100);
+ position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+
+ if (BitUtil.check(flags, 7)) {
+ position.setAltitude(buf.readMediumLE());
+ }
+
+ } else if (type == MSG_EXT_POS_DATA) {
+
+ int flags = buf.readUnsignedByte();
+
+ if (BitUtil.check(flags, 0)) {
+ position.set(Position.KEY_VDOP, buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(flags, 1)) {
+ position.set(Position.KEY_HDOP, buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(flags, 2)) {
+ position.set(Position.KEY_PDOP, buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(flags, 3)) {
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ }
+
+ } else if (type == MSG_AD_SENSORS_DATA) {
+
+ buf.readUnsignedByte(); // inputs flags
+
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+
+ buf.readUnsignedByte(); // adc flags
+
+ }
+
+ buf.readerIndex(end);
+ }
+
+ if (serviceType == SERVICE_TELEDATA && position.getValid()) {
+ if (useObjectIdAsDeviceId && objectId != 0L) {
+ deviceSession = getDeviceSession(channel, remoteAddress, true, String.valueOf(objectId));
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+ }
+ if (deviceSession != null) {
+ positions.add(position);
+ }
+ }
+ }
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+}
diff --git a/src/org/traccar/protocol/EnforaProtocol.java b/src/main/java/org/traccar/protocol/EnforaProtocol.java
index 79cc47c0b..e462ab322 100644
--- a/src/org/traccar/protocol/EnforaProtocol.java
+++ b/src/main/java/org/traccar/protocol/EnforaProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,33 +15,32 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class EnforaProtocol extends BaseProtocol {
public EnforaProtocol() {
- super("enfora");
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, -2, 2));
- pipeline.addLast("objectEncoder", new EnforaProtocolEncoder());
- pipeline.addLast("objectDecoder", new EnforaProtocolDecoder(EnforaProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new EnforaProtocolEncoder(EnforaProtocol.this));
+ pipeline.addLast(new EnforaProtocolDecoder(EnforaProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/EnforaProtocolDecoder.java b/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java
index f0b79aa1f..bfa7a116b 100644
--- a/src/org/traccar/protocol/EnforaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EnforaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,15 +15,15 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBufferIndexFinder;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BufferUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
-import org.traccar.helper.StringFinder;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -32,7 +32,7 @@ import java.util.regex.Pattern;
public class EnforaProtocolDecoder extends BaseProtocolDecoder {
- public EnforaProtocolDecoder(EnforaProtocol protocol) {
+ public EnforaProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -56,23 +56,22 @@ public class EnforaProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
// Find IMEI number
- int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new ChannelBufferIndexFinder() {
- @Override
- public boolean find(ChannelBuffer buffer, int guessedIndex) {
- if (buffer.writerIndex() - guessedIndex >= IMEI_LENGTH) {
- for (int i = 0; i < IMEI_LENGTH; i++) {
- if (!Character.isDigit((char) buffer.getByte(guessedIndex + i))) {
- return false;
- }
- }
- return true;
+ int index = -1;
+ for (int i = buf.readerIndex(); i < buf.writerIndex() - IMEI_LENGTH; i++) {
+ index = i;
+ for (int j = i; j < i + IMEI_LENGTH; j++) {
+ if (!Character.isDigit((char) buf.getByte(j))) {
+ index = -1;
+ break;
}
- return false;
}
- });
+ if (index > 0) {
+ break;
+ }
+ }
if (index == -1) {
return null;
}
@@ -84,7 +83,7 @@ public class EnforaProtocolDecoder extends BaseProtocolDecoder {
}
// Find NMEA sentence
- int start = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("GPRMC"));
+ int start = BufferUtil.indexOf("GPRMC", buf);
if (start == -1) {
return null;
}
@@ -95,8 +94,7 @@ public class EnforaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/org/traccar/protocol/EnforaProtocolEncoder.java b/src/main/java/org/traccar/protocol/EnforaProtocolEncoder.java
index 3dca1b9b3..8cc24dc0f 100644
--- a/src/org/traccar/protocol/EnforaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/EnforaProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Jose Castellanos
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,19 +16,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
import java.nio.charset.StandardCharsets;
public class EnforaProtocolEncoder extends StringProtocolEncoder {
- private ChannelBuffer encodeContent(String content) {
+ public EnforaProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(String content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
buf.writeShort(content.length() + 6);
buf.writeShort(0); // index
@@ -49,11 +53,8 @@ public class EnforaProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ENGINE_RESUME:
return encodeContent("AT$IOGP3=0");
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/EsealProtocol.java b/src/main/java/org/traccar/protocol/EsealProtocol.java
new file mode 100644
index 000000000..fc1d342e1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EsealProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 - 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 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.model.Command;
+
+public class EsealProtocol extends BaseProtocol {
+
+ public EsealProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_ALARM_ARM,
+ Command.TYPE_ALARM_DISARM);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new EsealProtocolEncoder(EsealProtocol.this));
+ pipeline.addLast(new EsealProtocolDecoder(EsealProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
new file mode 100644
index 000000000..7a1fd7022
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Context;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+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 EsealProtocolDecoder extends BaseProtocolDecoder {
+
+ private String config;
+
+ public EsealProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ config = Context.getConfig().getString(getProtocolName() + ".config");
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("##S,")
+ .expression("[^,]+,") // device type
+ .number("(d+),") // device id
+ .number("d+,") // customer id
+ .expression("[^,]+,") // firmware version
+ .expression("([^,]+),") // type
+ .number("(d+),") // index
+ .number("(dddd)-(dd)-(dd),") // date
+ .number("(dd):(dd):(dd),") // time
+ .number("d+,") // interval
+ .expression("([AV]),") // validity
+ .number("(d+.d+)([NS]) ") // latitude
+ .number("(d+.d+)([EW]),") // longitude
+ .number("(d+),") // course
+ .number("(d+),") // speed
+ .expression("([^,]+),") // door
+ .number("(d+.d+),") // acceleration
+ .expression("([^,]+),") // nfc
+ .number("(d+.d+),") // battery
+ .number("(-?d+),") // rssi
+ .text("E##")
+ .compile();
+
+ private void sendResponse(Channel channel, String prefix, String type, String payload) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ prefix + type + "," + payload + ",E##\r\n", channel.remoteAddress()));
+ }
+ }
+
+ private String decodeAlarm(String type) {
+ switch (type) {
+ case "Event-Door":
+ return Position.ALARM_DOOR;
+ case "Event-Shock":
+ return Position.ALARM_SHOCK;
+ case "Event-Drop":
+ return Position.ALARM_FALL_DOWN;
+ case "Event-Lock":
+ return Position.ALARM_LOCK;
+ case "Event-RC-Unlock":
+ return Position.ALARM_UNLOCK;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ String type = parser.next();
+ String prefix = sentence.substring(0, sentence.indexOf(type));
+ int index = parser.nextInt();
+
+ position.set(Position.KEY_INDEX, index);
+ position.set(Position.KEY_ALARM, decodeAlarm(type));
+
+ switch (type) {
+ case "Startup":
+ sendResponse(channel, prefix, type + " ACK", index + "," + config);
+ break;
+ case "Normal":
+ case "Button-Normal":
+ case "Termination":
+ case "Event-Door":
+ case "Event-Shock":
+ case "Event-Drop":
+ case "Event-Lock":
+ case "Event-RC-Unlock":
+ sendResponse(channel, prefix, type + " ACK", String.valueOf(index));
+ break;
+ default:
+ break;
+ }
+
+ position.setTime(parser.nextDateTime());
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setCourse(parser.nextInt());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+
+ switch (parser.next()) {
+ case "Open":
+ position.set(Position.KEY_DOOR, true);
+ break;
+ case "Close":
+ position.set(Position.KEY_DOOR, false);
+ break;
+ default:
+ break;
+ }
+
+ position.set(Position.KEY_ACCELERATION, parser.nextDouble());
+ position.set("nfc", parser.next());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java b/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
new file mode 100644
index 000000000..74f9e22ab
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 - 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 EsealProtocolEncoder extends StringProtocolEncoder {
+
+ public EsealProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(
+ command, "##S,eSeal,%s,256,3.0.8,%s,E##", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ case Command.TYPE_ALARM_ARM:
+ return formatCommand(
+ command, "##S,eSeal,%s,256,3.0.8,RC-Power Control,Power OFF,E##", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ALARM_DISARM:
+ return formatCommand(
+ command, "##S,eSeal,%s,256,3.0.8,RC-Unlock,E##", Command.KEY_UNIQUE_ID);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/EskyFrameDecoder.java b/src/main/java/org/traccar/protocol/EskyFrameDecoder.java
index 3175698fd..da24c1273 100644
--- a/src/org/traccar/protocol/EskyFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/EskyFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,24 +15,24 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class EskyFrameDecoder extends FrameDecoder {
+public class EskyFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
buf.readerIndex(buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 'E'));
int endIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 'E');
if (endIndex > 0) {
- return buf.readBytes(endIndex - buf.readerIndex());
+ return buf.readRetainedSlice(endIndex - buf.readerIndex());
} else {
- return buf.readBytes(buf.readableBytes()); // assume full frame
+ return buf.readRetainedSlice(buf.readableBytes()); // assume full frame
}
}
diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java
new file mode 100644
index 000000000..aaa92da58
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/EskyProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class EskyProtocol extends BaseProtocol {
+
+ public EskyProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new EskyFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
index d524224af..641b2e28f 100644
--- a/src/org/traccar/protocol/EskyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,25 +29,27 @@ import java.util.regex.Pattern;
public class EskyProtocolDecoder extends BaseProtocolDecoder {
- public EskyProtocolDecoder(EskyProtocol protocol) {
+ public EskyProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN = new PatternBuilder()
- .text("EO;") // header
+ .expression("..;") // header
.number("d+;") // index
.number("(d+);") // imei
.text("R;") // data type
- .number("(d+)").text("+") // satellites
+ .number("(d+)[+;]") // satellites
.number("(dd)(dd)(dd)") // date
- .number("(dd)(dd)(dd)").text("+") // time
- .number("(-?d+.d+)").text("+") // latitude
- .number("(-?d+.d+)").text("+") // longitude
- .number("(d+.d+)").text("+") // speed
- .number("(d+)").text("+") // course
- .text("0x").number("(d+)").text("+") // input
- .number("(d+)").text("+") // message type
- .number("(d+)").text("+") // odometer
+ .number("(dd)(dd)(dd)[+;]") // time
+ .number("(-?d+.d+)[+;]") // latitude
+ .number("(-?d+.d+)[+;]") // longitude
+ .number("(d+.d+)[+;]") // speed
+ .number("(d+)[+;]") // course
+ .groupBegin()
+ .text("0x").number("(d+)[+;]") // input
+ .number("(d+)[+;]") // message type
+ .number("(d+)[+;]") // odometer
+ .groupEnd("?")
.number("(d+)") // voltage
.any()
.compile();
@@ -65,8 +68,7 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_SATELLITES, parser.nextInt());
@@ -78,10 +80,13 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromMps(parser.nextDouble()));
position.setCourse(parser.nextDouble());
- position.set(Position.KEY_INPUT, parser.nextHexInt());
- position.set(Position.KEY_EVENT, parser.nextInt());
- position.set(Position.KEY_ODOMETER, parser.nextInt());
- position.set(Position.KEY_POWER, parser.nextInt());
+ if (parser.hasNext(3)) {
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_EVENT, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ }
+
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
return position;
}
diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
new file mode 100644
index 000000000..692fd4e99
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class ExtremTracProtocol extends BaseProtocol {
+
+ public ExtremTracProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new ExtremTracProtocolDecoder(ExtremTracProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ExtremTracProtocolDecoder.java b/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java
index 79e3306df..9fde6f0a0 100644
--- a/src/org/traccar/protocol/ExtremTracProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ExtremTracProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class ExtremTracProtocolDecoder extends BaseProtocolDecoder {
- public ExtremTracProtocolDecoder(ExtremTracProtocol protocol) {
+ public ExtremTracProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -61,8 +62,7 @@ public class ExtremTracProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java b/src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java
new file mode 100644
index 000000000..967b17a64
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FifotrackFrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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 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 FifotrackFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
+ if (index != -1) {
+ int length = index - buf.readerIndex() + 3 + Integer.parseInt(
+ buf.toString(buf.readerIndex() + 2, index - buf.readerIndex() - 2, StandardCharsets.US_ASCII));
+ if (buf.readableBytes() >= length + 2) {
+ ByteBuf frame = buf.readRetainedSlice(length);
+ buf.skipBytes(2); // delimiter
+ return frame;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/FifotrackProtocol.java b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
index f4ca450c0..4a0a12ed3 100644
--- a/src/org/traccar/protocol/FifotrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,29 +15,25 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.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 java.util.List;
+import org.traccar.model.Command;
public class FifotrackProtocol extends BaseProtocol {
public FifotrackProtocol() {
- super("fifotrack");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_REQUEST_PHOTO);
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new FifotrackProtocolDecoder(FifotrackProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FifotrackFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new FifotrackProtocolEncoder(FifotrackProtocol.this));
+ pipeline.addLast(new FifotrackProtocolDecoder(FifotrackProtocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
new file mode 100644
index 000000000..40e146e0b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2016 - 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
+
+ private ByteBuf photo;
+
+ public FifotrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$$")
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .number("x+,") // index
+ .expression("[^,]+,") // type
+ .number("(d+)?,") // alarm
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("([AV]),") // validity
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(-?d+),") // altitude
+ .number("(d+),") // odometer
+ .number("d+,") // runtime
+ .number("(x{4,8}),") // status
+ .number("(x+)?,") // input
+ .number("(x+)?,") // output
+ .number("(d+)|") // mcc
+ .number("(d+)|") // mnc
+ .number("(x+)|") // lac
+ .number("(x+),") // cid
+ .number("([x|]+)") // adc
+ .expression(",([^,]+)") // rfid
+ .expression(",([^*]*)").optional(2) // sensors
+ .any()
+ .compile();
+
+ private static final Pattern PATTERN_PHOTO = new PatternBuilder()
+ .text("$$")
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .any()
+ .number(",(d+),") // length
+ .expression("([^*]+)") // photo id
+ .text("*")
+ .number("xx")
+ .compile();
+
+ private static final Pattern PATTERN_PHOTO_DATA = new PatternBuilder()
+ .text("$$")
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .number("x+,") // index
+ .expression("[^,]+,") // type
+ .expression("([^,]+),") // photo id
+ .number("(d+),") // offset
+ .number("(d+),") // size
+ .compile();
+
+ private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) {
+ 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));
+ }
+ }
+
+ private String decodeAlarm(Integer alarm) {
+ if (alarm != null) {
+ switch (alarm) {
+ case 2:
+ return Position.ALARM_SOS;
+ case 14:
+ return Position.ALARM_LOW_POWER;
+ case 15:
+ return Position.ALARM_POWER_CUT;
+ case 16:
+ return Position.ALARM_POWER_RESTORED;
+ default:
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private Object decodeLocation(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_ALARM, decodeAlarm(parser.nextInt()));
+
+ position.setTime(parser.nextDateTime());
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_ODOMETER, parser.nextLong());
+ position.set(Position.KEY_STATUS, parser.nextHexLong());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt())));
+
+ String[] adc = parser.next().split("\\|");
+ for (int i = 0; i < adc.length; i++) {
+ position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16));
+ }
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(parser.nextHexInt()));
+ }
+
+ if (parser.hasNext()) {
+ String[] sensors = parser.next().split("\\|");
+ for (int i = 0; i < sensors.length; i++) {
+ position.set(Position.PREFIX_IO + (i + 1), sensors[i]);
+ }
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ int typeIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',') + 1;
+ typeIndex = buf.indexOf(typeIndex, buf.writerIndex(), (byte) ',') + 1;
+ typeIndex = buf.indexOf(typeIndex, buf.writerIndex(), (byte) ',') + 1;
+ String type = buf.toString(typeIndex, 3, StandardCharsets.US_ASCII);
+
+ if (type.equals("D05")) {
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
+ Parser parser = new Parser(PATTERN_PHOTO, sentence);
+ if (parser.matches()) {
+ String imei = parser.next();
+ int length = parser.nextInt();
+ String photoId = parser.next();
+ photo = Unpooled.buffer(length);
+ requestPhoto(channel, remoteAddress, imei, photoId);
+ }
+ } else if (type.equals("D06")) {
+ if (photo == null) {
+ return null;
+ }
+ int dataIndex = buf.indexOf(typeIndex + 4, buf.writerIndex(), (byte) ',') + 1;
+ dataIndex = buf.indexOf(dataIndex, buf.writerIndex(), (byte) ',') + 1;
+ dataIndex = buf.indexOf(dataIndex, buf.writerIndex(), (byte) ',') + 1;
+ String sentence = buf.toString(buf.readerIndex(), dataIndex, StandardCharsets.US_ASCII);
+ Parser parser = new Parser(PATTERN_PHOTO_DATA, sentence);
+ if (parser.matches()) {
+ String imei = parser.next();
+ String photoId = parser.next();
+ parser.nextInt(); // offset
+ parser.nextInt(); // size
+ buf.readerIndex(dataIndex);
+ buf.readBytes(photo, buf.readableBytes() - 3); // ignore checksum
+ if (photo.isWritable()) {
+ requestPhoto(channel, remoteAddress, imei, photoId);
+ } else {
+ 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"));
+ photo.release();
+ photo = null;
+ return position;
+ }
+ }
+ } else {
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
+ return decodeLocation(channel, remoteAddress, sentence);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolEncoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolEncoder.java
new file mode 100644
index 000000000..a4e69b47b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolEncoder.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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.helper.Checksum;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class FifotrackProtocolEncoder extends StringProtocolEncoder {
+
+ public FifotrackProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private Object formatCommand(Command command, String content) {
+ String uniqueId = getUniqueId(command.getDeviceId());
+ int length = 1 + uniqueId.length() + 3 + content.length();
+ String result = String.format("##%02d,%s,1,%s*", length, uniqueId, content);
+ result += Checksum.sum(result) + "\r\n";
+ return result;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, command.getString(Command.KEY_DATA));
+ case Command.TYPE_REQUEST_PHOTO:
+ return formatCommand(command, "D05,3");
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocol.java b/src/main/java/org/traccar/protocol/FlespiProtocol.java
new file mode 100644
index 000000000..2c0729b76
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlespiProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class FlespiProtocol extends BaseProtocol {
+
+ public FlespiProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
+ pipeline.addLast(new FlespiProtocolDecoder(FlespiProtocol.this));
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
new file mode 100644
index 000000000..7405fb6ef
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+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.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public FlespiProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ JsonArray result = Json.createReader(new StringReader(request.content().toString(StandardCharsets.UTF_8)))
+ .readArray();
+ 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) {
+ continue;
+ }
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ident.getString());
+ if (deviceSession == null) {
+ continue;
+ }
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ decodePosition(message, position);
+ positions.add(position);
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return positions;
+ }
+
+ private void decodePosition(JsonObject object, Position position) {
+ for (Map.Entry<String, JsonValue> param : object.entrySet()) {
+ String paramName = param.getKey();
+ JsonValue paramValue = param.getValue();
+ int index = -1;
+ if (paramName.contains("#")) {
+ String[] parts = paramName.split("#");
+ paramName = parts[0];
+ index = Integer.parseInt(parts[1]);
+ }
+ if (!decodeParam(paramName, index, paramValue, position)) {
+ decodeUnknownParam(param.getKey(), param.getValue(), position);
+ }
+ }
+ if (position.getLatitude() == 0 && position.getLongitude() == 0) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+ }
+
+ private boolean decodeParam(String name, int index, JsonValue value, Position position) {
+ switch (name) {
+ case "timestamp":
+ position.setTime(new Date(((JsonNumber) value).longValue() * 1000));
+ return true;
+ case "position.latitude":
+ position.setLatitude(((JsonNumber) value).doubleValue());
+ return true;
+ case "position.longitude":
+ position.setLongitude(((JsonNumber) value).doubleValue());
+ return true;
+ case "position.speed":
+ position.setSpeed(UnitsConverter.knotsFromKph(((JsonNumber) value).doubleValue()));
+ return true;
+ case "position.direction":
+ position.setCourse(((JsonNumber) value).doubleValue());
+ return true;
+ case "position.altitude":
+ position.setAltitude(((JsonNumber) value).doubleValue());
+ return true;
+ case "position.satellites":
+ position.set(Position.KEY_SATELLITES, ((JsonNumber) value).intValue());
+ return true;
+ case "position.valid":
+ position.setValid(value == JsonValue.TRUE);
+ return true;
+ case "position.hdop":
+ position.set(Position.KEY_HDOP, ((JsonNumber) value).doubleValue());
+ return true;
+ case "position.pdop":
+ position.set(Position.KEY_PDOP, ((JsonNumber) value).doubleValue());
+ return true;
+ case "din":
+ case "dout":
+ position.set(name.equals("din") ? Position.KEY_INPUT : Position.KEY_OUTPUT,
+ ((JsonNumber) value).intValue());
+ return true;
+ case "gps.vehicle.mileage":
+ position.set(Position.KEY_ODOMETER, ((JsonNumber) value).doubleValue());
+ return true;
+ case "external.powersource.voltage":
+ position.set(Position.KEY_POWER, ((JsonNumber) value).doubleValue());
+ return true;
+ case "battery.voltage":
+ position.set(Position.KEY_BATTERY, ((JsonNumber) value).doubleValue());
+ return true;
+ case "fuel.level":
+ case "can.fuel.level":
+ position.set(Position.KEY_FUEL_LEVEL, ((JsonNumber) value).doubleValue());
+ return true;
+ case "engine.rpm":
+ case "can.engine.rpm":
+ position.set(Position.KEY_RPM, ((JsonNumber) value).doubleValue());
+ return true;
+ case "can.engine.temperature":
+ position.set(Position.PREFIX_TEMP + (index > 0 ? index : 0), ((JsonNumber) value).doubleValue());
+ return true;
+ case "engine.ignition.status":
+ position.set(Position.KEY_IGNITION, value == JsonValue.TRUE);
+ return true;
+ case "movement.status":
+ position.set(Position.KEY_MOTION, value == JsonValue.TRUE);
+ return true;
+ case "device.temperature":
+ position.set(Position.KEY_DEVICE_TEMP, ((JsonNumber) value).doubleValue());
+ return true;
+ case "ibutton.code":
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, ((JsonString) value).getString());
+ return true;
+ case "vehicle.vin":
+ position.set(Position.KEY_VIN, ((JsonString) value).getString());
+ return true;
+ case "alarm.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+ return true;
+ case "towing.event.trigger":
+ case "towing.alarm.status":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ }
+ return true;
+ case "geofence.event.enter":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER);
+ }
+ return true;
+ case "geofence.event.exit":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT);
+ }
+ return true;
+ case "shock.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SHOCK);
+ }
+ return true;
+ case "overspeeding.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+ return true;
+ case "harsh.acceleration.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ }
+ return true;
+ case "harsh.braking.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ }
+ return true;
+ case "harsh.cornering.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ }
+ return true;
+ case "gnss.antenna.cut.status":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GPS_ANTENNA_CUT);
+ }
+ return true;
+ case "gsm.jamming.event.trigger":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
+ }
+ return true;
+ case "hood.open.status":
+ if (value == JsonValue.TRUE) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BONNET);
+ }
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void decodeUnknownParam(String name, JsonValue value, Position position) {
+ if (value instanceof JsonNumber) {
+ if (((JsonNumber) value).isIntegral()) {
+ position.set(name, ((JsonNumber) value).longValue());
+ } else {
+ position.set(name, ((JsonNumber) value).doubleValue());
+ }
+ position.set(name, ((JsonNumber) value).doubleValue());
+ } else if (value instanceof JsonString) {
+ position.set(name, ((JsonString) value).getString());
+ } else if (value == JsonValue.TRUE || value == JsonValue.FALSE) {
+ position.set(name, value == JsonValue.TRUE);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlexCommProtocol.java b/src/main/java/org/traccar/protocol/FlexCommProtocol.java
new file mode 100644
index 000000000..9343ebeb8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexCommProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.FixedLengthFrameDecoder;
+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;
+
+public class FlexCommProtocol extends BaseProtocol {
+
+ public FlexCommProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FixedLengthFrameDecoder(2 + 2 + 101 + 5));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new FlexCommProtocolDecoder(FlexCommProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/FlexCommProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java
index f401145b9..068c0a05c 100644
--- a/src/org/traccar/protocol/FlexCommProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlexCommProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -30,7 +32,7 @@ import java.util.regex.Pattern;
public class FlexCommProtocolDecoder extends BaseProtocolDecoder {
- public FlexCommProtocolDecoder(FlexCommProtocol protocol) {
+ public FlexCommProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -78,8 +80,7 @@ public class FlexCommProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_STATUS, parser.nextInt());
@@ -118,7 +119,7 @@ public class FlexCommProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, parser.nextInt() * 0.1);
if (channel != null) {
- channel.write("{01}");
+ channel.writeAndFlush(new NetworkMessage("{01}", remoteAddress));
}
return position;
diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocol.java b/src/main/java/org/traccar/protocol/FlextrackProtocol.java
new file mode 100644
index 000000000..ddd1d58f0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlextrackProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class FlextrackProtocol extends BaseProtocol {
+
+ public FlextrackProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new FlextrackProtocolDecoder(FlextrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/FlextrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java
index ab2e4d24c..9dce22ede 100644
--- a/src/org/traccar/protocol/FlextrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlextrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -30,7 +32,7 @@ import java.util.regex.Pattern;
public class FlextrackProtocolDecoder extends BaseProtocolDecoder {
- public FlextrackProtocolDecoder(FlextrackProtocol protocol) {
+ public FlextrackProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,9 +67,9 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)") // odometer
.compile();
- private void sendAcknowledgement(Channel channel, String index) {
+ private void sendAcknowledgement(Channel channel, SocketAddress remoteAddress, String index) {
if (channel != null) {
- channel.write(index + ",ACK\r");
+ channel.writeAndFlush(new NetworkMessage(index + ",ACK\r", remoteAddress));
}
}
@@ -84,7 +86,7 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- sendAcknowledgement(channel, parser.next());
+ sendAcknowledgement(channel, remoteAddress, parser.next());
String id = parser.next();
String iccid = parser.next();
@@ -103,11 +105,10 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- sendAcknowledgement(channel, parser.next());
+ sendAcknowledgement(channel, remoteAddress, parser.next());
position.setTime(parser.nextDateTime());
diff --git a/src/main/java/org/traccar/protocol/FoxProtocol.java b/src/main/java/org/traccar/protocol/FoxProtocol.java
new file mode 100644
index 000000000..9bac773b5
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FoxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class FoxProtocol extends BaseProtocol {
+
+ public FoxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "</fox>"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new FoxProtocolDecoder(FoxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/FoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java
index 9b2cf0e1d..449f00022 100644
--- a/src/org/traccar/protocol/FoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FoxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class FoxProtocolDecoder extends BaseProtocolDecoder {
- public FoxProtocolDecoder(FoxProtocol protocol) {
+ public FoxProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -88,8 +89,7 @@ public class FoxProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_STATUS, parser.nextInt(0));
diff --git a/src/main/java/org/traccar/protocol/FreedomProtocol.java b/src/main/java/org/traccar/protocol/FreedomProtocol.java
new file mode 100644
index 000000000..bc6b92d5f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FreedomProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class FreedomProtocol extends BaseProtocol {
+
+ public FreedomProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new FreedomProtocolDecoder(FreedomProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/FreedomProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java
index 56b6b9e19..1d2dd3133 100644
--- a/src/org/traccar/protocol/FreedomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FreedomProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class FreedomProtocolDecoder extends BaseProtocolDecoder {
- public FreedomProtocolDecoder(FreedomProtocol protocol) {
+ public FreedomProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -53,8 +54,7 @@ public class FreedomProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocol.java b/src/main/java/org/traccar/protocol/FreematicsProtocol.java
new file mode 100644
index 000000000..999b075a1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class FreematicsProtocol extends BaseProtocol {
+
+ public FreematicsProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..ba47699c3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+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 FreematicsProtocolDecoder extends BaseProtocolDecoder {
+
+ public FreematicsProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private Object decodeEvent(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ DeviceSession deviceSession = null;
+ String event = null;
+ String time = null;
+
+ for (String pair : sentence.split(",")) {
+ String[] data = pair.split("=");
+ String key = data[0];
+ String value = data[1];
+ switch (key) {
+ case "ID":
+ case "VIN":
+ if (deviceSession == null) {
+ deviceSession = getDeviceSession(channel, remoteAddress, value);
+ }
+ break;
+ case "EV":
+ event = value;
+ break;
+ case "TS":
+ time = value;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (channel != null && deviceSession != null && event != null && time != null) {
+ String message = String.format("1#EV=%s,RX=1,TS=%s", event, time);
+ message += '*' + Checksum.sum(message);
+ channel.writeAndFlush(new NetworkMessage(message, remoteAddress));
+ }
+
+ return null;
+ }
+
+ private Object decodePosition(
+ Channel channel, SocketAddress remoteAddress, String sentence) throws Exception {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+ Position position = null;
+ DateBuilder dateBuilder = null;
+
+ for (String pair : sentence.split(",")) {
+ String[] data = pair.split("[=:]");
+ int key = Integer.parseInt(data[0], 16);
+ String value = data[1];
+ switch (key) {
+ case 0x0:
+ if (position != null) {
+ position.setTime(dateBuilder.getDate());
+ positions.add(position);
+ }
+ position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setValid(true);
+ dateBuilder = new DateBuilder(new Date());
+ break;
+ case 0x11:
+ value = ("000000" + value).substring(value.length());
+ dateBuilder.setDateReverse(
+ Integer.parseInt(value.substring(0, 2)),
+ Integer.parseInt(value.substring(2, 4)),
+ Integer.parseInt(value.substring(4)));
+ break;
+ case 0x10:
+ value = ("00000000" + value).substring(value.length());
+ dateBuilder.setTime(
+ Integer.parseInt(value.substring(0, 2)),
+ Integer.parseInt(value.substring(2, 4)),
+ Integer.parseInt(value.substring(4, 6)),
+ Integer.parseInt(value.substring(6)) * 10);
+ break;
+ case 0xA:
+ position.setLatitude(Double.parseDouble(value));
+ break;
+ case 0xB:
+ position.setLongitude(Double.parseDouble(value));
+ break;
+ case 0xC:
+ position.setAltitude(Double.parseDouble(value));
+ break;
+ case 0xD:
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(value)));
+ break;
+ case 0xE:
+ position.setCourse(Integer.parseInt(value));
+ break;
+ case 0xF:
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
+ break;
+ case 0x20:
+ position.set(Position.KEY_ACCELERATION, value);
+ break;
+ case 0x24:
+ position.set(Position.KEY_BATTERY, Integer.parseInt(value) * 0.01);
+ break;
+ case 0x81:
+ position.set(Position.KEY_RSSI, Integer.parseInt(value));
+ break;
+ case 0x82:
+ position.set(Position.KEY_DEVICE_TEMP, Integer.parseInt(value) * 0.1);
+ break;
+ default:
+ position.set(data[0], value);
+ break;
+ }
+ }
+
+ if (position != null) {
+ position.setTime(dateBuilder.getDate());
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ int startIndex = sentence.indexOf('#');
+ int endIndex = sentence.indexOf('*');
+
+ if (startIndex > 0 && endIndex > 0) {
+ sentence = sentence.substring(startIndex + 1, endIndex);
+
+ if (sentence.startsWith("EV")) {
+ return decodeEvent(channel, remoteAddress, sentence);
+ } else {
+ return decodePosition(channel, remoteAddress, sentence);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/GalileoFrameDecoder.java b/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java
index 6d02ce744..c23d26c83 100644
--- a/src/org/traccar/protocol/GalileoFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,30 +15,26 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class GalileoFrameDecoder extends FrameDecoder {
+public class GalileoFrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_MINIMUM_LENGTH = 5;
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- // Check minimum length
if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
return null;
}
- // Read packet
- int length = buf.getUnsignedShort(buf.readerIndex() + 1) & 0x7fff;
+ int length = buf.getUnsignedShortLE(buf.readerIndex() + 1) & 0x7fff;
if (buf.readableBytes() >= (length + MESSAGE_MINIMUM_LENGTH)) {
- return buf.readBytes(length + MESSAGE_MINIMUM_LENGTH);
+ return buf.readRetainedSlice(length + MESSAGE_MINIMUM_LENGTH);
}
return null;
diff --git a/src/org/traccar/protocol/GalileoProtocol.java b/src/main/java/org/traccar/protocol/GalileoProtocol.java
index f76de04a0..a1570c9b0 100644
--- a/src/org/traccar/protocol/GalileoProtocol.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,36 +15,25 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.nio.ByteOrder;
-import java.util.List;
-
public class GalileoProtocol extends BaseProtocol {
public GalileoProtocol() {
- super("galileo");
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_OUTPUT_CONTROL);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new GalileoFrameDecoder());
- pipeline.addLast("objectEncoder", new GalileoProtocolEncoder());
- pipeline.addLast("objectDecoder", new GalileoProtocolDecoder(GalileoProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new GalileoFrameDecoder());
+ pipeline.addLast(new GalileoProtocolEncoder(GalileoProtocol.this));
+ pipeline.addLast(new GalileoProtocolDecoder(GalileoProtocol.this));
}
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
+ });
}
}
diff --git a/src/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
index 3944b37de..dfaedd695 100644
--- a/src/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,15 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
@@ -35,10 +39,12 @@ import java.util.Set;
public class GalileoProtocolDecoder extends BaseProtocolDecoder {
- public GalileoProtocolDecoder(GalileoProtocol protocol) {
+ public GalileoProtocolDecoder(Protocol protocol) {
super(protocol);
}
+ private ByteBuf photo;
+
private static final Map<Integer, Integer> TAG_LENGTH_MAP = new HashMap<>();
static {
@@ -90,16 +96,40 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
return length;
}
- private void sendReply(Channel channel, int checksum) {
- ChannelBuffer reply = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 3);
- reply.writeByte(0x02);
- reply.writeShort((short) checksum);
+ private void sendResponse(Channel channel, int header, int checksum) {
if (channel != null) {
- channel.write(reply);
+ ByteBuf reply = Unpooled.buffer(3);
+ reply.writeByte(header);
+ reply.writeShortLE((short) checksum);
+ channel.writeAndFlush(new NetworkMessage(reply, channel.remoteAddress()));
+ }
+ }
+
+ private void decodeTag(Position position, ByteBuf buf, int tag) {
+ if (tag >= 0x50 && tag <= 0x57) {
+ position.set(Position.PREFIX_ADC + (tag - 0x50), buf.readUnsignedShortLE());
+ } else if (tag >= 0x60 && tag <= 0x62) {
+ position.set("fuel" + (tag - 0x60), buf.readUnsignedShortLE());
+ } else if (tag >= 0xa0 && tag <= 0xaf) {
+ position.set("can8BitR" + (tag - 0xa0 + 15), buf.readUnsignedByte());
+ } else if (tag >= 0xb0 && tag <= 0xb9) {
+ position.set("can16BitR" + (tag - 0xb0 + 5), buf.readUnsignedShortLE());
+ } else if (tag >= 0xc4 && tag <= 0xd2) {
+ position.set("can8BitR" + (tag - 0xc4), buf.readUnsignedByte());
+ } else if (tag >= 0xd6 && tag <= 0xda) {
+ position.set("can16BitR" + (tag - 0xd6), buf.readUnsignedShortLE());
+ } else if (tag >= 0xdb && tag <= 0xdf) {
+ position.set("can32BitR" + (tag - 0xdb), buf.readUnsignedIntLE());
+ } else if (tag >= 0xe2 && tag <= 0xe9) {
+ position.set("userData" + (tag - 0xe2), buf.readUnsignedIntLE());
+ } else if (tag >= 0xf0 && tag <= 0xf9) {
+ position.set("can32BitR" + (tag - 0xf0 + 5), buf.readUnsignedIntLE());
+ } else {
+ decodeTagOther(position, buf, tag);
}
}
- private void decodeTag(Position position, ChannelBuffer buf, int tag) {
+ private void decodeTagOther(Position position, ByteBuf buf, int tag) {
switch (tag) {
case 0x01:
position.set(Position.KEY_VERSION_HW, buf.readUnsignedByte());
@@ -108,125 +138,84 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
break;
case 0x04:
- position.set("deviceId", buf.readUnsignedShort());
+ position.set("deviceId", buf.readUnsignedShortLE());
break;
case 0x10:
- position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ position.set(Position.KEY_INDEX, buf.readUnsignedShortLE());
break;
case 0x20:
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
break;
case 0x33:
- position.setSpeed(buf.readUnsignedShort() * 0.0539957);
- position.setCourse(buf.readUnsignedShort() * 0.1);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE() * 0.1));
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
break;
case 0x34:
- position.setAltitude(buf.readShort());
+ position.setAltitude(buf.readShortLE());
+ break;
+ case 0x35:
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
break;
case 0x40:
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+ position.set(Position.KEY_STATUS, buf.readUnsignedShortLE());
break;
case 0x41:
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() / 1000.0);
break;
case 0x42:
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() / 1000.0);
break;
case 0x43:
position.set(Position.KEY_DEVICE_TEMP, buf.readByte());
break;
case 0x44:
- position.set(Position.KEY_ACCELERATION, buf.readUnsignedInt());
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedIntLE());
break;
case 0x45:
- position.set(Position.KEY_OUTPUT, buf.readUnsignedShort());
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedShortLE());
break;
case 0x46:
- position.set(Position.KEY_INPUT, buf.readUnsignedShort());
+ position.set(Position.KEY_INPUT, buf.readUnsignedShortLE());
break;
- case 0x50:
- case 0x51:
- case 0x52:
- case 0x53:
- case 0x54:
- case 0x55:
- case 0x56:
- case 0x57:
- position.set(Position.PREFIX_ADC + (tag - 0x50), buf.readUnsignedShort());
+ case 0x48:
+ position.set("statusExtended", buf.readUnsignedShortLE());
break;
case 0x58:
- position.set("rs2320", buf.readUnsignedShort());
+ position.set("rs2320", buf.readUnsignedShortLE());
break;
case 0x59:
- position.set("rs2321", buf.readUnsignedShort());
+ position.set("rs2321", buf.readUnsignedShortLE());
+ break;
+ case 0x90:
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedIntLE()));
break;
case 0xc0:
- position.set("fuelTotal", buf.readUnsignedInt() * 0.5);
+ position.set("fuelTotal", buf.readUnsignedIntLE() * 0.5);
break;
case 0xc1:
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4);
position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte() - 40);
- position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125);
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE() * 0.125);
break;
case 0xc2:
- position.set("canB0", buf.readUnsignedInt());
+ position.set("canB0", buf.readUnsignedIntLE());
break;
case 0xc3:
- position.set("canB1", buf.readUnsignedInt());
- break;
- case 0xc4:
- case 0xc5:
- case 0xc6:
- case 0xc7:
- case 0xc8:
- case 0xc9:
- case 0xca:
- case 0xcb:
- case 0xcc:
- case 0xcd:
- case 0xce:
- case 0xcf:
- case 0xd0:
- case 0xd1:
- case 0xd2:
- position.set("can8Bit" + (tag - 0xc4), buf.readUnsignedByte());
- break;
- case 0xd6:
- case 0xd7:
- case 0xd8:
- case 0xd9:
- case 0xda:
- position.set("can16Bit" + (tag - 0xd6), buf.readUnsignedShort());
- break;
- case 0xdb:
- case 0xdc:
- case 0xdd:
- case 0xde:
- case 0xdf:
- position.set("can32Bit" + (tag - 0xdb), buf.readUnsignedInt());
+ position.set("canB1", buf.readUnsignedIntLE());
break;
case 0xd4:
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
break;
case 0xe0:
- position.set(Position.KEY_INDEX, buf.readUnsignedInt());
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
break;
case 0xe1:
position.set(Position.KEY_RESULT,
- buf.readBytes(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII));
- break;
- case 0xe2:
- case 0xe3:
- case 0xe4:
- case 0xe5:
- case 0xe6:
- case 0xe7:
- case 0xe8:
- case 0xe9:
- position.set("userData" + (tag - 0xe2), buf.readUnsignedInt());
+ buf.readSlice(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII));
break;
case 0xea:
- position.set("userDataArray", ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedByte())));
+ position.set("userDataArray", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())));
+ position.set("userDataArray", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())));
break;
default:
buf.skipBytes(getTagLength(tag));
@@ -238,21 +227,32 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
+
+ int header = buf.readUnsignedByte();
+ if (header == 0x01) {
+ return decodePositions(channel, remoteAddress, buf);
+ } else if (header == 0x07) {
+ return decodePhoto(channel, remoteAddress, buf);
+ }
+
+ return null;
+ }
+
+ private Object decodePositions(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
- buf.readUnsignedByte(); // header
- int length = (buf.readUnsignedShort() & 0x7fff) + 3;
+ int length = (buf.readUnsignedShortLE() & 0x7fff) + 3;
List<Position> positions = new LinkedList<>();
Set<Integer> tags = new HashSet<>();
boolean hasLocation = false;
DeviceSession deviceSession = null;
- Position position = new Position();
+ Position position = new Position(getProtocolName());
while (buf.readerIndex() < length) {
- // Check if new message started
int tag = buf.readUnsignedByte();
if (tags.contains(tag)) {
if (hasLocation && position.getFixTime() != null) {
@@ -260,31 +260,24 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
tags.clear();
hasLocation = false;
- position = new Position();
+ position = new Position(getProtocolName()); // new position starts
}
tags.add(tag);
if (tag == 0x03) {
deviceSession = getDeviceSession(
- channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
+ channel, remoteAddress, buf.readSlice(15).toString(StandardCharsets.US_ASCII));
} else if (tag == 0x30) {
hasLocation = true;
position.setValid((buf.readUnsignedByte() & 0xf0) == 0x00);
- position.setLatitude(buf.readInt() / 1000000.0);
- position.setLongitude(buf.readInt() / 1000000.0);
+ position.setLatitude(buf.readIntLE() / 1000000.0);
+ position.setLongitude(buf.readIntLE() / 1000000.0);
} else {
decodeTag(position, buf, tag);
}
}
- if (hasLocation && position.getFixTime() != null) {
- positions.add(position);
- } else if (position.getAttributes().containsKey(Position.KEY_RESULT)) {
- getLastLocation(position, null);
- positions.add(position);
- }
-
if (deviceSession == null) {
deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
@@ -292,14 +285,58 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
}
- sendReply(channel, buf.readUnsignedShort());
+ if (hasLocation && position.getFixTime() != null) {
+ positions.add(position);
+ } else if (position.getAttributes().containsKey(Position.KEY_RESULT)) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ positions.add(position);
+ }
+
+ sendResponse(channel, 0x02, buf.readUnsignedShortLE());
for (Position p : positions) {
- p.setProtocol(getProtocolName());
p.setDeviceId(deviceSession.getDeviceId());
}
return positions.isEmpty() ? null : positions;
}
+ private Object decodePhoto(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
+
+ int length = buf.readUnsignedShortLE();
+
+ Position position = null;
+
+ if (length > 1) {
+
+ if (photo == null) {
+ photo = Unpooled.buffer();
+ }
+
+ buf.readUnsignedByte(); // part number
+ photo.writeBytes(buf, length - 1);
+
+ sendResponse(channel, 0x07, buf.readUnsignedShortLE());
+
+ } else if (photo != null) {
+
+ 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"));
+ photo.release();
+ photo = null;
+
+ }
+
+ return position;
+ }
+
}
diff --git a/src/org/traccar/protocol/GalileoProtocolEncoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolEncoder.java
index cb6028abb..cd068b251 100644
--- a/src/org/traccar/protocol/GalileoProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,39 +15,42 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
public class GalileoProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeText(String uniqueId, String text) {
+ public GalileoProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeText(String uniqueId, String text) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 256);
+ ByteBuf buf = Unpooled.buffer(256);
buf.writeByte(0x01);
- buf.writeShort(uniqueId.length() + text.length() + 11); // TODO
+ buf.writeShortLE(uniqueId.length() + text.length() + 11);
buf.writeByte(0x03); // imei tag
buf.writeBytes(uniqueId.getBytes(StandardCharsets.US_ASCII));
buf.writeByte(0x04); // device id tag
- buf.writeShort(0); // not needed if imei provided
+ buf.writeShortLE(0); // not needed if imei provided
buf.writeByte(0xE0); // index tag
- buf.writeInt(0); // index
+ buf.writeIntLE(0); // index
buf.writeByte(0xE1); // command text tag
buf.writeByte(text.length());
buf.writeBytes(text.getBytes(StandardCharsets.US_ASCII));
- buf.writeShort(Checksum.crc16(Checksum.CRC16_MODBUS, buf.toByteBuffer(0, buf.writerIndex())));
+ buf.writeShortLE(Checksum.crc16(Checksum.CRC16_MODBUS, buf.nioBuffer(0, buf.writerIndex())));
return buf;
}
@@ -62,11 +65,8 @@ public class GalileoProtocolEncoder extends BaseProtocolEncoder {
return encodeText(getUniqueId(command.getDeviceId()),
"Out " + command.getInteger(Command.KEY_INDEX) + "," + command.getString(Command.KEY_DATA));
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/GatorProtocol.java b/src/main/java/org/traccar/protocol/GatorProtocol.java
new file mode 100644
index 000000000..ca81caefb
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GatorProtocol.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class GatorProtocol extends BaseProtocol {
+
+ public GatorProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2));
+ pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
index 2ad4be3d3..043839be9 100644
--- a/src/org/traccar/protocol/GatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
@@ -29,7 +31,7 @@ import java.net.SocketAddress;
public class GatorProtocolDecoder extends BaseProtocolDecoder {
- public GatorProtocolDecoder(GatorProtocol protocol) {
+ public GatorProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -56,9 +58,9 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
return String.format("%02d%02d%02d%02d%02d", d1, d2, d3, d4, d5);
}
- private void sendResponse(Channel channel, byte calibration) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, byte calibration) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(0x24); response.writeByte(0x24); // header
response.writeByte(MSG_HEARTBEAT); // size
response.writeShort(5);
@@ -67,7 +69,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
response.writeByte(0); // slave order
response.writeByte(1); // calibration
response.writeByte(0x0D);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
@@ -75,7 +77,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
int type = buf.readUnsignedByte();
@@ -85,13 +87,12 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(), buf.readUnsignedByte(),
buf.readUnsignedByte(), buf.readUnsignedByte());
- sendResponse(channel, buf.getByte(buf.writerIndex() - 2));
+ sendResponse(channel, remoteAddress, buf.getByte(buf.writerIndex() - 2));
if (type == MSG_POSITION_DATA || type == MSG_ROLLCALL_RESPONSE
|| type == MSG_ALARM_DATA || type == MSG_BLIND_AREA) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, "1" + id, id);
if (deviceSession == null) {
@@ -119,8 +120,10 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_STATUS, buf.readUnsignedByte());
position.set("key", buf.readUnsignedByte());
- position.set("oil", buf.readUnsignedShort() / 10.0);
- position.set(Position.KEY_POWER, buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
return position;
diff --git a/src/main/java/org/traccar/protocol/GenxProtocol.java b/src/main/java/org/traccar/protocol/GenxProtocol.java
new file mode 100644
index 000000000..c87ba946a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GenxProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GenxProtocol extends BaseProtocol {
+
+ public GenxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new GenxProtocolDecoder(GenxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GenxProtocolDecoder.java b/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java
index ebf6f2b53..2ae9de7a0 100644
--- a/src/org/traccar/protocol/GenxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GenxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,7 +30,7 @@ public class GenxProtocolDecoder extends BaseProtocolDecoder {
private int[] reportColumns;
- public GenxProtocolDecoder(GenxProtocol protocol) {
+ public GenxProtocolDecoder(Protocol protocol) {
super(protocol);
setReportColumns(Context.getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4"));
}
@@ -48,13 +49,13 @@ public class GenxProtocolDecoder extends BaseProtocolDecoder {
String[] values = ((String) msg).split(",");
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setValid(true);
for (int i = 0; i < Math.min(values.length, reportColumns.length); i++) {
switch (reportColumns[i]) {
case 1:
+ case 28:
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[i]);
if (deviceSession != null) {
position.setDeviceId(deviceSession.getDeviceId());
diff --git a/src/main/java/org/traccar/protocol/Gl100Protocol.java b/src/main/java/org/traccar/protocol/Gl100Protocol.java
new file mode 100644
index 000000000..063e606db
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gl100Protocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Gl100Protocol extends BaseProtocol {
+
+ public Gl100Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Gl100ProtocolDecoder(Gl100Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java
index 945edfff0..ae0383e5c 100644
--- a/src/org/traccar/protocol/Gl100ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl100ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +29,7 @@ import java.util.regex.Pattern;
public class Gl100ProtocolDecoder extends BaseProtocolDecoder {
- public Gl100ProtocolDecoder(Gl100Protocol protocol) {
+ public Gl100ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -64,7 +66,7 @@ public class Gl100ProtocolDecoder extends BaseProtocolDecoder {
String response = "+RESP:GTHBD,GPRS ACTIVE,";
response += sentence.substring(9, sentence.lastIndexOf(','));
response += '\0';
- channel.write(response); // heartbeat response
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); // heartbeat response
}
Parser parser = new Parser(PATTERN, sentence);
@@ -72,8 +74,7 @@ public class Gl100ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
index 071960e49..c3339bea5 100644
--- a/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitBuffer;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
@@ -35,11 +36,11 @@ import java.util.List;
public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
- public Gl200BinaryProtocolDecoder(Gl200Protocol protocol) {
+ public Gl200BinaryProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private Date decodeTime(ChannelBuffer buf) {
+ private Date decodeTime(ByteBuf buf) {
DateBuilder dateBuilder = new DateBuilder()
.setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte())
.setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
@@ -50,7 +51,7 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_RSP_GEO = 8;
public static final int MSG_RSP_COMPRESSED = 100;
- private List<Position> decodeLocation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private List<Position> decodeLocation(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
List<Position> positions = new LinkedList<>();
@@ -108,13 +109,12 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
time += 1;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
switch (BitUtil.from(buf.getUnsignedByte(buf.readerIndex()), 8 - 2)) {
case 1:
- bits = new BitBuffer(buf.readBytes(3));
+ bits = new BitBuffer(buf.readSlice(3));
bits.readUnsigned(2); // point attribute
bits.readUnsigned(1); // fix type
speed = bits.readUnsigned(12);
@@ -126,7 +126,7 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
}
break;
case 2:
- bits = new BitBuffer(buf.readBytes(5));
+ bits = new BitBuffer(buf.readSlice(5));
bits.readUnsigned(2); // point attribute
bits.readUnsigned(1); // fix type
speed += bits.readSigned(7);
@@ -156,8 +156,7 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
for (int i = 0; i < count; i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_BATTERY_LEVEL, battery);
@@ -204,10 +203,9 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_EVT_CRA = 23;
public static final int MSG_EVT_UPC = 34;
- private Position decodeEvent(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Position decodeEvent(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
int type = buf.readUnsignedByte();
@@ -311,10 +309,9 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_INF_TMZ = 9;
public static final int MSG_INF_GIR = 10;
- private Position decodeInformation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Position decodeInformation(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
int type = buf.readUnsignedByte();
@@ -389,9 +386,9 @@ public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- switch (buf.readBytes(4).toString(StandardCharsets.US_ASCII)) {
+ switch (buf.readSlice(4).toString(StandardCharsets.US_ASCII)) {
case "+RSP":
return decodeLocation(channel, remoteAddress, buf);
case "+INF":
diff --git a/src/org/traccar/protocol/Gl200FrameDecoder.java b/src/main/java/org/traccar/protocol/Gl200FrameDecoder.java
index 960c3779a..c192cc28d 100644
--- a/src/org/traccar/protocol/Gl200FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,24 +15,25 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.traccar.BaseFrameDecoder;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
-public class Gl200FrameDecoder extends FrameDecoder {
+public class Gl200FrameDecoder extends BaseFrameDecoder {
private static final int MINIMUM_LENGTH = 11;
private static final Set<String> BINARY_HEADERS = new HashSet<>(
Arrays.asList("+RSP", "+BSP", "+EVT", "+BVT", "+INF", "+BNF", "+HBD", "+CRD", "+BRD"));
- public static boolean isBinary(ChannelBuffer buf) {
+ public static boolean isBinary(ByteBuf buf) {
String header = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII);
if (header.equals("+ACK")) {
return buf.getByte(buf.readerIndex() + header.length()) != (byte) ':';
@@ -43,7 +44,7 @@ public class Gl200FrameDecoder extends FrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < MINIMUM_LENGTH) {
return null;
@@ -73,7 +74,7 @@ public class Gl200FrameDecoder extends FrameDecoder {
}
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
} else {
@@ -83,7 +84,7 @@ public class Gl200FrameDecoder extends FrameDecoder {
endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
}
if (endIndex > 0) {
- ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex());
+ ByteBuf frame = buf.readRetainedSlice(endIndex - buf.readerIndex());
buf.readByte(); // delimiter
return frame;
}
diff --git a/src/main/java/org/traccar/protocol/Gl200Protocol.java b/src/main/java/org/traccar/protocol/Gl200Protocol.java
new file mode 100644
index 000000000..e2d0c6d2a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gl200Protocol.java
@@ -0,0 +1,53 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+import io.netty.handler.codec.string.StringEncoder;
+
+public class Gl200Protocol extends BaseProtocol {
+
+ public Gl200Protocol() {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Gl200ProtocolEncoder(Gl200Protocol.this));
+ pipeline.addLast(new Gl200ProtocolDecoder(Gl200Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
index 0de7bb926..ca1df7a13 100644
--- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,10 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.Protocol;
+
import java.net.SocketAddress;
public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
@@ -26,7 +28,7 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
private final Gl200TextProtocolDecoder textProtocolDecoder;
private final Gl200BinaryProtocolDecoder binaryProtocolDecoder;
- public Gl200ProtocolDecoder(Gl200Protocol protocol) {
+ public Gl200ProtocolDecoder(Protocol protocol) {
super(protocol);
textProtocolDecoder = new Gl200TextProtocolDecoder(protocol);
binaryProtocolDecoder = new Gl200BinaryProtocolDecoder(protocol);
@@ -36,7 +38,7 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (Gl200FrameDecoder.isBinary(buf)) {
return binaryProtocolDecoder.decode(channel, remoteAddress, msg);
diff --git a/src/org/traccar/protocol/Gl200ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
index 2c8efa318..dd0672c23 100644
--- a/src/org/traccar/protocol/Gl200ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -16,11 +16,15 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class Gl200ProtocolEncoder extends StringProtocolEncoder {
+ public Gl200ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -28,23 +32,20 @@ public class Gl200ProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "AT+GTRTO={%s},1,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "AT+GTRTO=%s,1,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "AT+GTOUT={%s},1,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
+ return formatCommand(command, "AT+GTOUT=%s,1,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "AT+GTOUT={%s},0,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
+ return formatCommand(command, "AT+GTOUT=%s,0,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_IDENTIFICATION:
- return formatCommand(command, "AT+GTRTO={%s},8,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "AT+GTRTO=%s,8,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "AT+GTRTO={%s},3,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "AT+GTRTO=%s,3,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 2f03cbb8f..283dbeb37 100644
--- a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,11 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.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.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -29,10 +29,17 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
+import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -40,7 +47,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
private boolean ignoreFixTime;
- public Gl200TextProtocolDecoder(Gl200Protocol protocol) {
+ public Gl200TextProtocolDecoder(Protocol protocol) {
super(protocol);
ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime");
@@ -112,9 +119,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN_LOCATION = new PatternBuilder()
- .number("(d{1,2})?,") // hdop
+ .number("(d{1,2}.?d?)?,") // hdop
.number("(d{1,3}.d)?,") // speed
- .number("(d{1,3})?,") // course
+ .number("(d{1,3}.?d?)?,") // course
.number("(-?d{1,5}.d)?,") // altitude
.number("(-?d{1,3}.d{6})?,") // longitude
.number("(-?d{1,2}.d{6})?,") // latitude
@@ -169,21 +176,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN_FRI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTFRI,")
+ .text("+").expression("(?:RESP|BUFF):GT...,")
.number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("(?:([0-9A-Z]{17}),)?") // vin
.expression("[^,]*,") // device name
.number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
+ .number("d{1,2},").optional() // report type
+ .number("d{1,2},").optional() // count
+ .number("d*,").optional() // reserved
+ .number("(d+),").optional() // battery
.expression("((?:")
.expression(PATTERN_LOCATION.pattern())
.expression(")+)")
.groupBegin()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .or()
.number("(d{1,7}.d)?,") // odometer
.number("(d{5}:dd:dd)?,") // hour meter
.number("(x+)?,") // adc 1
@@ -193,7 +199,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)?,") // rpm
.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
.groupEnd()
+ .any()
.number("(dddd)(dd)(dd)") // date (yyyymmdd)
.number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
.text(",")
@@ -206,7 +219,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("[^,]*,") // device name
- .number("x{8},") // mask
+ .number("(x{8}),") // mask
.number("(d+)?,") // power
.number("d{1,2},") // report type
.number("d{1,2},") // count
@@ -216,7 +229,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(d{1,7}.d)?,") // odometer
.number("(d{5}:dd:dd)?,") // hour meter
.number("(x+)?,") // adc 1
- .number("(x+)?,") // adc 2
+ .number("(x+)?,").optional() // adc 2
.number("(d{1,3})?,") // battery
.number("(?:(xx)(xx)(xx))?,") // device status
.expression("(.*)") // additional data
@@ -243,6 +256,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.text("$").optional()
.compile();
+ private static final Pattern PATTERN_LSW = new PatternBuilder()
+ .text("+RESP:").expression("GT[LT]SW,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("[01],") // type
+ .number("([01]),") // state
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
private static final Pattern PATTERN_IDA = new PatternBuilder()
.text("+RESP:GTIDA,")
.number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
@@ -289,13 +317,25 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.text("$").optional()
.compile();
+ private static final Pattern PATTERN_PNA = new PatternBuilder()
+ .text("+RESP:GT").expression("P[NF]A,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
private static final Pattern PATTERN = new PatternBuilder()
.text("+").expression("(?:RESP|BUFF):GT...,")
.number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("[^,]*,") // device name
.number("d*,")
- .number("(d{1,2}),") // report type
+ .number("(x{1,2}),") // report type
.number("d{1,2},") // count
.expression(PATTERN_LOCATION.pattern())
.groupBegin()
@@ -349,11 +389,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (type.equals("HBD")) {
if (channel != null) {
parser.skip(6);
- channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(
+ "+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress));
}
} else {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, parser.nextDateTime());
position.setValid(false);
@@ -368,8 +408,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (parser.matches()) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession != null) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
return position;
}
@@ -387,6 +426,16 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private Long parseHours(String hoursString) {
+ if (hoursString != null) {
+ String[] hours = hoursString.split(":");
+ return (long) (Integer.parseInt(hours[0]) * 3600
+ + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60 : 0)
+ + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000;
+ }
+ return null;
+ }
+
private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
Parser parser = new Parser(PATTERN_INF, sentence);
Position position = initPosition(parser, channel, remoteAddress);
@@ -427,8 +476,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
parser.next(); // odometer or external power
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
- position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_CHARGE, parser.nextInt() == 1);
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
@@ -442,7 +491,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, parser.nextDateTime());
- position.set(Position.KEY_INDEX, parser.nextHexInt(0));
+ position.set(Position.KEY_INDEX, parser.nextHexInt());
return position;
}
@@ -455,17 +504,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
position.set("deviceType", parser.next());
- position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0));
- position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0));
+ position.set(Position.KEY_VERSION_FW, parser.nextHexInt());
+ position.set(Position.KEY_VERSION_HW, parser.nextHexInt());
getLastLocation(position, parser.nextDateTime());
return position;
}
+ private void skipLocation(Parser parser) {
+ parser.skip(19);
+ }
+
private void decodeLocation(Position position, Parser parser) {
- int hdop = parser.nextInt(0);
- position.setValid(hdop > 0);
+ Double hdop = parser.nextDouble();
+ position.setValid(hdop == null || hdop > 0);
position.set(Position.KEY_HDOP, hdop);
position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
@@ -474,25 +527,27 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext(8)) {
position.setValid(true);
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
position.setTime(parser.nextDateTime());
} else {
getLastLocation(position, null);
}
if (parser.hasNext(6)) {
- int mcc = parser.nextInt(0);
- int mnc = parser.nextInt(0);
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0))));
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt())));
}
if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0))));
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt())));
}
}
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
}
private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
@@ -507,32 +562,235 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
position.set("dtcsClearedDistance", parser.nextInt());
- position.set("odbConnect", parser.nextInt(0) == 1);
+ if (parser.hasNext()) {
+ position.set("odbConnect", parser.nextInt() == 1);
+ }
position.set("dtcsNumber", parser.nextInt());
position.set("dtcsCodes", parser.next());
position.set(Position.KEY_THROTTLE, parser.nextInt());
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
- position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000);
+ if (parser.hasNext()) {
+ position.set(Position.KEY_OBD_ODOMETER, parser.nextInt() * 1000);
+ }
decodeLocation(position, parser);
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ if (parser.hasNext()) {
+ position.set(Position.KEY_OBD_ODOMETER, (int) (parser.nextDouble() * 1000));
+ }
decodeDeviceTime(position, parser);
return position;
}
+ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
+ Position position = new Position(getProtocolName());
+
+ int index = 0;
+ String[] values = sentence.split(",");
+
+ index += 1; // header
+ index += 1; // protocol version
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ index += 1; // device name
+ index += 1; // report type
+ index += 1; // canbus state
+ long reportMask = Long.parseLong(values[index++], 16);
+ long reportMaskExt = 0;
+
+ if (BitUtil.check(reportMask, 0)) {
+ position.set(Position.KEY_VIN, values[index++]);
+ }
+ if (BitUtil.check(reportMask, 1)) {
+ position.set(Position.KEY_IGNITION, Integer.parseInt(values[index++]) > 0);
+ }
+ if (BitUtil.check(reportMask, 2)) {
+ position.set(Position.KEY_OBD_ODOMETER, values[index++]);
+ }
+ if (BitUtil.check(reportMask, 3) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 5) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_RPM, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 4) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1])));
+ }
+ if (BitUtil.check(reportMask, 6) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 7) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(values[index - 1].substring(1)));
+ }
+ if (BitUtil.check(reportMask, 8) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index - 1].substring(1)));
+ }
+ if (BitUtil.check(reportMask, 9) && !values[index++].isEmpty()) {
+ position.set("range", Long.parseLong(values[index - 1]) * 100);
+ }
+ if (BitUtil.check(reportMask, 10) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 11) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1])));
+ }
+ if (BitUtil.check(reportMask, 12)) {
+ position.set("drivingHours", Double.parseDouble(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 13)) {
+ position.set("idleHours", Double.parseDouble(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 14) && !values[index++].isEmpty()) {
+ position.set("idleFuelConsumption", Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 15) && !values[index++].isEmpty()) {
+ position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 16) && !values[index++].isEmpty()) {
+ position.set("tachographInfo", Integer.parseInt(values[index - 1], 16));
+ }
+ if (BitUtil.check(reportMask, 17) && !values[index++].isEmpty()) {
+ position.set("indicators", Integer.parseInt(values[index - 1], 16));
+ }
+ if (BitUtil.check(reportMask, 18) && !values[index++].isEmpty()) {
+ position.set("lights", Integer.parseInt(values[index - 1], 16));
+ }
+ if (BitUtil.check(reportMask, 19) && !values[index++].isEmpty()) {
+ position.set("doors", Integer.parseInt(values[index - 1], 16));
+ }
+ if (BitUtil.check(reportMask, 20) && !values[index++].isEmpty()) {
+ position.set("vehicleOverspeed", Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 21) && !values[index++].isEmpty()) {
+ position.set("engineOverspeed", Double.parseDouble(values[index - 1]));
+ }
+ if (BitUtil.check(reportMask, 29)) {
+ reportMaskExt = Long.parseLong(values[index++], 16);
+ }
+ if (BitUtil.check(reportMaskExt, 0) && !values[index++].isEmpty()) {
+ position.set("adBlueLevel", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 1) && !values[index++].isEmpty()) {
+ position.set("axleWeight1", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 2) && !values[index++].isEmpty()) {
+ position.set("axleWeight3", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 3) && !values[index++].isEmpty()) {
+ position.set("axleWeight4", Integer.parseInt(values[index - 1]));
+ }
+ if (BitUtil.check(reportMaskExt, 4)) {
+ index += 1; // tachograph overspeed
+ }
+ if (BitUtil.check(reportMaskExt, 5)) {
+ index += 1; // tachograph motion
+ }
+ if (BitUtil.check(reportMaskExt, 6)) {
+ index += 1; // tachograph direction
+ }
+ if (BitUtil.check(reportMaskExt, 7) && !values[index++].isEmpty()) {
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[index - 1]) * 0.001);
+ }
+ if (BitUtil.check(reportMaskExt, 8)) {
+ index += 1; // pedal breaking factor
+ }
+ if (BitUtil.check(reportMaskExt, 9)) {
+ index += 1; // engine breaking factor
+ }
+ if (BitUtil.check(reportMaskExt, 10)) {
+ index += 1; // total accelerator kick-downs
+ }
+ if (BitUtil.check(reportMaskExt, 11)) {
+ index += 1; // total effective engine speed
+ }
+ if (BitUtil.check(reportMaskExt, 12)) {
+ index += 1; // total cruise control time
+ }
+ if (BitUtil.check(reportMaskExt, 13)) {
+ index += 1; // total accelerator kick-down time
+ }
+ if (BitUtil.check(reportMaskExt, 14)) {
+ index += 1; // total brake application
+ }
+ if (BitUtil.check(reportMaskExt, 15) && !values[index++].isEmpty()) {
+ position.set("driver1Card", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 16) && !values[index++].isEmpty()) {
+ position.set("driver2Card", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 17) && !values[index++].isEmpty()) {
+ position.set("driver1Name", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 18) && !values[index++].isEmpty()) {
+ position.set("driver2Name", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 19) && !values[index++].isEmpty()) {
+ position.set("registration", values[index - 1]);
+ }
+ if (BitUtil.check(reportMaskExt, 20)) {
+ index += 1; // expansion information
+ }
+ if (BitUtil.check(reportMaskExt, 21)) {
+ index += 1; // rapid brakings
+ }
+ if (BitUtil.check(reportMaskExt, 22)) {
+ index += 1; // rapid accelerations
+ }
+ if (BitUtil.check(reportMaskExt, 23)) {
+ index += 1; // engine torque
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ if (BitUtil.check(reportMask, 30)) {
+ while (values[index].isEmpty()) {
+ index += 1;
+ }
+ position.setValid(Integer.parseInt(values[index++]) > 0);
+ if (!values[index].isEmpty()) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Integer.parseInt(values[index++]));
+ position.setAltitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setTime(dateFormat.parse(values[index++]));
+ } else {
+ index += 6; // no location
+ getLastLocation(position, null);
+ }
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (BitUtil.check(reportMask, 31)) {
+ index += 4; // cell
+ index += 1; // reserved
+ }
+
+ if (ignoreFixTime) {
+ position.setTime(dateFormat.parse(values[index]));
+ } else {
+ position.setDeviceTime(dateFormat.parse(values[index]));
+ }
+
+ return position;
+ }
+
private void decodeStatus(Position position, Parser parser) {
if (parser.hasNext(3)) {
- int ignition = parser.nextHexInt(0);
+ int ignition = parser.nextHexInt();
if (BitUtil.check(ignition, 4)) {
position.set(Position.KEY_IGNITION, false);
} else if (BitUtil.check(ignition, 5)) {
position.set(Position.KEY_IGNITION, true);
}
- position.set(Position.KEY_INPUT, parser.nextHexInt(0));
- position.set(Position.KEY_OUTPUT, parser.nextHexInt(0));
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
}
}
@@ -550,12 +808,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
LinkedList<Position> positions = new LinkedList<>();
String vin = parser.next();
- int power = parser.nextInt(0);
+ Integer power = parser.nextInt();
+ Integer battery = parser.nextInt();
Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
while (itemParser.find()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_VIN, vin);
@@ -567,18 +825,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
Position position = positions.getLast();
- decodeLocation(position, parser);
+ skipLocation(parser);
- // power value only on some devices
- if (power > 10) {
- position.set(Position.KEY_POWER, power);
+ if (power != null && power > 10) {
+ position.set(Position.KEY_POWER, power * 0.001); // only on some devices
+ }
+ if (battery != null) {
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
}
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_HOURS, parser.next());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+ position.set(Position.KEY_HOURS, parseHours(parser.next()));
position.set(Position.PREFIX_ADC + 1, parser.next());
position.set(Position.PREFIX_ADC + 2, parser.next());
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
@@ -588,6 +847,13 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, parser.nextInt());
position.set(Position.KEY_FUEL_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) {
positions.clear();
@@ -608,14 +874,15 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ long mask = parser.nextHexLong();
+
LinkedList<Position> positions = new LinkedList<>();
- int power = parser.nextInt(0);
+ Integer power = parser.nextInt();
Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
while (itemParser.find()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
decodeLocation(position, itemParser);
@@ -625,11 +892,13 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
Position position = positions.getLast();
- decodeLocation(position, parser);
+ skipLocation(parser);
- position.set(Position.KEY_POWER, power);
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_HOURS, parser.next());
+ if (power != null) {
+ position.set(Position.KEY_POWER, power * 0.001);
+ }
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ position.set(Position.KEY_HOURS, parseHours(parser.next()));
position.set(Position.PREFIX_ADC + 1, parser.next());
position.set(Position.PREFIX_ADC + 2, parser.next());
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
@@ -638,14 +907,37 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
int index = 0;
String[] data = parser.next().split(",");
- if (data.length > 1) {
- int deviceType = Integer.parseInt(data[index++]);
- if (deviceType == 2) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index++; // id
- index++; // type
- position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index++], 16) * 0.0625);
+
+ index += 1; // device type
+
+ if (BitUtil.check(mask, 0)) {
+ index += 1; // digital fuel sensor data
+ }
+
+ if (BitUtil.check(mask, 1)) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // id
+ index += 1; // type
+ if (!data[index++].isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625);
+ }
+ }
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ index += 1; // can data
+ }
+
+ if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // type
+ if (BitUtil.check(mask, 3)) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++]));
+ }
+ if (BitUtil.check(mask, 4)) {
+ index += 1; // volume
}
}
}
@@ -668,8 +960,25 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
decodeLocation(position, parser);
- position.set(Position.KEY_HOURS, parser.next());
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_IGNITION, sentence.contains("IGN"));
+ position.set(Position.KEY_HOURS, parseHours(parser.next()));
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeLsw(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_LSW, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.PREFIX_IN + (sentence.contains("LSW") ? 1 : 2), parser.nextInt() == 1);
+
+ decodeLocation(position, parser);
decodeDeviceTime(position, parser);
@@ -687,7 +996,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
decodeLocation(position, parser);
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
decodeDeviceTime(position, parser);
@@ -705,7 +1014,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
Network network = new Network();
- parser.nextInt(0); // count
+ parser.nextInt(); // count
Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next());
while (matcher.find()) {
String mac = matcher.group(1).replaceAll("(..)", "$1:");
@@ -715,7 +1024,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(network);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
return position;
}
@@ -746,6 +1055,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Object decodePna(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_PNA, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_ALARM, sentence.contains("PNA") ? Position.ALARM_POWER_ON : Position.ALARM_POWER_OFF);
+
+ 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);
@@ -753,24 +1076,48 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- int reportType = parser.nextInt(0);
+ int reportType = parser.nextHexInt();
if (type.equals("NMR")) {
position.set(Position.KEY_MOTION, reportType == 1);
} else if (type.equals("SOS")) {
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ } else if (type.equals("DIS")) {
+ position.set(Position.PREFIX_IN + reportType / 0x10, reportType % 0x10 == 1);
+ } else if (type.equals("IGL")) {
+ position.set(Position.KEY_IGNITION, reportType % 0x10 == 1);
+ } else if (type.equals("HBM")) {
+ switch (reportType % 0x10) {
+ case 0:
+ case 3:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 1:
+ case 4:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 2:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ break;
+ }
}
decodeLocation(position, parser);
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
decodeDeviceTime(position, parser);
if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) {
- channel.write("+SACK:" + parser.next() + "$", remoteAddress);
+ channel.writeAndFlush(new NetworkMessage("+SACK:" + parser.next() + "$", remoteAddress));
}
return position;
@@ -783,17 +1130,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- int hdop = parser.nextInt(0);
- position.setValid(hdop > 0);
- position.set(Position.KEY_HDOP, hdop);
+ if (parser.hasNext()) {
+ int hdop = parser.nextInt();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+ }
position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
position.setCourse(parser.nextDouble(0));
position.setAltitude(parser.nextDouble(0));
if (parser.hasNext(2)) {
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
} else {
getLastLocation(position, null);
}
@@ -804,12 +1153,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext(4)) {
position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt())));
}
decodeDeviceTime(position, parser);
switch (type) {
+ case "TOW":
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ break;
+ case "IDL":
+ position.set(Position.KEY_ALARM, Position.ALARM_IDLE);
+ break;
case "PNA":
position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
break;
@@ -817,9 +1172,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
break;
case "EPN":
+ case "MPN":
position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
break;
case "EPF":
+ case "MPF":
position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
break;
case "BPL":
@@ -850,7 +1207,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = ((ChannelBuffer) msg).toString(StandardCharsets.US_ASCII);
+ String sentence = ((ByteBuf) msg).toString(StandardCharsets.US_ASCII);
int typeIndex = sentence.indexOf(":GT");
if (typeIndex < 0) {
@@ -869,7 +1226,15 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
case "OBD":
result = decodeObd(channel, remoteAddress, sentence);
break;
+ case "CAN":
+ result = decodeCan(channel, remoteAddress, sentence);
+ break;
+ case "CTN":
case "FRI":
+ case "GEO":
+ case "RTL":
+ case "DOG":
+ case "STR":
result = decodeFri(channel, remoteAddress, sentence);
break;
case "ERI":
@@ -879,6 +1244,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
case "IGF":
result = decodeIgn(channel, remoteAddress, sentence);
break;
+ case "LSW":
+ case "TSW":
+ result = decodeLsw(channel, remoteAddress, sentence);
+ break;
case "IDA":
result = decodeIda(channel, remoteAddress, sentence);
break;
@@ -891,6 +1260,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
case "VER":
result = decodeVer(channel, remoteAddress, sentence);
break;
+ case "PNA":
+ case "PFA":
+ result = decodePna(channel, remoteAddress, sentence);
+ break;
default:
result = decodeOther(channel, remoteAddress, sentence, type);
break;
diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
new file mode 100644
index 000000000..7243be72a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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 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;
+
+public class GlobalSatProtocol extends BaseProtocol {
+
+ public GlobalSatProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "!\r\n", "!"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new GlobalSatProtocolDecoder(GlobalSatProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GlobalSatProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java
index c45a937d4..b48df4047 100644
--- a/src/org/traccar/protocol/GlobalSatProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,14 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+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.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -33,7 +38,7 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
private String format0;
private String format1;
- public GlobalSatProtocolDecoder(GlobalSatProtocol protocol) {
+ public GlobalSatProtocolDecoder(Protocol protocol) {
super(protocol);
format0 = Context.getConfig().getString(getProtocolName() + ".format0", "TSPRXAB27GHKLMnaicz*U!");
@@ -48,14 +53,43 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
format1 = format;
}
+ private Double decodeVoltage(String value) {
+ if (value.endsWith("mV")) {
+ return Integer.parseInt(value.substring(0, value.length() - 2)) / 1000.0;
+ } else if (value.endsWith("V")) {
+ return Double.parseDouble(value.substring(0, value.length() - 1));
+ } else {
+ return null;
+ }
+ }
+
+ private String decodeAlarm(int value) {
+ if (BitUtil.check(value, 0)) {
+ return Position.ALARM_SOS;
+ }
+ if (BitUtil.check(value, 3) || BitUtil.check(value, 4)) {
+ return Position.ALARM_GEOFENCE;
+ }
+ if (BitUtil.check(value, 5)) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if (BitUtil.check(value, 6)) {
+ return Position.ALARM_POWER_CUT;
+ }
+ if (BitUtil.check(value, 7)) {
+ return Position.ALARM_LOW_POWER;
+ }
+ return null;
+ }
+
private Position decodeOriginal(Channel channel, SocketAddress remoteAddress, String sentence) {
if (channel != null) {
- channel.write("ACK\r");
+ channel.writeAndFlush(new NetworkMessage("ACK\r", remoteAddress));
}
String format;
- if (sentence.startsWith("GSr")) {
+ if (sentence.startsWith("GSr") || sentence.startsWith("GSb")) {
format = format0;
} else if (sentence.startsWith("GSh")) {
format = format1;
@@ -76,12 +110,13 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
}
String[] values = sentence.split(",");
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
+
+ CellTower cellTower = new CellTower();
for (int formatIndex = 0, valueIndex = 1; formatIndex < format.length()
&& valueIndex < values.length; formatIndex++) {
- String value = values[valueIndex];
+ String value = values[valueIndex].replace("\"", "");
switch (format.charAt(formatIndex)) {
case 'S':
@@ -164,14 +199,49 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
case 'K':
position.setCourse(Double.parseDouble(value));
break;
- case 'N':
- if (value.endsWith("mV")) {
- position.set(Position.KEY_BATTERY,
- Integer.parseInt(value.substring(0, value.length() - 2)) / 1000.0);
- } else {
- position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(value));
+ case 'L':
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
+ break;
+ case 'P':
+ if (value.length() == 4) {
+ position.set(Position.KEY_ALARM, decodeAlarm(Integer.parseInt(value, 16)));
+ }
+ break;
+ case 'Z':
+ if (!value.isEmpty()) {
+ position.set("geofence", value);
}
break;
+ case 'Y':
+ int io = Integer.parseInt(value, 16);
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(io, 1));
+ position.set(Position.KEY_MOTION, BitUtil.check(io, 7));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 9));
+ position.set(Position.KEY_IGNITION, BitUtil.check(io, 13));
+ position.set(Position.KEY_CHARGE, BitUtil.check(io, 15));
+ break;
+ case 'c':
+ cellTower.setSignalStrength(Integer.parseInt(value));
+ break;
+ case 'm':
+ position.set(Position.KEY_POWER, decodeVoltage(value));
+ break;
+ case 'n':
+ case 'N':
+ position.set(Position.KEY_BATTERY, decodeVoltage(value));
+ break;
+ case 't':
+ cellTower.setMobileCountryCode(Integer.parseInt(value));
+ break;
+ case 'u':
+ cellTower.setMobileNetworkCode(Integer.parseInt(value));
+ break;
+ case 'v':
+ cellTower.setLocationAreaCode(Integer.parseInt(value, 16));
+ break;
+ case 'w':
+ cellTower.setCellId(Long.parseLong(value, 16));
+ break;
default:
// Unsupported
break;
@@ -179,6 +249,11 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
valueIndex += 1;
}
+
+ if (cellTower.getCellId() != null) {
+ position.setNetwork(new Network(cellTower));
+ }
+
return position;
}
@@ -207,8 +282,7 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
new file mode 100644
index 000000000..84cd5565b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 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;
+
+public class GlobalstarProtocol extends BaseProtocol {
+
+ public GlobalstarProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new GlobalstarProtocolDecoder(GlobalstarProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
new file mode 100644
index 000000000..26af8d5af
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 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 com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.Position;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ private DocumentBuilder documentBuilder;
+ private XPath xPath;
+ private XPathExpression messageExpression;
+
+ public GlobalstarProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ try {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ builderFactory.setXIncludeAware(false);
+ builderFactory.setExpandEntityReferences(false);
+ documentBuilder = builderFactory.newDocumentBuilder();
+ xPath = XPathFactory.newInstance().newXPath();
+ messageExpression = xPath.compile("//stuMessages/stuMessage");
+ } catch (ParserConfigurationException | XPathExpressionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void sendResponse(Channel channel, String messageId) throws TransformerException {
+
+ Document document = documentBuilder.newDocument();
+ Element rootElement = document.createElement("stuResponseMsg");
+ rootElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ rootElement.setAttribute(
+ "xsi:noNamespaceSchemaLocation", "http://cody.glpconnect.com/XSD/StuResponse_Rev1_0.xsd");
+ rootElement.setAttribute("deliveryTimeStamp", new SimpleDateFormat("dd/MM/yyyy hh:mm:ss z").format(new Date()));
+ rootElement.setAttribute("messageID", "00000000000000000000000000000000");
+ rootElement.setAttribute("correlationID", messageId);
+ document.appendChild(rootElement);
+
+ Element state = document.createElement("state");
+ state.appendChild(document.createTextNode("pass"));
+ rootElement.appendChild(state);
+
+ Element stateMessage = document.createElement("stateMessage");
+ stateMessage.appendChild(document.createTextNode("Messages received and stored successfully"));
+ rootElement.appendChild(stateMessage);
+
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ ByteBuf content = Unpooled.buffer();
+ transformer.transform(new DOMSource(document), new StreamResult(new ByteBufOutputStream(content)));
+
+ FullHttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+
+ Document document = documentBuilder.parse(new ByteBufferBackedInputStream(request.content().nioBuffer()));
+ NodeList nodes = (NodeList) messageExpression.evaluate(document, XPathConstants.NODESET);
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, xPath.evaluate("esn", node));
+ if (deviceSession != null) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(new Date(Long.parseLong(xPath.evaluate("unixTime", node)) * 1000));
+
+ ByteBuf buf = Unpooled.wrappedBuffer(
+ DataConverter.parseHex(xPath.evaluate("payload", node).substring(2)));
+
+ buf.readUnsignedByte(); // flags
+
+ position.setLatitude(buf.readUnsignedMedium() * 90.0 / (1 << 23));
+ if (position.getLatitude() > 90) {
+ position.setLatitude(position.getLatitude() - 180);
+ }
+
+ position.setLongitude(buf.readUnsignedMedium() * 180.0 / (1 << 23));
+ if (position.getLongitude() > 180) {
+ position.setLongitude(position.getLongitude() - 360);
+ }
+
+ buf.readUnsignedByte(); // status
+ buf.readUnsignedByte(); // status
+
+ positions.add(position);
+
+ }
+ }
+
+ sendResponse(channel, document.getFirstChild().getAttributes().getNamedItem("messageID").getNodeValue());
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GnxProtocol.java b/src/main/java/org/traccar/protocol/GnxProtocol.java
new file mode 100644
index 000000000..3576bf805
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GnxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GnxProtocol extends BaseProtocol {
+
+ public GnxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\n\r"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new GnxProtocolDecoder(GnxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GnxProtocolDecoder.java b/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java
index 2274ec164..c9c221a69 100644
--- a/src/org/traccar/protocol/GnxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GnxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class GnxProtocolDecoder extends BaseProtocolDecoder {
- public GnxProtocolDecoder(GnxProtocol protocol) {
+ public GnxProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -80,8 +81,7 @@ public class GnxProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocol.java b/src/main/java/org/traccar/protocol/GoSafeProtocol.java
new file mode 100644
index 000000000..853b78a16
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GoSafeProtocol extends BaseProtocol {
+
+ public GoSafeProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ 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
new file mode 100644
index 000000000..76278070e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
@@ -0,0 +1,261 @@
+/*
+ * 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+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.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
+
+ public GoSafeProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("*GS") // header
+ .number("d+,") // protocol version
+ .number("(d+),") // imei
+ .expression("([^#]*)#?") // data
+ .compile();
+
+ private static final Pattern PATTERN_OLD = new PatternBuilder()
+ .text("*GS") // header
+ .number("d+,") // protocol version
+ .number("(d+),") // imei
+ .text("GPS:")
+ .number("(dd)(dd)(dd);") // time (hhmmss)
+ .number("d;").optional() // fix type
+ .expression("([AV]);") // validity
+ .number("([NS])(d+.d+);") // latitude
+ .number("([EW])(d+.d+);") // longitude
+ .number("(d+)?;") // speed
+ .number("(d+);") // course
+ .number("(d+.?d*)").optional() // hdop
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .any()
+ .compile();
+
+ private void decodeFragment(Position position, String fragment) {
+
+ int dataIndex = fragment.indexOf(':');
+ int index = 0;
+ String[] values;
+ if (fragment.length() == dataIndex + 1) {
+ values = new String[0];
+ } else {
+ values = fragment.substring(dataIndex + 1).split(";");
+ }
+
+ switch (fragment.substring(0, dataIndex)) {
+ case "GPS":
+ position.setValid(values[index++].equals("A"));
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index].substring(1)));
+ if (values[index++].charAt(0) == 'S') {
+ position.setLatitude(-position.getLatitude());
+ }
+ position.setLongitude(Double.parseDouble(values[index].substring(1)));
+ if (values[index++].charAt(0) == 'W') {
+ position.setLongitude(-position.getLongitude());
+ }
+ if (!values[index++].isEmpty()) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1])));
+ }
+ position.setCourse(Integer.parseInt(values[index++]));
+ if (index < values.length) {
+ position.setAltitude(Integer.parseInt(values[index++]));
+ }
+ if (index < values.length) {
+ position.set(Position.KEY_HDOP, Double.parseDouble(values[index++]));
+ }
+ if (index < values.length) {
+ position.set(Position.KEY_VDOP, Double.parseDouble(values[index++]));
+ }
+ break;
+ case "GSM":
+ index += 1; // registration status
+ index += 1; // signal strength
+ position.setNetwork(new Network(CellTower.from(
+ Integer.parseInt(values[index++]), Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++], 16), Integer.parseInt(values[index++], 16),
+ Integer.parseInt(values[index++]))));
+ break;
+ case "COT":
+ if (index < values.length) {
+ position.set(Position.KEY_ODOMETER, Long.parseLong(values[index++]));
+ }
+ if (index < values.length) {
+ String[] hours = values[index].split("-");
+ position.set(Position.KEY_HOURS, (Integer.parseInt(hours[0]) * 3600
+ + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60 : 0)
+ + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000);
+ }
+ break;
+ case "ADC":
+ position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
+ if (index < values.length) {
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++]));
+ }
+ if (index < values.length) {
+ position.set(Position.PREFIX_ADC + 1, Double.parseDouble(values[index++]));
+ }
+ if (index < values.length) {
+ position.set(Position.PREFIX_ADC + 2, Double.parseDouble(values[index++]));
+ }
+ break;
+ case "DTT":
+ position.set(Position.KEY_STATUS, Integer.parseInt(values[index++], 16));
+ if (!values[index++].isEmpty()) {
+ int io = Integer.parseInt(values[index - 1], 16);
+ position.set(Position.KEY_IGNITION, BitUtil.check(io, 0));
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(io, 1));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(io, 2));
+ position.set(Position.PREFIX_IN + 3, BitUtil.check(io, 3));
+ position.set(Position.PREFIX_IN + 4, BitUtil.check(io, 4));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 5));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 6));
+ position.set(Position.PREFIX_OUT + 3, BitUtil.check(io, 7));
+ }
+ position.set(Position.KEY_GEOFENCE, values[index++] + values[index++]);
+ position.set("eventStatus", values[index++]);
+ if (index < values.length) {
+ position.set("packetType", values[index++]);
+ }
+ break;
+ case "ETD":
+ position.set("eventData", values[index++]);
+ break;
+ case "OBD":
+ position.set("obd", values[index++]);
+ break;
+ case "TAG":
+ position.set("tagData", values[index++]);
+ break;
+ case "IWD":
+ while (index < values.length) {
+ int sensorIndex = Integer.parseInt(values[index++]);
+ int dataType = Integer.parseInt(values[index++]);
+ if (dataType == 0) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, values[index++]);
+ } else if (dataType == 1) {
+ index += 1; // temperature sensor serial number
+ position.set(Position.PREFIX_TEMP + sensorIndex, Double.parseDouble(values[index++]));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private Position decodePosition(DeviceSession deviceSession, String sentence) throws ParseException {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int index = 0;
+ String[] fragments = sentence.split(",");
+
+ position.setTime(new SimpleDateFormat("HHmmssddMMyy").parse(fragments[index++]));
+
+ for (; index < fragments.length; index += 1) {
+ if (!fragments[index].isEmpty()) {
+ if (fragments[index].matches("\\p{XDigit}+")) {
+ position.set(Position.KEY_EVENT, Integer.parseInt(fragments[index], 16));
+ } else {
+ decodeFragment(position, fragments[index]);
+ }
+ }
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("1234", remoteAddress));
+ }
+
+ String sentence = (String) msg;
+ Pattern pattern = PATTERN;
+ if (sentence.startsWith("*GS02")) {
+ pattern = PATTERN_OLD;
+ }
+
+ Parser parser = new Parser(pattern, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (pattern == PATTERN_OLD) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+
+ position.set(Position.KEY_HDOP, parser.next());
+
+ dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+
+ } else {
+
+ List<Position> positions = new LinkedList<>();
+ for (String item : parser.next().split("\\$")) {
+ positions.add(decodePosition(deviceSession, item));
+ }
+ return positions;
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GotopProtocol.java b/src/main/java/org/traccar/protocol/GotopProtocol.java
new file mode 100644
index 000000000..07fe02248
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GotopProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GotopProtocol extends BaseProtocol {
+
+ public GotopProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new GotopProtocolDecoder(GotopProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GotopProtocolDecoder.java b/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java
index f938887e2..a867451aa 100644
--- a/src/org/traccar/protocol/GotopProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GotopProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class GotopProtocolDecoder extends BaseProtocolDecoder {
- public GotopProtocolDecoder(GotopProtocol protocol) {
+ public GotopProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -39,7 +40,7 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder {
.number("DATE:(dd)(dd)(dd),") // date (yyddmm)
.number("TIME:(dd)(dd)(dd),") // time (hhmmss)
.number("LAT:(d+.d+)([NS]),") // latitude
- .number("LOT:(d+.d+)([EW]),") // longitude
+ .number("LO[NT]:(d+.d+)([EW]),") // longitude
.text("Speed:").number("(d+.d+),") // speed
.expression("([^,]+),") // status
.number("(d+)?") // course
@@ -55,8 +56,7 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/Gps056FrameDecoder.java b/src/main/java/org/traccar/protocol/Gps056FrameDecoder.java
index 4ce83dc0a..0d84bf231 100644
--- a/src/org/traccar/protocol/Gps056FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps056FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,26 +15,26 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+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 Gps056FrameDecoder extends FrameDecoder {
+public class Gps056FrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_HEADER = 4;
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() >= MESSAGE_HEADER) {
int length = Integer.parseInt(buf.toString(2, 2, StandardCharsets.US_ASCII)) + 5;
if (buf.readableBytes() >= length) {
- ChannelBuffer frame = buf.readBytes(length);
- while (buf.readable() && buf.getUnsignedByte(buf.readerIndex()) != '$') {
+ ByteBuf frame = buf.readRetainedSlice(length);
+ while (buf.isReadable() && buf.getUnsignedByte(buf.readerIndex()) != '$') {
buf.readByte();
}
return frame;
diff --git a/src/org/traccar/protocol/Gps056Protocol.java b/src/main/java/org/traccar/protocol/Gps056Protocol.java
index 33c190ad2..b6ab10a19 100644
--- a/src/org/traccar/protocol/Gps056Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gps056Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class Gps056Protocol extends BaseProtocol {
public Gps056Protocol() {
- super("gps056");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Gps056FrameDecoder());
- pipeline.addLast("objectDecoder", new Gps056ProtocolDecoder(Gps056Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Gps056FrameDecoder());
+ pipeline.addLast(new Gps056ProtocolDecoder(Gps056Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Gps056ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java
index 7248365b0..0ba79bb51 100644
--- a/src/org/traccar/protocol/Gps056ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps056ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,24 +31,24 @@ import java.nio.charset.StandardCharsets;
public class Gps056ProtocolDecoder extends BaseProtocolDecoder {
- public Gps056ProtocolDecoder(Gps056Protocol protocol) {
+ public Gps056ProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private static void sendResponse(Channel channel, String type, String imei, ChannelBuffer content) {
+ private static void sendResponse(Channel channel, String type, String imei, ByteBuf content) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
String header = "*" + type + imei;
response.writeBytes(header.getBytes(StandardCharsets.US_ASCII));
if (content != null) {
response.writeBytes(content);
}
response.writeByte('#');
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
- private static double decodeCoordinate(ChannelBuffer buf) {
+ private static double decodeCoordinate(ByteBuf buf) {
double degrees = buf.getUnsignedShort(buf.readerIndex()) / 100;
double minutes = buf.readUnsignedShort() % 100 + buf.readUnsignedShort() * 0.0001;
degrees += minutes / 60;
@@ -57,12 +59,12 @@ public class Gps056ProtocolDecoder extends BaseProtocolDecoder {
return degrees;
}
- private static void decodeStatus(ChannelBuffer buf, Position position) {
+ private static void decodeStatus(ByteBuf buf, Position position) {
position.set(Position.KEY_INPUT, buf.readUnsignedByte());
position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
- position.set(Position.PREFIX_ADC + 1, ChannelBuffers.swapShort(buf.readShort()) * 5.06); // mV
+ position.set(Position.PREFIX_ADC + 1, buf.readShortLE() * 5.06); // mV
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
@@ -73,13 +75,13 @@ public class Gps056ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
buf.skipBytes(2); // length
- String type = buf.readBytes(7).toString(StandardCharsets.US_ASCII);
- String imei = buf.readBytes(15).toString(StandardCharsets.US_ASCII);
+ String type = buf.readSlice(7).toString(StandardCharsets.US_ASCII);
+ String imei = buf.readSlice(15).toString(StandardCharsets.US_ASCII);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
@@ -88,13 +90,16 @@ public class Gps056ProtocolDecoder extends BaseProtocolDecoder {
if (type.startsWith("LOGN")) {
- sendResponse(channel, "LGSA" + type.substring(4), imei,
- ChannelBuffers.copiedBuffer("1", StandardCharsets.US_ASCII));
+ ByteBuf content = Unpooled.copiedBuffer("1", StandardCharsets.US_ASCII);
+ try {
+ sendResponse(channel, "LGSA" + type.substring(4), imei, content);
+ } finally {
+ content.release();
+ }
} else if (type.startsWith("GPSL")) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -110,14 +115,13 @@ public class Gps056ProtocolDecoder extends BaseProtocolDecoder {
decodeStatus(buf, position);
- sendResponse(channel, "GPSA" + type.substring(4), imei, buf.readBytes(2));
+ sendResponse(channel, "GPSA" + type.substring(4), imei, buf.readSlice(2));
return position;
} else if (type.startsWith("SYNC")) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
diff --git a/src/main/java/org/traccar/protocol/Gps103Protocol.java b/src/main/java/org/traccar/protocol/Gps103Protocol.java
new file mode 100644
index 000000000..5356387ce
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gps103Protocol.java
@@ -0,0 +1,60 @@
+/*
+ * 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 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.model.Command;
+
+public class Gps103Protocol extends BaseProtocol {
+
+ public Gps103Protocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_POSITION_PERIODIC,
+ Command.TYPE_POSITION_STOP,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_ALARM_ARM,
+ Command.TYPE_ALARM_DISARM,
+ Command.TYPE_REQUEST_PHOTO);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(2048, false, "\r\n", "\n", ";", "*"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Gps103ProtocolEncoder(Gps103Protocol.this));
+ pipeline.addLast(new Gps103ProtocolDecoder(Gps103Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Gps103ProtocolEncoder(Gps103Protocol.this));
+ pipeline.addLast(new Gps103ProtocolDecoder(Gps103Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
new file mode 100644
index 000000000..e00d83061
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DataConverter;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
+
+ private int photoPackets = 0;
+ private ByteBuf photo;
+
+ public Gps103ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("imei:")
+ .number("(d+),") // imei
+ .expression("([^,]+),") // alarm
+ .groupBegin()
+ .number("(dd)/?(dd)/?(dd) ?") // local date (yymmdd)
+ .number("(dd):?(dd)(?:dd)?,") // local time (hhmmss)
+ .or()
+ .number("d*,")
+ .groupEnd()
+ .expression("([^,]+)?,") // rfid
+ .groupBegin()
+ .text("L,,,")
+ .number("(x+),,") // lac
+ .number("(x+),,,") // cid
+ .or()
+ .text("F,")
+ .groupBegin()
+ .number("(dd)(dd)(dd).d+") // time utc (hhmmss)
+ .or()
+ .number("(?:d{1,5}.d+)?")
+ .groupEnd()
+ .text(",")
+ .expression("([AV]),") // validity
+ .expression("([NS]),").optional()
+ .number("(d+)(dd.d+),") // latitude (ddmm.mmmm)
+ .expression("([NS]),").optional()
+ .expression("([EW]),").optional()
+ .number("(d+)(dd.d+),") // longitude (dddmm.mmmm)
+ .expression("([EW])?,").optional()
+ .number("(d+.?d*)?").optional() // speed
+ .number(",(d+.?d*)?").optional() // course
+ .number(",(-?d+.?d*)?").optional() // altitude
+ .number(",([01])?").optional() // ignition
+ .number(",([01])?").optional() // door
+ .groupBegin()
+ .number(",(?:(d+.d+)%)?") // fuel 1
+ .number(",(?:(d+.d+)%|d+)?") // fuel 2
+ .groupEnd("?")
+ .number(",([-+]?d+)?").optional() // temperature
+ .groupEnd()
+ .any()
+ .compile();
+
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("imei:")
+ .number("(d+),") // imei
+ .expression("OBD,") // type
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+)?,") // odometer
+ .number("(d+.d+)?,") // fuel instant
+ .number("(d+.d+)?,") // fuel average
+ .number("(d+)?,") // hours
+ .number("(d+),") // speed
+ .number("(d+.?d*%),") // power load
+ .number("(?:([-+]?d+)|[-+]?),") // temperature
+ .number("(d+.?d*%),") // throttle
+ .number("(d+),") // rpm
+ .number("(d+.d+),") // battery
+ .number("([^;]*)") // dtcs
+ .any()
+ .compile();
+
+ private static final Pattern PATTERN_ALT = new PatternBuilder()
+ .text("imei:")
+ .number("(d+),") // imei
+ .expression("[^,]+,")
+ .expression("(?:-+|(.+)),") // event
+ .expression("(?:-+|(.+)),") // sensor id
+ .expression("(?:-+|(.+)),") // sensor voltage
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(d+),") // rssi
+ .number("(d),") // gps status
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(-?d+),") // altitude
+ .number("(d+.d+),") // hdop
+ .number("(d+),") // satellites
+ .number("([01]),") // ignition
+ .number("([01]),") // charge
+ .expression("(?:-+|(.+))") // error
+ .any()
+ .compile();
+
+ private String decodeAlarm(String value) {
+ if (value.startsWith("T:")) {
+ return Position.ALARM_TEMPERATURE;
+ } else if (value.startsWith("oil")) {
+ return Position.ALARM_FUEL_LEAK;
+ }
+ switch (value) {
+ case "tracker":
+ return null;
+ case "help me":
+ return Position.ALARM_SOS;
+ case "low battery":
+ return Position.ALARM_LOW_BATTERY;
+ case "stockade":
+ return Position.ALARM_GEOFENCE;
+ case "move":
+ return Position.ALARM_MOVEMENT;
+ case "speed":
+ return Position.ALARM_OVERSPEED;
+ case "acc on":
+ return Position.ALARM_POWER_ON;
+ case "acc off":
+ return Position.ALARM_POWER_OFF;
+ case "door alarm":
+ return Position.ALARM_DOOR;
+ case "ac alarm":
+ return Position.ALARM_POWER_CUT;
+ case "accident alarm":
+ return Position.ALARM_ACCIDENT;
+ case "sensor alarm":
+ return Position.ALARM_SHOCK;
+ case "bonnet alarm":
+ return Position.ALARM_BONNET;
+ case "footbrake alarm":
+ return Position.ALARM_FOOT_BRAKE;
+ case "DTC":
+ return Position.ALARM_FAULT;
+ default:
+ return null;
+ }
+ }
+
+ private Position decodeRegular(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String imei = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ String alarm = parser.next();
+ position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+ if (alarm.equals("help me")) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("**,imei:" + imei + ",E;", remoteAddress));
+ }
+ } else if (alarm.startsWith("vt")) {
+ photoPackets = Integer.parseInt(alarm.substring(2));
+ photo = Unpooled.buffer();
+ } else if (alarm.equals("acc on")) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (alarm.equals("acc off")) {
+ position.set(Position.KEY_IGNITION, false);
+ } else if (alarm.startsWith("T:")) {
+ 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")) {
+ position.set(Position.KEY_EVENT, alarm);
+ }
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ int localHours = parser.nextInt(0);
+ int localMinutes = parser.nextInt(0);
+
+ String rfid = parser.next();
+ if (alarm.equals("rfid")) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
+ }
+
+ if (parser.hasNext(2)) {
+
+ getLastLocation(position, null);
+
+ position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0))));
+
+ } else {
+
+ String utcHours = parser.next();
+ String utcMinutes = parser.next();
+
+ dateBuilder.setTime(localHours, localMinutes, parser.nextInt(0));
+
+ // Timezone calculation
+ if (utcHours != null && utcMinutes != null) {
+ int deltaMinutes = (localHours - Integer.parseInt(utcHours)) * 60;
+ deltaMinutes += localMinutes - Integer.parseInt(utcMinutes);
+ if (deltaMinutes <= -12 * 60) {
+ deltaMinutes += 24 * 60;
+ } else if (deltaMinutes > 12 * 60) {
+ deltaMinutes -= 24 * 60;
+ }
+ dateBuilder.addMinute(-deltaMinutes);
+ }
+ position.setTime(dateBuilder.getDate());
+
+ position.setValid(parser.next().equals("A"));
+ position.setFixTime(position.getDeviceTime());
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM));
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_DOOR, parser.nextInt() == 1);
+ }
+ position.set("fuel1", parser.nextDouble());
+ position.set("fuel2", parser.nextDouble());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+
+ }
+
+ return position;
+ }
+
+ private Position decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_OBD, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, parser.nextDateTime());
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ parser.nextDouble(0); // instant fuel consumption
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble(0));
+ if (parser.hasNext()) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(parser.nextInt()));
+ }
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt(0));
+ position.set(Position.KEY_ENGINE_LOAD, parser.next());
+ position.set(Position.KEY_COOLANT_TEMP, parser.nextInt());
+ position.set(Position.KEY_THROTTLE, parser.next());
+ position.set(Position.KEY_RPM, parser.nextInt(0));
+ position.set(Position.KEY_BATTERY, parser.nextDouble(0));
+ position.set(Position.KEY_DTCS, parser.next().replace(',', ' ').trim());
+
+ return position;
+ }
+
+
+ private Position decodeAlternative(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_ALT, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_EVENT, parser.next());
+ position.set("sensorId", parser.next());
+ position.set("sensorVoltage", parser.nextDouble());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ position.setValid(parser.nextInt() > 0);
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+ position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
+ position.set("error", parser.next());
+
+ return position;
+ }
+
+ private Position decodePhoto(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ String imei = sentence.substring(5, 5 + 15);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(
+ sentence.substring(24, sentence.endsWith(";") ? sentence.length() - 1 : sentence.length())));
+ int index = buf.readUnsignedShortLE();
+ photo.writeBytes(buf, buf.readerIndex() + 2, buf.readableBytes() - 4);
+
+ if (index + 1 >= photoPackets) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ try {
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ } finally {
+ photoPackets = 0;
+ photo.release();
+ photo = null;
+ }
+
+ return position;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (sentence.contains("imei:") && sentence.length() <= 30) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("LOAD", remoteAddress));
+ Matcher matcher = Pattern.compile("imei:(\\d+),").matcher(sentence);
+ if (matcher.find()) {
+ getDeviceSession(channel, remoteAddress, matcher.group(1));
+ }
+ }
+ return null;
+ }
+
+ if (!sentence.isEmpty() && Character.isDigit(sentence.charAt(0))) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("ON", remoteAddress));
+ }
+ int start = sentence.indexOf("imei:");
+ if (start >= 0) {
+ sentence = sentence.substring(start);
+ } else {
+ return null;
+ }
+ }
+
+ if (sentence.substring(21, 21 + 2).equals("vr")) {
+ return decodePhoto(channel, remoteAddress, sentence);
+ } else if (sentence.substring(21, 21 + 3).contains("OBD")) {
+ return decodeObd(channel, remoteAddress, sentence);
+ } else if (sentence.endsWith("*")) {
+ return decodeAlternative(channel, remoteAddress, sentence);
+ } else {
+ return decodeRegular(channel, remoteAddress, sentence);
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gps103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
index 36801b401..e662e9b04 100644
--- a/src/org/traccar/protocol/Gps103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -16,11 +16,15 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class Gps103ProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
+ public Gps103ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
public String formatValue(String key, Object value) {
@@ -43,30 +47,27 @@ public class Gps103ProtocolEncoder extends StringProtocolEncoder implements Stri
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "**,imei:{%s},{%s}", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ 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,A", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "**,imei:{%s},B", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,B", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_PERIODIC:
return formatCommand(
- command, "**,imei:{%s},C,{%s}", this, Command.KEY_UNIQUE_ID, Command.KEY_FREQUENCY);
+ command, "**,imei:%s,C,%s", this, Command.KEY_UNIQUE_ID, Command.KEY_FREQUENCY);
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "**,imei:{%s},J", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,J", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "**,imei:{%s},K", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,K", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_ARM:
- return formatCommand(command, "**,imei:{%s},L", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,L", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, "**,imei:{%s},M", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,M", Command.KEY_UNIQUE_ID);
case Command.TYPE_REQUEST_PHOTO:
- return formatCommand(command, "**,imei:{%s},160", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,160", Command.KEY_UNIQUE_ID);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocol.java b/src/main/java/org/traccar/protocol/GpsGateProtocol.java
new file mode 100644
index 000000000..a131b6f48
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GpsGateProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GpsGateProtocol extends BaseProtocol {
+
+ public GpsGateProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\0", "\n", "\r\n"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new GpsGateProtocolDecoder(GpsGateProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
index ca1d6453e..cc187225b 100644
--- a/src/org/traccar/protocol/GpsGateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -29,7 +31,7 @@ import java.util.regex.Pattern;
public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
- public GpsGateProtocolDecoder(GpsGateProtocol protocol) {
+ public GpsGateProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,9 +67,9 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
- private void send(Channel channel, String message) {
+ private void send(Channel channel, SocketAddress remoteAddress, String message) {
if (channel != null) {
- channel.write(message + Checksum.nmea(message) + "\r\n");
+ channel.writeAndFlush(new NetworkMessage(message + Checksum.nmea(message) + "\r\n", remoteAddress));
}
}
@@ -79,7 +81,6 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
if (sentence.startsWith("$FRLIN,")) {
- // Login
int beginIndex = sentence.indexOf(',', 7);
if (beginIndex != -1) {
beginIndex += 1;
@@ -89,22 +90,21 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession != null) {
if (channel != null) {
- send(channel, "$FRSES," + channel.getId());
+ send(channel, remoteAddress, "$FRSES," + channel.id().asShortText());
}
} else {
- send(channel, "$FRERR,AuthError,Unknown device");
+ send(channel, remoteAddress, "$FRERR,AuthError,Unknown device");
}
} else {
- send(channel, "$FRERR,AuthError,Parse error");
+ send(channel, remoteAddress, "$FRERR,AuthError,Parse error");
}
} else {
- send(channel, "$FRERR,AuthError,Parse error");
+ send(channel, remoteAddress, "$FRERR,AuthError,Parse error");
}
} else if (sentence.startsWith("$FRVER,")) {
- // Version check
- send(channel, "$FRVER,1,0,GpsGate Server 1.0");
+ send(channel, remoteAddress, "$FRVER,1,0,GpsGate Server 1.0");
} else if (sentence.startsWith("$GPRMC,")) {
@@ -118,8 +118,7 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -143,8 +142,7 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
new file mode 100644
index 000000000..ad23ece48
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GpsMarkerProtocol extends BaseProtocol {
+
+ public GpsMarkerProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new GpsMarkerProtocolDecoder(GpsMarkerProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GpsMarkerProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java
index f65f5b2bb..bbb2c31e2 100644
--- a/src/org/traccar/protocol/GpsMarkerProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class GpsMarkerProtocolDecoder extends BaseProtocolDecoder {
- public GpsMarkerProtocolDecoder(GpsMarkerProtocol protocol) {
+ public GpsMarkerProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -61,8 +62,7 @@ public class GpsMarkerProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
new file mode 100644
index 000000000..ce6cc5929
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class GpsmtaProtocol extends BaseProtocol {
+
+ public GpsmtaProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new GpsmtaProtocolDecoder(GpsmtaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GpsmtaProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java
index 11777bece..31f9401b4 100644
--- a/src/org/traccar/protocol/GpsmtaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsmtaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -28,15 +30,15 @@ import java.util.regex.Pattern;
public class GpsmtaProtocolDecoder extends BaseProtocolDecoder {
- public GpsmtaProtocolDecoder(GpsmtaProtocol protocol) {
+ public GpsmtaProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN = new PatternBuilder()
.expression("([^ ]+) ") // uid
.number("(d+) ") // time (unix time)
- .number("(d+.d+) ") // latitude
- .number("(d+.d+) ") // longitude
+ .number("(-?d+.d+) ") // latitude
+ .number("(-?d+.d+) ") // longitude
.number("(d+) ") // speed
.number("(d+) ") // course
.number("(d+) ") // accuracy
@@ -44,7 +46,7 @@ public class GpsmtaProtocolDecoder extends BaseProtocolDecoder {
.number("(d+) ") // flags
.number("(d+) ") // battery
.number("(d+) ") // temperature
- .number("(d)") // changing status
+ .number("(d)") // charging status
.any()
.compile();
@@ -57,8 +59,7 @@ public class GpsmtaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -69,20 +70,20 @@ public class GpsmtaProtocolDecoder extends BaseProtocolDecoder {
String time = parser.next();
position.setTime(new Date(Long.parseLong(time) * 1000));
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
- position.setSpeed(parser.nextInt(0));
- position.setCourse(parser.nextInt(0));
- position.setAccuracy(parser.nextInt(0));
- position.setAltitude(parser.nextInt(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(parser.nextInt());
+ position.setCourse(parser.nextInt());
+ position.setAccuracy(parser.nextInt());
+ position.setAltitude(parser.nextInt());
- position.set(Position.KEY_STATUS, parser.nextInt(0));
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
- position.set(Position.PREFIX_TEMP + 1, parser.nextInt(0));
- position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
+ position.set(Position.KEY_STATUS, parser.nextInt());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.KEY_CHARGE, parser.nextInt() == 1);
if (channel != null) {
- channel.write(time, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(time, remoteAddress));
}
return position;
diff --git a/src/org/traccar/protocol/GranitFrameDecoder.java b/src/main/java/org/traccar/protocol/GranitFrameDecoder.java
index 7e8f4a3f5..bb7f4be44 100644
--- a/src/org/traccar/protocol/GranitFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/GranitFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,29 +15,29 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.StringFinder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
-public class GranitFrameDecoder extends FrameDecoder {
+public class GranitFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- int indexEnd = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("\r\n"));
+ int indexEnd = BufferUtil.indexOf("\r\n", buf);
if (indexEnd != -1) {
- int indexTilde = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("~"));
+ int indexTilde = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '~');
if (indexTilde != -1 && indexTilde < indexEnd) {
- int length = buf.getUnsignedShort(indexTilde + 1);
- indexEnd = buf.indexOf(indexTilde + 2 + length, buf.writerIndex(), new StringFinder("\r\n"));
+ int length = buf.getUnsignedShortLE(indexTilde + 1);
+ indexEnd = BufferUtil.indexOf("\r\n", buf, indexTilde + 2 + length, buf.writerIndex());
if (indexEnd == -1) {
return null;
}
}
- ChannelBuffer frame = buf.readBytes(indexEnd - buf.readerIndex());
+ ByteBuf frame = buf.readRetainedSlice(indexEnd - buf.readerIndex());
buf.skipBytes(2);
return frame;
}
diff --git a/src/org/traccar/protocol/GranitProtocol.java b/src/main/java/org/traccar/protocol/GranitProtocol.java
index 32e8e00b0..244c3977b 100644
--- a/src/org/traccar/protocol/GranitProtocol.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocol.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2016 - 2019 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.
@@ -16,41 +16,30 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.nio.ByteOrder;
-import java.util.List;
-
public class GranitProtocol extends BaseProtocol {
public GranitProtocol() {
- super("granit");
setSupportedDataCommands(
Command.TYPE_IDENTIFICATION,
Command.TYPE_REBOOT_DEVICE,
Command.TYPE_POSITION_SINGLE);
- setTextCommandEncoder(new GranitProtocolSmsEncoder());
+ setTextCommandEncoder(new GranitProtocolSmsEncoder(this));
setSupportedTextCommands(
Command.TYPE_REBOOT_DEVICE,
Command.TYPE_POSITION_PERIODIC);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new GranitFrameDecoder());
- pipeline.addLast("objectEncoder", new GranitProtocolEncoder());
- pipeline.addLast("objectDecoder", new GranitProtocolDecoder(GranitProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new GranitFrameDecoder());
+ pipeline.addLast(new GranitProtocolEncoder(GranitProtocol.this));
+ pipeline.addLast(new GranitProtocolDecoder(GranitProtocol.this));
}
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
+ });
}
}
diff --git a/src/org/traccar/protocol/GranitProtocolDecoder.java b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
index 8e935ae9e..8900e5b39 100644
--- a/src/org/traccar/protocol/GranitProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,19 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
-import org.traccar.helper.StringFinder;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
@@ -42,7 +42,7 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
private double adc3Ratio;
private double adc4Ratio;
- public GranitProtocolDecoder(GranitProtocol protocol) {
+ public GranitProtocolDecoder(Protocol protocol) {
super(protocol);
adc1Ratio = Context.getConfig().getDouble("granit.adc1Ratio", 1);
adc2Ratio = Context.getConfig().getDouble("granit.adc2Ratio", 1);
@@ -50,35 +50,35 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
adc4Ratio = Context.getConfig().getDouble("granit.adc4Ratio", 1);
}
- public static void appendChecksum(ChannelBuffer buffer, int length) {
+ public static void appendChecksum(ByteBuf buffer, int length) {
buffer.writeByte('*');
- int checksum = Checksum.xor(buffer.toByteBuffer(0, length)) & 0xFF;
+ int checksum = Checksum.xor(buffer.nioBuffer(0, length)) & 0xFF;
String checksumString = String.format("%02X", checksum);
buffer.writeBytes(checksumString.getBytes(StandardCharsets.US_ASCII));
buffer.writeByte('\r'); buffer.writeByte('\n');
}
private static void sendResponseCurrent(Channel channel, int deviceId, long time) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
+ ByteBuf response = Unpooled.buffer();
response.writeBytes("BB+UGRC~".getBytes(StandardCharsets.US_ASCII));
- response.writeShort(6); // length
+ response.writeShortLE(6); // length
response.writeInt((int) time);
- response.writeShort(deviceId);
+ response.writeShortLE(deviceId);
appendChecksum(response, 16);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
private static void sendResponseArchive(Channel channel, int deviceId, int packNum) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
+ ByteBuf response = Unpooled.buffer();
response.writeBytes("BB+ARCF~".getBytes(StandardCharsets.US_ASCII));
- response.writeShort(4); // length
- response.writeShort(packNum);
- response.writeShort(deviceId);
+ response.writeShortLE(4); // length
+ response.writeShortLE(packNum);
+ response.writeShortLE(deviceId);
appendChecksum(response, 14);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
- private void decodeStructure(ChannelBuffer buf, Position position) {
+ private void decodeStructure(ByteBuf buf, Position position) {
short flags = buf.readUnsignedByte();
position.setValid(BitUtil.check(flags, 7));
if (BitUtil.check(flags, 1)) {
@@ -93,8 +93,8 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
int lonDegrees = buf.readUnsignedByte();
int latDegrees = buf.readUnsignedByte();
- int lonMinutes = buf.readUnsignedShort();
- int latMinutes = buf.readUnsignedShort();
+ int lonMinutes = buf.readUnsignedShortLE();
+ int latMinutes = buf.readUnsignedShortLE();
double latitude = latDegrees + latMinutes / 60000.0;
double longitude = lonDegrees + lonMinutes / 60000.0;
@@ -119,7 +119,7 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
}
position.setCourse(course);
- position.set(Position.KEY_DISTANCE, buf.readShort());
+ position.set(Position.KEY_DISTANCE, buf.readShortLE());
int analogIn1 = buf.readUnsignedByte();
int analogIn2 = buf.readUnsignedByte();
@@ -150,16 +150,15 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
@Override
protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- int indexTilde = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("~"));
+ int indexTilde = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '~');
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession != null && indexTilde == -1) {
String bufString = buf.toString(StandardCharsets.US_ASCII);
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date());
@@ -172,22 +171,21 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
if (buf.readableBytes() < HEADER_LENGTH) {
return null;
}
- String header = buf.readBytes(HEADER_LENGTH).toString(StandardCharsets.US_ASCII);
+ String header = buf.readSlice(HEADER_LENGTH).toString(StandardCharsets.US_ASCII);
if (header.equals("+RRCB~")) {
- buf.skipBytes(2); //binary length 26
- int deviceId = buf.readUnsignedShort();
+ buf.skipBytes(2); // binary length 26
+ int deviceId = buf.readUnsignedShortLE();
deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
if (deviceSession == null) {
return null;
}
- long unixTime = buf.readUnsignedInt();
+ long unixTime = buf.readUnsignedIntLE();
if (channel != null) {
sendResponseCurrent(channel, deviceId, unixTime);
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date(unixTime * 1000));
@@ -197,8 +195,8 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
} else if (header.equals("+DDAT~")) {
- buf.skipBytes(2); //binary length
- int deviceId = buf.readUnsignedShort();
+ buf.skipBytes(2); // binary length
+ int deviceId = buf.readUnsignedShortLE();
deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
if (deviceSession == null) {
return null;
@@ -208,19 +206,18 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
return null;
}
byte nblocks = buf.readByte();
- int packNum = buf.readUnsignedShort();
+ int packNum = buf.readUnsignedShortLE();
if (channel != null) {
sendResponseArchive(channel, deviceId, packNum);
}
List<Position> positions = new ArrayList<>();
while (nblocks > 0) {
nblocks--;
- long unixTime = buf.readUnsignedInt();
- int timeIncrement = buf.getUnsignedShort(buf.readerIndex() + 120);
+ long unixTime = buf.readUnsignedIntLE();
+ int timeIncrement = buf.getUnsignedShortLE(buf.readerIndex() + 120);
for (int i = 0; i < 6; i++) {
if (buf.getUnsignedByte(buf.readerIndex()) != 0xFE) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date((unixTime + i * timeIncrement) * 1000));
decodeStructure(buf, position);
diff --git a/src/org/traccar/protocol/GranitProtocolEncoder.java b/src/main/java/org/traccar/protocol/GranitProtocolEncoder.java
index dbfd30ff1..66c2a4973 100644
--- a/src/org/traccar/protocol/GranitProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -13,43 +13,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- package org.traccar.protocol;
+package org.traccar.protocol;
import java.nio.charset.StandardCharsets;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class GranitProtocolEncoder extends BaseProtocolEncoder {
- @Override
- protected Object encodeCommand(Command command) {
- String commandString = "";
+ public GranitProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeCommand(String commandString) {
+ ByteBuf buffer = Unpooled.buffer();
+ buffer.writeBytes(commandString.getBytes(StandardCharsets.US_ASCII));
+ GranitProtocolDecoder.appendChecksum(buffer, commandString.length());
+ return buffer;
+ }
+ @Override
+ protected Object encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_IDENTIFICATION:
- commandString = "BB+IDNT";
- break;
+ return encodeCommand("BB+IDNT");
case Command.TYPE_REBOOT_DEVICE:
- commandString = "BB+RESET";
- break;
+ return encodeCommand("BB+RESET");
case Command.TYPE_POSITION_SINGLE:
- commandString = "BB+RRCD";
- break;
+ return encodeCommand("BB+RRCD");
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
return null;
}
- if (!commandString.isEmpty()) {
- ChannelBuffer commandBuf = ChannelBuffers.dynamicBuffer();
- commandBuf.writeBytes(commandString.getBytes(StandardCharsets.US_ASCII));
- GranitProtocolDecoder.appendChecksum(commandBuf, commandString.length());
- return commandBuf;
- }
- return null;
}
}
diff --git a/src/org/traccar/protocol/GranitProtocolSmsEncoder.java b/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
index 668e5d4d3..be0ab5130 100644
--- a/src/org/traccar/protocol/GranitProtocolSmsEncoder.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,20 +17,23 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class GranitProtocolSmsEncoder extends StringProtocolEncoder {
+ public GranitProtocolSmsEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected String encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
return "BB+RESET";
case Command.TYPE_POSITION_PERIODIC:
- return formatCommand(command, "BB+BBMD={%s}", Command.KEY_FREQUENCY);
+ return formatCommand(command, "BB+BBMD=%s", Command.KEY_FREQUENCY);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/Gt02Protocol.java b/src/main/java/org/traccar/protocol/Gt02Protocol.java
new file mode 100644
index 000000000..f412ee720
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gt02Protocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Gt02Protocol extends BaseProtocol {
+
+ public Gt02Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0));
+ pipeline.addLast(new Gt02ProtocolDecoder(Gt02Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gt02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java
index a520bff13..78a3fd3ee 100644
--- a/src/org/traccar/protocol/Gt02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt02ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,11 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
@@ -30,7 +33,7 @@ import java.nio.charset.StandardCharsets;
public class Gt02ProtocolDecoder extends BaseProtocolDecoder {
- public Gt02ProtocolDecoder(Gt02Protocol protocol) {
+ public Gt02ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -42,19 +45,18 @@ public class Gt02ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
buf.readByte(); // size
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
// Zero for location messages
int power = buf.readUnsignedByte();
int gsm = buf.readUnsignedByte();
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
@@ -74,7 +76,7 @@ public class Gt02ProtocolDecoder extends BaseProtocolDecoder {
if (channel != null) {
byte[] response = {0x54, 0x68, 0x1A, 0x0D, 0x0A};
- channel.write(ChannelBuffers.wrappedBuffer(response));
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(response), remoteAddress));
}
} else if (type == MSG_DATA) {
@@ -109,7 +111,7 @@ public class Gt02ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
position.set(Position.KEY_RESULT,
- buf.readBytes(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII));
+ buf.readSlice(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII));
} else {
diff --git a/src/org/traccar/protocol/Gt06FrameDecoder.java b/src/main/java/org/traccar/protocol/Gt06FrameDecoder.java
index c8b5e56ae..cc934be42 100644
--- a/src/org/traccar/protocol/Gt06FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,16 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class Gt06FrameDecoder extends FrameDecoder {
+public class Gt06FrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 5) {
return null;
@@ -39,14 +39,14 @@ public class Gt06FrameDecoder extends FrameDecoder {
}
if (buf.readableBytes() >= length && buf.getUnsignedShort(buf.readerIndex() + length - 2) == 0x0d0a) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
int endIndex = buf.readerIndex() - 1;
do {
endIndex = buf.indexOf(endIndex + 1, buf.writerIndex(), (byte) 0x0d);
if (endIndex > 0 && buf.writerIndex() > endIndex + 1 && buf.getByte(endIndex + 1) == 0x0a) {
- return buf.readBytes(endIndex + 2 - buf.readerIndex());
+ return buf.readRetainedSlice(endIndex + 2 - buf.readerIndex());
}
} while (endIndex > 0);
diff --git a/src/org/traccar/protocol/Gt06Protocol.java b/src/main/java/org/traccar/protocol/Gt06Protocol.java
index f45ac95ee..9ec8de098 100644
--- a/src/org/traccar/protocol/Gt06Protocol.java
+++ b/src/main/java/org/traccar/protocol/Gt06Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,32 +15,24 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class Gt06Protocol extends BaseProtocol {
public Gt06Protocol() {
- super("gt06");
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_CUSTOM);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Gt06FrameDecoder());
- pipeline.addLast("objectEncoder", new Gt06ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Gt06ProtocolDecoder(Gt06Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..946652b03
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -0,0 +1,1199 @@
+/*
+ * Copyright 2012 - 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+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;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Pattern;
+
+public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
+
+ private final Map<Integer, ByteBuf> photos = new HashMap<>();
+
+ public Gt06ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_GPS = 0x10;
+ public static final int MSG_LBS = 0x11;
+ public static final int MSG_GPS_LBS_1 = 0x12;
+ public static final int MSG_GPS_LBS_2 = 0x22;
+ public static final int MSG_STATUS = 0x13;
+ public static final int MSG_SATELLITE = 0x14;
+ public static final int MSG_STRING = 0x15;
+ public static final int MSG_GPS_LBS_STATUS_1 = 0x16;
+ public static final int MSG_WIFI = 0x17;
+ public static final int MSG_GPS_LBS_STATUS_2 = 0x26;
+ public static final int MSG_GPS_LBS_STATUS_3 = 0x27;
+ public static final int MSG_LBS_MULTIPLE = 0x28;
+ 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_AZ735_GPS = 0x32;
+ public static final int MSG_AZ735_ALARM = 0x33;
+ 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;
+ public static final int MSG_WIFI_2 = 0x69;
+ public static final int MSG_GPS_MODULAR = 0x70;
+ 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_INFO = 0x94;
+ public static final int MSG_RFID = 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;
+
+ private static boolean isSupported(int type) {
+ return hasGps(type) || hasLbs(type) || hasStatus(type);
+ }
+
+ private static boolean hasGps(int type) {
+ switch (type) {
+ case MSG_GPS:
+ case MSG_GPS_LBS_1:
+ case MSG_GPS_LBS_2:
+ case MSG_GPS_LBS_STATUS_1:
+ case MSG_GPS_LBS_STATUS_2:
+ case MSG_GPS_LBS_STATUS_3:
+ case MSG_GPS_PHONE:
+ case MSG_GPS_LBS_EXTEND:
+ case MSG_GPS_2:
+ case MSG_FENCE_SINGLE:
+ case MSG_FENCE_MULTI:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean hasLbs(int type) {
+ switch (type) {
+ case MSG_LBS:
+ case MSG_LBS_STATUS:
+ case MSG_GPS_LBS_1:
+ case MSG_GPS_LBS_2:
+ case MSG_GPS_LBS_STATUS_1:
+ case MSG_GPS_LBS_STATUS_2:
+ case MSG_GPS_LBS_STATUS_3:
+ case MSG_GPS_2:
+ case MSG_FENCE_SINGLE:
+ case MSG_FENCE_MULTI:
+ case MSG_LBS_ALARM:
+ case MSG_LBS_ADDRESS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean hasStatus(int type) {
+ switch (type) {
+ case MSG_STATUS:
+ case MSG_LBS_STATUS:
+ case MSG_GPS_LBS_STATUS_1:
+ case MSG_GPS_LBS_STATUS_2:
+ case MSG_GPS_LBS_STATUS_3:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean hasLanguage(int type) {
+ switch (type) {
+ case MSG_GPS_PHONE:
+ case MSG_HEARTBEAT:
+ case MSG_GPS_LBS_STATUS_3:
+ case MSG_LBS_MULTIPLE:
+ case MSG_LBS_2:
+ case MSG_FENCE_MULTI:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void sendResponse(Channel channel, boolean extended, int type, int index, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ int length = 5 + (content != null ? content.readableBytes() : 0);
+ if (extended) {
+ response.writeShort(0x7979);
+ response.writeShort(length);
+ } else {
+ response.writeShort(0x7878);
+ response.writeByte(length);
+ }
+ response.writeByte(type);
+ if (content != null) {
+ response.writeBytes(content);
+ content.release();
+ }
+ response.writeShort(index);
+ response.writeShort(Checksum.crc16(Checksum.CRC16_X25,
+ response.nioBuffer(2, response.writerIndex() - 2)));
+ response.writeByte('\r');
+ response.writeByte('\n');
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private void sendPhotoRequest(Channel channel, int pictureId) {
+ ByteBuf photo = photos.get(pictureId);
+ ByteBuf content = Unpooled.buffer();
+ content.writeInt(pictureId);
+ content.writeInt(photo.writerIndex());
+ content.writeShort(Math.min(photo.writableBytes(), 1024));
+ sendResponse(channel, false, MSG_X1_PHOTO_DATA, 0, content);
+ }
+
+ public static boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) {
+
+ DateBuilder dateBuilder = new DateBuilder(timezone)
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ if (hasLength && buf.readUnsignedByte() == 0) {
+ return false;
+ }
+
+ position.set(Position.KEY_SATELLITES, BitUtil.to(buf.readUnsignedByte(), 4));
+
+ 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);
+
+ if (BitUtil.check(flags, 14)) {
+ position.set(Position.KEY_IGNITION, BitUtil.check(flags, 15));
+ }
+
+ return true;
+ }
+
+ private boolean decodeLbs(Position position, ByteBuf buf, boolean hasLength) {
+
+ int length = 0;
+ if (hasLength) {
+ length = buf.readUnsignedByte();
+ if (length == 0) {
+ return false;
+ }
+ }
+
+ int mcc = buf.readUnsignedShort();
+ int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte();
+
+ position.setNetwork(new Network(CellTower.from(
+ BitUtil.to(mcc, 15), mnc, buf.readUnsignedShort(), buf.readUnsignedMedium())));
+
+ if (length > 9) {
+ buf.skipBytes(length - 9);
+ }
+
+ return true;
+ }
+
+ private boolean decodeStatus(Position position, ByteBuf buf) {
+
+ int status = buf.readUnsignedByte();
+
+ position.set(Position.KEY_STATUS, status);
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 1));
+ position.set(Position.KEY_CHARGE, BitUtil.check(status, 2));
+ position.set(Position.KEY_BLOCKED, BitUtil.check(status, 7));
+
+ switch (BitUtil.between(status, 3, 6)) {
+ case 1:
+ position.set(Position.KEY_ALARM, Position.ALARM_SHOCK);
+ break;
+ case 2:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case 3:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case 4:
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ break;
+ case 7:
+ position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ break;
+ default:
+ break;
+ }
+
+ 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()));
+
+ return true;
+ }
+
+ private String decodeAlarm(short value) {
+ switch (value) {
+ case 0x01:
+ return Position.ALARM_SOS;
+ case 0x02:
+ return Position.ALARM_POWER_CUT;
+ case 0x03:
+ case 0x09:
+ return Position.ALARM_VIBRATION;
+ case 0x04:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 0x05:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 0x06:
+ return Position.ALARM_OVERSPEED;
+ case 0x0E:
+ case 0x0F:
+ return Position.ALARM_LOW_BATTERY;
+ case 0x11:
+ return Position.ALARM_POWER_OFF;
+ case 0x13:
+ return Position.ALARM_TAMPERING;
+ case 0x14:
+ return Position.ALARM_DOOR;
+ case 0x29:
+ return Position.ALARM_ACCELERATION;
+ case 0x30:
+ return Position.ALARM_BRAKING;
+ case 0x2A:
+ case 0x2B:
+ 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 {
+
+ int length = buf.readUnsignedByte();
+ int dataLength = length - 5;
+ int type = buf.readUnsignedByte();
+
+ 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()));
+ }
+ }
+
+ if (type == MSG_LOGIN) {
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
+ buf.readUnsignedShort(); // type
+
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession != null && deviceSession.getTimeZone() == null) {
+ deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ }
+
+ if (dataLength > 10) {
+ int extensionBits = buf.readUnsignedShort();
+ int hours = (extensionBits >> 4) / 100;
+ int minutes = (extensionBits >> 4) % 100;
+ int offset = (hours * 60 + minutes) * 60;
+ if ((extensionBits & 0x8) != 0) {
+ offset = -offset;
+ }
+ if (deviceSession != null) {
+ TimeZone timeZone = deviceSession.getTimeZone();
+ if (timeZone.getRawOffset() == 0) {
+ timeZone.setRawOffset(offset * 1000);
+ deviceSession.setTimeZone(timeZone);
+ }
+ }
+
+ }
+
+ if (deviceSession != null) {
+ sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
+ }
+
+ } else if (type == MSG_HEARTBEAT) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_ARMED, BitUtil.check(status, 0));
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 1));
+ position.set(Position.KEY_CHARGE, BitUtil.check(status, 2));
+
+ if (buf.readableBytes() >= 2 + 6) {
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ }
+ if (buf.readableBytes() >= 1 + 6) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
+
+ sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
+
+ return position;
+
+ } else if (type == MSG_ADDRESS_REQUEST) {
+
+ String response = "NA&&NA&&0##";
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(response.length());
+ content.writeInt(0);
+ content.writeBytes(response.getBytes(StandardCharsets.US_ASCII));
+ sendResponse(channel, true, MSG_ADDRESS_RESPONSE, 0, content);
+
+ } else if (type == MSG_TIME_REQUEST) {
+
+ Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(calendar.get(Calendar.YEAR) - 2000);
+ content.writeByte(calendar.get(Calendar.MONTH) + 1);
+ content.writeByte(calendar.get(Calendar.DAY_OF_MONTH));
+ content.writeByte(calendar.get(Calendar.HOUR_OF_DAY));
+ content.writeByte(calendar.get(Calendar.MINUTE));
+ 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) {
+
+ 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) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedInt(); // data and alarm
+
+ decodeGps(position, buf, false, deviceSession.getTimeZone());
+
+ buf.readUnsignedShort(); // terminal info
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedByte(),
+ buf.readUnsignedShort(), buf.readUnsignedInt())));
+
+ long driverId = buf.readUnsignedInt();
+ if (driverId > 0) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(driverId));
+ }
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+
+ long portInfo = buf.readUnsignedInt();
+
+ position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+
+ for (int i = 1; i <= BitUtil.between(portInfo, 20, 24); i++) {
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort() * 0.01);
+ }
+
+ return position;
+
+ } else if (type == MSG_X1_PHOTO_INFO) {
+
+ buf.skipBytes(6); // time
+ buf.readUnsignedByte(); // fix status
+ buf.readUnsignedInt(); // latitude
+ buf.readUnsignedInt(); // longitude
+ buf.readUnsignedByte(); // camera id
+ buf.readUnsignedByte(); // photo source
+ buf.readUnsignedByte(); // picture format
+
+ ByteBuf photo = Unpooled.buffer(buf.readInt());
+ int pictureId = buf.readInt();
+ photos.put(pictureId, photo);
+ sendPhotoRequest(channel, pictureId);
+
+ }
+
+ return null;
+ }
+
+ private Object decodeWifi(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
+
+ 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());
+
+ Network network = new Network();
+
+ int wifiCount = buf.getByte(2);
+ for (int i = 0; i < wifiCount; i++) {
+ String mac = String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(),
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ network.addWifiAccessPoint(WifiAccessPoint.from(mac, 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()));
+ }
+
+ position.setNetwork(network);
+
+ 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()));
+ }
+
+ return position;
+ }
+
+ private Object decodeBasicOther(
+ Channel channel, ByteBuf buf, DeviceSession deviceSession, int type, int dataLength) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI
+ || type == MSG_LBS_2 || type == MSG_WIFI_3) {
+
+ boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3;
+
+ DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ int mcc = buf.readUnsignedShort();
+ int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte();
+ Network network = new Network();
+ for (int i = 0; i < 7; i++) {
+ int lac = longFormat ? buf.readInt() : buf.readUnsignedShort();
+ int cid = longFormat ? (int) buf.readLong() : buf.readUnsignedMedium();
+ int rssi = -buf.readUnsignedByte();
+ if (lac > 0) {
+ network.addCellTower(CellTower.from(BitUtil.to(mcc, 15), mnc, lac, cid, rssi));
+ }
+ }
+
+ buf.readUnsignedByte(); // time leads
+
+ if (type != MSG_LBS_MULTIPLE && type != MSG_LBS_2) {
+ int wifiCount = buf.readUnsignedByte();
+ for (int i = 0; i < wifiCount; i++) {
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
+ }
+ }
+
+ position.setNetwork(network);
+
+ } else if (type == MSG_STRING) {
+
+ getLastLocation(position, null);
+
+ int commandLength = buf.readUnsignedByte();
+
+ if (commandLength > 0) {
+ buf.readUnsignedByte(); // server flag (reserved)
+ position.set(Position.KEY_RESULT,
+ buf.readSlice(commandLength - 1).toString(StandardCharsets.US_ASCII));
+ }
+
+ } else if (type == MSG_BMS || type == MSG_BMS_2) {
+
+ buf.skipBytes(8); // serial number
+
+ getLastLocation(position, new Date(buf.readUnsignedInt() * 1000));
+
+ position.set("relativeCapacity", buf.readUnsignedByte());
+ position.set("remainingCapacity", buf.readUnsignedShort());
+ position.set("absoluteCapacity", buf.readUnsignedByte());
+ position.set("fullCapacity", buf.readUnsignedShort());
+ position.set("batteryHealth", buf.readUnsignedByte());
+ position.set("batteryTemp", buf.readUnsignedShort() * 0.1 - 273.1);
+ position.set("current", buf.readUnsignedShort());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set("cycleIndex", buf.readUnsignedShort());
+ for (int i = 1; i <= 14; i++) {
+ position.set("batteryCell" + i, buf.readUnsignedShort() * 0.001);
+ }
+ position.set("currentChargeInterval", buf.readUnsignedShort());
+ position.set("maxChargeInterval", buf.readUnsignedShort());
+ position.set("barcode", buf.readCharSequence(16, StandardCharsets.US_ASCII).toString().trim());
+ position.set("batteryVersion", buf.readUnsignedShort());
+ position.set("manufacturer", buf.readCharSequence(16, StandardCharsets.US_ASCII).toString().trim());
+ position.set("batteryStatus", buf.readUnsignedInt());
+
+ position.set("controllerStatus", buf.readUnsignedInt());
+ position.set("controllerFault", buf.readUnsignedInt());
+
+ sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
+
+ return position;
+
+ } else if (isSupported(type)) {
+
+ if (hasGps(type)) {
+ decodeGps(position, buf, false, deviceSession.getTimeZone());
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (hasLbs(type)) {
+ decodeLbs(position, buf, hasStatus(type));
+ }
+
+ if (hasStatus(type)) {
+ decodeStatus(position, buf);
+ }
+
+ 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() == 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_1 && buf.readableBytes() == 4 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
+
+ if (type == MSG_GPS_LBS_2 && 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);
+ }
+
+ } else if (type == MSG_ALARM) {
+
+ DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ short alarmType = buf.readUnsignedByte();
+
+ switch (alarmType) {
+ case 0x80:
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ break;
+ case 0x87:
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ case 0x90:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 0x91:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 0x92:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ break;
+ }
+
+ position.set("alarmValue", buf.readShort());
+
+ } else {
+
+ if (dataLength > 0) {
+ buf.skipBytes(dataLength);
+ }
+ if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) {
+ sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
+ }
+ return null;
+
+ }
+
+ if (hasLanguage(type)) {
+ buf.readUnsignedShort();
+ }
+
+ if (type == MSG_GPS_LBS_STATUS_3 || type == MSG_FENCE_MULTI) {
+ position.set(Position.KEY_GEOFENCE, buf.readUnsignedByte());
+ }
+
+ sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null);
+
+ return position;
+ }
+
+ private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (deviceSession.getTimeZone() == null) {
+ deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedShort(); // length
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_STRING_INFO) {
+
+ buf.readUnsignedInt(); // server flag
+ String data;
+ if (buf.readUnsignedByte() == 1) {
+ data = buf.readSlice(buf.readableBytes() - 6).toString(StandardCharsets.US_ASCII);
+ } else {
+ data = buf.readSlice(buf.readableBytes() - 6).toString(StandardCharsets.UTF_16BE);
+ }
+
+ if (decodeLocationString(position, data) == null) {
+ getLastLocation(position, null);
+ position.set(Position.KEY_RESULT, data);
+ }
+
+ return position;
+
+ } else if (type == MSG_INFO) {
+
+ int subType = buf.readUnsignedByte();
+
+ getLastLocation(position, null);
+
+ if (subType == 0x00) {
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.01);
+ return position;
+ } else if (subType == 0x05) {
+ 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(8)));
+ return position;
+ } else if (subType == 0x0d) {
+ if (buf.getByte(buf.readerIndex()) != '!') {
+ buf.skipBytes(6);
+ }
+ return decodeFuelData(position, buf.toString(
+ buf.readerIndex(), buf.readableBytes() - 4 - 2, StandardCharsets.US_ASCII));
+ }
+
+ } else if (type == MSG_X1_PHOTO_DATA) {
+
+ int pictureId = buf.readInt();
+
+ ByteBuf photo = photos.get(pictureId);
+
+ buf.readUnsignedInt(); // offset
+ buf.readBytes(photo, buf.readUnsignedShort());
+
+ 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"));
+ photos.remove(pictureId).release();
+ }
+
+ } else if (type == MSG_AZ735_GPS || type == MSG_AZ735_ALARM) {
+
+ if (!decodeGps(position, buf, true, deviceSession.getTimeZone())) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ if (decodeLbs(position, buf, true)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
+
+ buf.skipBytes(buf.readUnsignedByte()); // additional cell towers
+ buf.skipBytes(buf.readUnsignedByte()); // wifi access point
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_STATUS, status);
+
+ if (type == MSG_AZ735_ALARM) {
+ switch (status) {
+ case 0xA0:
+ position.set(Position.KEY_ARMED, true);
+ break;
+ case 0xA1:
+ position.set(Position.KEY_ARMED, false);
+ break;
+ case 0xA2:
+ case 0xA3:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case 0xA4:
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ break;
+ case 0xA5:
+ position.set(Position.KEY_ALARM, Position.ALARM_DOOR);
+ break;
+ default:
+ break;
+ }
+ }
+
+ buf.skipBytes(buf.readUnsignedByte()); // reserved extension
+
+ sendResponse(channel, true, type, buf.getShort(buf.writerIndex() - 6), null);
+
+ return position;
+
+ } else if (type == MSG_OBD) {
+
+ DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
+
+ String data = buf.readCharSequence(buf.readableBytes() - 18, StandardCharsets.US_ASCII).toString();
+ for (String pair : data.split(",")) {
+ String[] values = pair.split("=");
+ switch (Integer.parseInt(values[0].substring(0, 2), 16)) {
+ case 40:
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 43:
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 45:
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 53:
+ position.set(Position.KEY_OBD_SPEED, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 54:
+ position.set(Position.KEY_RPM, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 71:
+ position.set(Position.KEY_FUEL_USED, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 73:
+ position.set(Position.KEY_HOURS, Integer.parseInt(values[1], 16) * 0.01);
+ break;
+ case 74:
+ position.set(Position.KEY_VIN, values[1]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return position;
+
+ } else if (type == MSG_RFID) {
+
+ getLastLocation(position, null);
+
+ buf.readUnsignedByte(); // external device type code
+ buf.readUnsignedByte(); // card type
+ position.set(
+ Position.KEY_DRIVER_UNIQUE_ID,
+ buf.readCharSequence(buf.readableBytes() - 9, StandardCharsets.US_ASCII).toString());
+
+ return position;
+
+ } else if (type == MSG_GPS_MODULAR) {
+
+ return decodeExtendedModular(buf, deviceSession);
+
+ } else {
+
+ return decodeExtendedOther(channel, buf, deviceSession, type);
+
+ }
+
+ return null;
+ }
+
+ private Object decodeExtendedModular(ByteBuf buf, DeviceSession deviceSession) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ while (buf.readableBytes() > 6) {
+ int moduleType = buf.readUnsignedShort();
+ int moduleLength = buf.readUnsignedShort();
+
+ switch (moduleType) {
+ case 0x03:
+ position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10)));
+ break;
+ case 0x09:
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x0a:
+ position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
+ break;
+ case 0x11:
+ CellTower cellTower = CellTower.from(
+ buf.readUnsignedShort(),
+ buf.readUnsignedShort(),
+ buf.readUnsignedShort(),
+ buf.readUnsignedMedium(),
+ buf.readUnsignedByte());
+ if (cellTower.getCellId() > 0) {
+ position.setNetwork(new Network(cellTower));
+ }
+ break;
+ case 0x18:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x28:
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
+ break;
+ case 0x29:
+ position.set(Position.KEY_INDEX, buf.readUnsignedInt());
+ break;
+ case 0x2a:
+ int input = buf.readUnsignedByte();
+ position.set(Position.KEY_DOOR, BitUtil.to(input, 4) > 0);
+ position.set("tamper", BitUtil.from(input, 4) > 0);
+ break;
+ case 0x2b:
+ int event = buf.readUnsignedByte();
+ switch (event) {
+ case 0x11:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case 0x12:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ break;
+ case 0x13:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case 0x14:
+ position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ break;
+ default:
+ break;
+ }
+ position.set(Position.KEY_EVENT, event);
+ break;
+ case 0x2e:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ break;
+ case 0x33:
+ position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.setAltitude(buf.readShort());
+
+ double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+ double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ int flags = buf.readUnsignedShort();
+ position.setCourse(BitUtil.to(flags, 10));
+ position.setValid(BitUtil.check(flags, 12));
+
+ if (!BitUtil.check(flags, 10)) {
+ latitude = -latitude;
+ }
+ if (BitUtil.check(flags, 11)) {
+ longitude = -longitude;
+ }
+
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ break;
+ case 0x34:
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ buf.readUnsignedIntLE(); // time
+ buf.skipBytes(buf.readUnsignedByte()); // content
+ break;
+ default:
+ buf.skipBytes(moduleLength);
+ break;
+ }
+ }
+
+ return position;
+ }
+
+ private Object decodeExtendedOther(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
+
+ Position position = null;
+
+ if (type == MSG_MULTIMEDIA || type == MSG_MULTIMEDIA_2) {
+
+ buf.skipBytes(8); // serial number
+ long timestamp = buf.readUnsignedInt() * 1000;
+ buf.skipBytes(4 + 4 + 2 + 1 + 1 + 2); // gps
+ buf.skipBytes(2 + 2 + 2 + 2); // cell
+
+ int mediaId = buf.readInt();
+ int mediaLength = buf.readInt();
+ int mediaType = buf.readUnsignedByte();
+ int mediaFormat = buf.readUnsignedByte();
+
+ if (mediaType == 0 && mediaFormat == 0) {
+
+ buf.readUnsignedByte(); // event
+
+ ByteBuf photo;
+ if (buf.readUnsignedShort() == 0) {
+ photo = Unpooled.buffer(mediaLength);
+ if (photos.containsKey(mediaId)) {
+ photos.remove(mediaId).release();
+ }
+ photos.put(mediaId, photo);
+ } else {
+ photo = photos.get(mediaId);
+ }
+
+ if (photo != null) {
+ buf.readBytes(photo, buf.readableBytes() - 3 * 2);
+ if (!photo.isWritable()) {
+ 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"));
+ photos.remove(mediaId).release();
+ }
+ }
+
+ }
+
+ sendResponse(channel, true, type, buf.getShort(buf.writerIndex() - 6), null);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int header = buf.readShort();
+
+ if (header == 0x7878) {
+ return decodeBasic(channel, remoteAddress, buf);
+ } else if (header == 0x7979) {
+ return decodeExtended(channel, remoteAddress, buf);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
index a3e0a2264..9115ba10f 100644
--- a/src/org/traccar/protocol/Gt06ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,26 +15,33 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.Context;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
import java.nio.charset.StandardCharsets;
public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeContent(String content) {
+ public Gt06ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(long deviceId, String content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ boolean language = Context.getIdentityManager()
+ .lookupAttributeBoolean(deviceId, getProtocolName() + ".language", false, false, true);
+
+ ByteBuf buf = Unpooled.buffer();
buf.writeByte(0x78);
buf.writeByte(0x78);
- buf.writeByte(1 + 1 + 4 + content.length() + 2 + 2); // message length
+ buf.writeByte(1 + 1 + 4 + content.length() + 2 + 2 + (language ? 2 : 0)); // message length
buf.writeByte(0x80); // message type
@@ -42,9 +49,13 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
buf.writeInt(0);
buf.writeBytes(content.getBytes(StandardCharsets.US_ASCII)); // command
+ if (language) {
+ buf.writeShort(2); // english language
+ }
+
buf.writeShort(0); // message index
- buf.writeShort(Checksum.crc16(Checksum.CRC16_X25, buf.toByteBuffer(2, buf.writerIndex() - 2)));
+ buf.writeShort(Checksum.crc16(Checksum.CRC16_X25, buf.nioBuffer(2, buf.writerIndex() - 2)));
buf.writeByte('\r');
buf.writeByte('\n');
@@ -56,21 +67,23 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
protected Object encodeCommand(Command command) {
boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
- command.getDeviceId(), "gt06.alternative", false, true);
+ command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+
+ String password = Context.getIdentityManager()
+ .getDevicePassword(command.getDeviceId(), getProtocolName(), "123456");
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return encodeContent(alternative ? "DYD,123456#" : "Relay,1#");
+ return encodeContent(command.getDeviceId(),
+ alternative ? "DYD," + password + "#" : "Relay,1#");
case Command.TYPE_ENGINE_RESUME:
- return encodeContent(alternative ? "HFYD,123456#" : "Relay,0#");
+ return encodeContent(command.getDeviceId(),
+ alternative ? "HFYD," + password + "#" : "Relay,0#");
case Command.TYPE_CUSTOM:
- return encodeContent(command.getString(Command.KEY_DATA));
+ return encodeContent(command.getDeviceId(), command.getString(Command.KEY_DATA));
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/Gt30Protocol.java b/src/main/java/org/traccar/protocol/Gt30Protocol.java
new file mode 100644
index 000000000..aa4ad20b1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Gt30Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Gt30Protocol extends BaseProtocol {
+
+ public Gt30Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Gt30ProtocolDecoder(Gt30Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gt30ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java
index 51135c80a..abf208a46 100644
--- a/src/org/traccar/protocol/Gt30ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt30ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class Gt30ProtocolDecoder extends BaseProtocolDecoder {
- public Gt30ProtocolDecoder(Gt30Protocol protocol) {
+ public Gt30ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -84,8 +85,7 @@ public class Gt30ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.hasNext()) {
diff --git a/src/org/traccar/protocol/H02FrameDecoder.java b/src/main/java/org/traccar/protocol/H02FrameDecoder.java
index 391fccc87..583ad599f 100644
--- a/src/org/traccar/protocol/H02FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,12 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class H02FrameDecoder extends FrameDecoder {
+public class H02FrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_SHORT = 32;
private static final int MESSAGE_LONG = 45;
@@ -33,7 +33,7 @@ public class H02FrameDecoder extends FrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
char marker = (char) buf.getByte(buf.readerIndex());
@@ -50,8 +50,8 @@ public class H02FrameDecoder extends FrameDecoder {
// Return text message
int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '#');
if (index != -1) {
- ChannelBuffer result = buf.readBytes(index + 1 - buf.readerIndex());
- while (buf.readable()
+ ByteBuf result = buf.readRetainedSlice(index + 1 - buf.readerIndex());
+ while (buf.isReadable()
&& (buf.getByte(buf.readerIndex()) == '\r' || buf.getByte(buf.readerIndex()) == '\n')) {
buf.readByte(); // skip new line
}
@@ -71,7 +71,7 @@ public class H02FrameDecoder extends FrameDecoder {
}
if (buf.readableBytes() >= messageLength) {
- return buf.readBytes(messageLength);
+ return buf.readRetainedSlice(messageLength);
}
break;
@@ -79,7 +79,7 @@ public class H02FrameDecoder extends FrameDecoder {
case 'X':
if (buf.readableBytes() >= MESSAGE_SHORT) {
- return buf.readBytes(MESSAGE_SHORT);
+ return buf.readRetainedSlice(MESSAGE_SHORT);
}
break;
diff --git a/src/org/traccar/protocol/H02Protocol.java b/src/main/java/org/traccar/protocol/H02Protocol.java
index 66965e9db..b897d83ad 100644
--- a/src/org/traccar/protocol/H02Protocol.java
+++ b/src/main/java/org/traccar/protocol/H02Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,21 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+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.model.Command;
-import java.util.List;
-
public class H02Protocol extends BaseProtocol {
public H02Protocol() {
- super("h02");
setSupportedDataCommands(
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM,
@@ -37,26 +32,22 @@ public class H02Protocol extends BaseProtocol {
Command.TYPE_ENGINE_RESUME,
Command.TYPE_POSITION_PERIODIC
);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
int messageLength = Context.getConfig().getInteger(getName() + ".messageLength");
- pipeline.addLast("frameDecoder", new H02FrameDecoder(messageLength));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new H02ProtocolEncoder());
- pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this));
+ pipeline.addLast(new H02FrameDecoder(messageLength));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new H02ProtocolEncoder(H02Protocol.this));
+ pipeline.addLast(new H02ProtocolDecoder(H02Protocol.this));
}
});
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new H02ProtocolEncoder());
- pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new H02ProtocolEncoder(H02Protocol.this));
+ pipeline.addLast(new H02ProtocolDecoder(H02Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index 4414870d2..320fe991d 100644
--- a/src/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,11 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
@@ -31,16 +34,19 @@ 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;
import java.util.regex.Pattern;
public class H02ProtocolDecoder extends BaseProtocolDecoder {
- public H02ProtocolDecoder(H02Protocol protocol) {
+ public H02ProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private static double readCoordinate(ChannelBuffer buf, boolean lon) {
+ private static double readCoordinate(ByteBuf buf, boolean lon) {
int degrees = BcdUtil.readInteger(buf, 2);
if (lon) {
@@ -69,7 +75,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
if (!BitUtil.check(status, 0)) {
position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
- } else if (!BitUtil.check(status, 1)) {
+ } else if (!BitUtil.check(status, 1) || !BitUtil.check(status, 18)) {
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
} else if (!BitUtil.check(status, 2)) {
position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
@@ -83,31 +89,37 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
}
private Integer decodeBattery(int value) {
- switch (value) {
- case 6:
- return 100;
- case 5:
- return 80;
- case 4:
- return 60;
- case 3:
- return 20;
- case 2:
- return 10;
- default:
- return null;
+ if (value == 0) {
+ return null;
+ } else if (value <= 3) {
+ return (value - 1) * 10;
+ } else if (value <= 6) {
+ return (value - 1) * 20;
+ } else if (value <= 100) {
+ return value;
+ } else if (value >= 0xF1 && value <= 0xF6) {
+ return value - 0xF0;
+ } else {
+ return null;
}
}
- private Position decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ private Position decodeBinary(ByteBuf buf, Channel channel, SocketAddress remoteAddress) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
+
+ boolean longId = buf.readableBytes() == 42;
buf.readByte(); // marker
- DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(5)));
+ String id;
+ if (longId) {
+ id = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ } else {
+ id = ByteBufUtil.hexDump(buf.readSlice(5));
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
@@ -149,18 +161,14 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.text("*")
.expression("..,") // manufacturer
- .number("(d+),") // imei
- .groupBegin()
- .text("VP1,")
- .or()
+ .number("(d+)?,") // imei
.groupBegin()
.text("V4,")
.expression("(.*),") // response
.or()
- .expression("V[^,]*,")
+ .expression("(V[^,]*),")
.groupEnd()
.number("(?:(dd)(dd)(dd))?,") // time (hhmmss)
- .groupEnd()
.groupBegin()
.expression("([ABV])?,") // validity
.or()
@@ -178,8 +186,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)(dd.d+),") // longitude
.groupEnd()
.expression("([EW]),")
- .number("(d+.?d*),") // speed
+ .number(" *(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
+ .number("(?:d+,)?") // battery
.number("(?:(dd)(dd)(dd))?") // date (ddmmyy)
.groupBegin()
.expression(",[^,]*,")
@@ -215,7 +224,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // mnc
.number("d+,") // gsm delay time
.number("d+,") // count
- .number("((?:d+,d+,d+,)+)") // cells
+ .number("((?:d+,d+,-?d+,)+)") // cells
.number("(dd)(dd)(dd),") // date (ddmmyy)
.number("(x{8})") // status
.any()
@@ -255,6 +264,43 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.text("#").optional()
.compile();
+ private static final Pattern PATTERN_VP1 = new PatternBuilder()
+ .text("*hq,")
+ .number("(d{15}),") // imei
+ .text("VP1,")
+ .groupBegin()
+ .text("V,")
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .expression("([^#]+)") // cells
+ .or()
+ .expression("[AB],") // validity
+ .number("(d+)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(d+)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .groupEnd()
+ .any()
+ .compile();
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String id, String type) {
+ if (channel != null && id != null) {
+ String response;
+ DateFormat dateFormat = new SimpleDateFormat(type.equals("R12") ? "HHmmss" : "yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String time = dateFormat.format(new Date());
+ if (type.equals("R12")) {
+ response = String.format("*HQ,%s,%s,%s#", id, type, time);
+ } else {
+ response = String.format("*HQ,%s,V4,%s,%s#", id, type, time);
+ }
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
private Position decodeText(String sentence, Channel channel, SocketAddress remoteAddress) {
Parser parser = new Parser(PATTERN, sentence);
@@ -262,19 +308,25 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ String id = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.hasNext()) {
position.set(Position.KEY_RESULT, parser.next());
}
+ if (parser.hasNext() && parser.next().equals("V1")) {
+ sendResponse(channel, remoteAddress, id, "V1");
+ } else if (Context.getConfig().getBoolean(getProtocolName() + ".ack")) {
+ sendResponse(channel, remoteAddress, id, "R12");
+ }
+
DateBuilder dateBuilder = new DateBuilder();
if (parser.hasNext(3)) {
dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
@@ -343,13 +395,15 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ String id = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ sendResponse(channel, remoteAddress, id, "NBR");
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -388,8 +442,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -422,8 +475,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -452,27 +504,85 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeVp1(String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_VP1, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (parser.hasNext(3)) {
+
+ getLastLocation(position, null);
+
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+
+ Network network = new Network();
+ for (String cell : parser.next().split("Y")) {
+ String[] values = cell.split(",");
+ network.addCellTower(CellTower.from(mcc, mnc,
+ Integer.parseInt(values[0]), Integer.parseInt(values[1]), Integer.parseInt(values[2])));
+ }
+
+ position.setNetwork(network);
+
+ } else {
+
+ position.setValid(true);
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ position.setTime(new DateBuilder()
+ .setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)).getDate());
+
+ }
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
String marker = buf.toString(0, 1, StandardCharsets.US_ASCII);
switch (marker) {
case "*":
- String sentence = buf.toString(StandardCharsets.US_ASCII);
+ String sentence = buf.toString(StandardCharsets.US_ASCII).trim();
int typeStart = sentence.indexOf(',', sentence.indexOf(',') + 1) + 1;
int typeEnd = sentence.indexOf(',', typeStart);
+ if (typeEnd < 0) {
+ typeEnd = sentence.indexOf('#', typeStart);
+ }
if (typeEnd > 0) {
String type = sentence.substring(typeStart, typeEnd);
switch (type) {
+ case "V0":
+ case "HTBT":
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(sentence, remoteAddress));
+ }
+ return null;
case "NBR":
return decodeLbs(sentence, channel, remoteAddress);
case "LINK":
return decodeLink(sentence, channel, remoteAddress);
case "V3":
return decodeV3(sentence, channel, remoteAddress);
+ case "VP1":
+ return decodeVp1(sentence, channel, remoteAddress);
default:
return decodeText(sentence, channel, remoteAddress);
}
diff --git a/src/org/traccar/protocol/H02ProtocolEncoder.java b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
index 7b5ff13bb..7a765332c 100644
--- a/src/org/traccar/protocol/H02ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com)
- * 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -16,20 +16,25 @@
*/
package org.traccar.protocol;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
+import org.traccar.Context;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import java.util.Date;
public class H02ProtocolEncoder extends StringProtocolEncoder {
private static final String MARKER = "HQ";
- private Object formatCommand(DateTime time, String uniqueId, String type, String... params) {
+ public H02ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private Object formatCommand(Date time, String uniqueId, String type, String... params) {
- StringBuilder result = new StringBuilder(String.format("*%s,%s,%s,%02d%02d%02d",
- MARKER, uniqueId, type, time.getHourOfDay(), time.getMinuteOfHour(), time.getSecondOfMinute()));
+ StringBuilder result = new StringBuilder(
+ String.format("*%s,%s,%s,%4$tH%4$tM%4$tS", MARKER, uniqueId, type, time));
for (String param : params) {
result.append(",").append(param);
@@ -40,7 +45,7 @@ public class H02ProtocolEncoder extends StringProtocolEncoder {
return result.toString();
}
- protected Object encodeCommand(Command command, DateTime time) {
+ protected Object encodeCommand(Command command, Date time) {
String uniqueId = getUniqueId(command.getDeviceId());
switch (command.getType()) {
@@ -49,24 +54,25 @@ public class H02ProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ALARM_DISARM:
return formatCommand(time, uniqueId, "SCF", "1", "1");
case Command.TYPE_ENGINE_STOP:
- return formatCommand(
- time, uniqueId, "S20", "1", "3", "10", "3", "5", "5", "3", "5", "3", "5", "3", "5");
+ return formatCommand(time, uniqueId, "S20", "1", "1");
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(time, uniqueId, "S20", "0", "0");
+ return formatCommand(time, uniqueId, "S20", "1", "0");
case Command.TYPE_POSITION_PERIODIC:
- return formatCommand(
- time, uniqueId, "S71", "22", command.getAttributes().get(Command.KEY_FREQUENCY).toString());
+ String frequency = command.getAttributes().get(Command.KEY_FREQUENCY).toString();
+ if (Context.getIdentityManager().lookupAttributeBoolean(
+ command.getDeviceId(), getProtocolName() + ".alternative", false, false, true)) {
+ return formatCommand(time, uniqueId, "D1", frequency);
+ } else {
+ return formatCommand(time, uniqueId, "S71", "22", frequency);
+ }
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
@Override
protected Object encodeCommand(Command command) {
- return encodeCommand(command, new DateTime(DateTimeZone.UTC));
+ return encodeCommand(command, new Date());
}
}
diff --git a/src/main/java/org/traccar/protocol/HaicomProtocol.java b/src/main/java/org/traccar/protocol/HaicomProtocol.java
new file mode 100644
index 000000000..6e5760bd4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HaicomProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class HaicomProtocol extends BaseProtocol {
+
+ public HaicomProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '*'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new HaicomProtocolDecoder(HaicomProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/HaicomProtocolDecoder.java b/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java
index 37898a9bc..dd20f2aeb 100644
--- a/src/org/traccar/protocol/HaicomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HaicomProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class HaicomProtocolDecoder extends BaseProtocolDecoder {
- public HaicomProtocolDecoder(HaicomProtocol protocol) {
+ public HaicomProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -62,8 +63,7 @@ public class HaicomProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocol.java b/src/main/java/org/traccar/protocol/HomtecsProtocol.java
new file mode 100644
index 000000000..34dbf0f51
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HomtecsProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class HomtecsProtocol extends BaseProtocol {
+
+ public HomtecsProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new HomtecsProtocolDecoder(HomtecsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/HomtecsProtocolDecoder.java b/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java
index 508de173c..a93572b5c 100644
--- a/src/org/traccar/protocol/HomtecsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HomtecsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class HomtecsProtocolDecoder extends BaseProtocolDecoder {
- public HomtecsProtocolDecoder(HomtecsProtocol protocol) {
+ public HomtecsProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -66,8 +67,7 @@ public class HomtecsProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.YMD_HMS));
diff --git a/src/org/traccar/protocol/HuaShengFrameDecoder.java b/src/main/java/org/traccar/protocol/HuaShengFrameDecoder.java
index 4c29b7915..bd52aa9e7 100644
--- a/src/org/traccar/protocol/HuaShengFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,19 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+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 HuaShengFrameDecoder extends FrameDecoder {
+public class HuaShengFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 2) {
return null;
@@ -35,7 +33,7 @@ public class HuaShengFrameDecoder extends FrameDecoder {
int index = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 0xC0);
if (index != -1) {
- ChannelBuffer result = ChannelBuffers.buffer(index + 1 - buf.readerIndex());
+ ByteBuf result = Unpooled.buffer(index + 1 - buf.readerIndex());
while (buf.readerIndex() <= index) {
int b = buf.readUnsignedByte();
diff --git a/src/org/traccar/protocol/HuaShengProtocol.java b/src/main/java/org/traccar/protocol/HuaShengProtocol.java
index e0fddae20..103f2d501 100644
--- a/src/org/traccar/protocol/HuaShengProtocol.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class HuaShengProtocol extends BaseProtocol {
public HuaShengProtocol() {
- super("huasheng");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new HuaShengFrameDecoder());
- pipeline.addLast("objectDecoder", new HuaShengProtocolDecoder(HuaShengProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HuaShengFrameDecoder());
+ pipeline.addLast(new HuaShengProtocolDecoder(HuaShengProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index a0a1eb0ab..920898039 100644
--- a/src/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,14 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -30,7 +34,7 @@ import java.nio.charset.StandardCharsets;
public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
- public HuaShengProtocolDecoder(HuaShengProtocol protocol) {
+ public HuaShengProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -38,19 +42,24 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_POSITION_RSP = 0xFF01;
public static final int MSG_LOGIN = 0xAA02;
public static final int MSG_LOGIN_RSP = 0xFF03;
+ public static final int MSG_HSO_REQ = 0x0002;
+ public static final int MSG_HSO_RSP = 0x0003;
- private static void sendResponse(Channel channel, int type, int index, ChannelBuffer content) {
+ private void sendResponse(Channel channel, int type, int index, ByteBuf content) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(0xC0);
response.writeShort(0x0100);
- response.writeShort(12 + content.readableBytes());
+ response.writeShort(12 + (content != null ? content.readableBytes() : 0));
response.writeShort(type);
response.writeShort(0);
response.writeInt(index);
- response.writeBytes(content);
+ if (content != null) {
+ response.writeBytes(content);
+ content.release();
+ }
response.writeByte(0xC0);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -58,7 +67,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(1); // start marker
buf.readUnsignedByte(); // flag
@@ -76,10 +85,10 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
int subtype = buf.readUnsignedShort();
int length = buf.readUnsignedShort() - 4;
if (subtype == 0x0003) {
- String imei = buf.readBytes(length).toString(StandardCharsets.US_ASCII);
+ String imei = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession != null && channel != null) {
- ChannelBuffer content = ChannelBuffers.dynamicBuffer();
+ ByteBuf content = Unpooled.buffer();
content.writeByte(0); // success
sendResponse(channel, MSG_LOGIN_RSP, index, content);
}
@@ -88,6 +97,10 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
}
}
+ } else if (type == MSG_HSO_REQ) {
+
+ sendResponse(channel, MSG_HSO_RSP, index, null);
+
} else if (type == MSG_POSITION) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
@@ -95,8 +108,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
int status = buf.readUnsignedShort();
@@ -107,7 +119,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_IGNITION, BitUtil.check(status, 14));
position.set(Position.KEY_EVENT, buf.readUnsignedShort());
- String time = buf.readBytes(12).toString(StandardCharsets.US_ASCII);
+ String time = buf.readCharSequence(12, StandardCharsets.US_ASCII).toString();
DateBuilder dateBuilder = new DateBuilder()
.setYear(Integer.parseInt(time.substring(0, 2)))
@@ -128,12 +140,48 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedShort() * 1000);
while (buf.readableBytes() > 4) {
- buf.readUnsignedShort(); // subtype
+ int subtype = buf.readUnsignedShort();
int length = buf.readUnsignedShort() - 4;
- buf.skipBytes(length);
+ switch (subtype) {
+ case 0x0001:
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ position.set("averageSpeed", buf.readUnsignedByte());
+ buf.readUnsignedShort(); // interval fuel consumption
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShort());
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4);
+ buf.readUnsignedInt(); // trip id
+ break;
+ case 0x0005:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte());
+ buf.readUnsignedInt(); // run time
+ break;
+ case 0x0009:
+ position.set(
+ Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
+ break;
+ case 0x0020:
+ Network network = new Network();
+ String[] cells = buf.readCharSequence(
+ length, StandardCharsets.US_ASCII).toString().split("\\+");
+ for (String cell : cells) {
+ String[] values = cell.split("@");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[0]), Integer.parseInt(values[1]),
+ Integer.parseInt(values[2], 16), Integer.parseInt(values[3], 16)));
+ }
+ position.setNetwork(network);
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
}
- sendResponse(channel, MSG_POSITION_RSP, index, ChannelBuffers.dynamicBuffer());
+ sendResponse(channel, MSG_POSITION_RSP, index, null);
return position;
diff --git a/src/org/traccar/protocol/HuabaoFrameDecoder.java b/src/main/java/org/traccar/protocol/HuabaoFrameDecoder.java
index 8e9c9fe72..b520f6be9 100644
--- a/src/org/traccar/protocol/HuabaoFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,17 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+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 HuabaoFrameDecoder extends FrameDecoder {
+public class HuabaoFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 2) {
return null;
@@ -33,7 +33,7 @@ public class HuabaoFrameDecoder extends FrameDecoder {
int index = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 0x7e);
if (index != -1) {
- ChannelBuffer result = ChannelBuffers.buffer(index + 1 - buf.readerIndex());
+ ByteBuf result = Unpooled.buffer(index + 1 - buf.readerIndex());
while (buf.readerIndex() <= index) {
int b = buf.readUnsignedByte();
diff --git a/src/org/traccar/protocol/HuabaoProtocol.java b/src/main/java/org/traccar/protocol/HuabaoProtocol.java
index c74cb58c7..791672b85 100644
--- a/src/org/traccar/protocol/HuabaoProtocol.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,31 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class HuabaoProtocol extends BaseProtocol {
public HuabaoProtocol() {
- super("huabao");
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new HuabaoFrameDecoder());
- pipeline.addLast("objectEncoder", new HuabaoProtocolEncoder());
- pipeline.addLast("objectDecoder", new HuabaoProtocolDecoder(HuabaoProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..bf5b6d89a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -0,0 +1,299 @@
+/*
+ * 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+
+public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
+
+ public HuabaoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_GENERAL_RESPONSE = 0x8001;
+ 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 = 0x0704;
+ public static final int MSG_OIL_CONTROL = 0XA006;
+
+ public static final int RESULT_SUCCESS = 0;
+
+ public static ByteBuf formatMessage(int type, ByteBuf id, ByteBuf data) {
+ ByteBuf buf = Unpooled.buffer();
+ buf.writeByte(0x7e);
+ buf.writeShort(type);
+ buf.writeShort(data.readableBytes());
+ buf.writeBytes(id);
+ buf.writeShort(0); // index
+ buf.writeBytes(data);
+ data.release();
+ buf.writeByte(Checksum.xor(buf.nioBuffer(1, buf.readableBytes() - 1)));
+ buf.writeByte(0x7e);
+ return buf;
+ }
+
+ private void sendGeneralResponse(
+ Channel channel, SocketAddress remoteAddress, ByteBuf id, int type, int index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(index);
+ response.writeShort(type);
+ response.writeByte(RESULT_SUCCESS);
+ channel.writeAndFlush(new NetworkMessage(
+ formatMessage(MSG_GENERAL_RESPONSE, id, response), remoteAddress));
+ }
+ }
+
+ private String decodeAlarm(long value) {
+ if (BitUtil.check(value, 0)) {
+ return Position.ALARM_SOS;
+ }
+ if (BitUtil.check(value, 1)) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if (BitUtil.check(value, 5)) {
+ return Position.ALARM_GPS_ANTENNA_CUT;
+ }
+ if (BitUtil.check(value, 4) || BitUtil.check(value, 9)
+ || BitUtil.check(value, 10) || BitUtil.check(value, 11)) {
+ return Position.ALARM_FAULT;
+ }
+ if (BitUtil.check(value, 8)) {
+ return Position.ALARM_POWER_OFF;
+ }
+ if (BitUtil.check(value, 20)) {
+ return Position.ALARM_GEOFENCE;
+ }
+ if (BitUtil.check(value, 28)) {
+ return Position.ALARM_MOVEMENT;
+ }
+ if (BitUtil.check(value, 29)) {
+ return Position.ALARM_ACCIDENT;
+ }
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // start marker
+ int type = buf.readUnsignedShort();
+ buf.readUnsignedShort(); // body length
+ ByteBuf id = buf.readSlice(6); // phone number
+ int index = buf.readUnsignedShort();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(id));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (deviceSession.getTimeZone() == null) {
+ deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId(), "GMT+8"));
+ }
+
+ if (type == MSG_TERMINAL_REGISTER) {
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(index);
+ response.writeByte(RESULT_SUCCESS);
+ response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(
+ formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress));
+ }
+
+ } else if (type == MSG_TERMINAL_AUTH) {
+
+ sendGeneralResponse(channel, remoteAddress, id, type, index);
+
+ } else if (type == MSG_LOCATION_REPORT) {
+
+ return decodeLocation(deviceSession, buf);
+
+ } else if (type == MSG_LOCATION_BATCH) {
+
+ return decodeLocationBatch(deviceSession, buf);
+
+ }
+
+ return null;
+ }
+
+ private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
+
+ int status = buf.readInt();
+
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10));
+
+ position.setValid(BitUtil.check(status, 1));
+
+ double lat = buf.readUnsignedInt() * 0.000001;
+ double lon = buf.readUnsignedInt() * 0.000001;
+
+ if (BitUtil.check(status, 2)) {
+ position.setLatitude(-lat);
+ } else {
+ position.setLatitude(lat);
+ }
+
+ if (BitUtil.check(status, 3)) {
+ position.setLongitude(-lon);
+ } else {
+ position.setLongitude(lon);
+ }
+
+ 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());
+
+ while (buf.readableBytes() > 2) {
+ int subtype = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+ int endIndex = buf.readerIndex() + length;
+ switch (subtype) {
+ case 0x01:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ break;
+ case 0x02:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x30:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ break;
+ case 0x31:
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x33:
+ String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ if (sentence.startsWith("*M00")) {
+ String lockStatus = sentence.substring(8, 8 + 7);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01);
+ }
+ break;
+ case 0x91:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.1);
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedByte() * 100 / 255);
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte() * 100 / 255);
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
+ buf.readUnsignedShort();
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01);
+ buf.readUnsignedShort();
+ buf.readUnsignedInt();
+ buf.readUnsignedShort();
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x94:
+ if (length > 0) {
+ position.set(
+ Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
+ }
+ break;
+ case 0xD0:
+ long userStatus = buf.readUnsignedInt();
+ if (BitUtil.check(userStatus, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ }
+ break;
+ case 0xD3:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0xEB:
+ while (buf.readerIndex() < endIndex) {
+ int tenetLength = buf.readUnsignedShort();
+ int tenetType = buf.readUnsignedShort();
+ switch (tenetType) {
+ case 0x0001:
+ position.set("fuel1", buf.readUnsignedShort() * 0.1);
+ buf.readUnsignedByte(); // unused
+ break;
+ case 0x0023:
+ buf.skipBytes(4); // unused
+ position.set("fuel2", Double.parseDouble(
+ buf.readCharSequence(2, StandardCharsets.US_ASCII).toString()));
+ break;
+ default:
+ buf.skipBytes(tenetLength - 2);
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ buf.readerIndex(endIndex);
+ }
+
+ return position;
+ }
+
+ private List<Position> decodeLocationBatch(DeviceSession deviceSession, ByteBuf buf) {
+
+ List<Position> positions = new LinkedList<>();
+
+ int count = buf.readUnsignedShort();
+ buf.readUnsignedByte(); // location type
+
+ for (int i = 0; i < count; i++) {
+ int endIndex = buf.readUnsignedShort() + buf.readerIndex();
+ positions.add(decodeLocation(deviceSession, buf));
+ buf.readerIndex(endIndex);
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
new file mode 100644
index 000000000..40d07230d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.Context;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class HuabaoProtocolEncoder extends BaseProtocolEncoder {
+
+ public HuabaoProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
+ command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+
+ ByteBuf id = Unpooled.wrappedBuffer(
+ DataConverter.parseHex(getUniqueId(command.getDeviceId())));
+ try {
+ ByteBuf data = Unpooled.buffer();
+ byte[] time = DataConverter.parseHex(new SimpleDateFormat("yyMMddHHmmss").format(new Date()));
+
+ switch (command.getType()) {
+ case Command.TYPE_ENGINE_STOP:
+ if (alternative) {
+ data.writeByte(0x01);
+ data.writeBytes(time);
+ return HuabaoProtocolDecoder.formatMessage(
+ HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data);
+ } else {
+ data.writeByte(0xf0);
+ return HuabaoProtocolDecoder.formatMessage(
+ HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data);
+ }
+ case Command.TYPE_ENGINE_RESUME:
+ if (alternative) {
+ data.writeByte(0x00);
+ data.writeBytes(time);
+ return HuabaoProtocolDecoder.formatMessage(
+ HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data);
+ } else {
+ data.writeByte(0xf1);
+ return HuabaoProtocolDecoder.formatMessage(
+ HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data);
+ }
+ default:
+ return null;
+ }
+ } finally {
+ id.release();
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/HunterProProtocol.java b/src/main/java/org/traccar/protocol/HunterProProtocol.java
new file mode 100644
index 000000000..9f6424a57
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HunterProProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class HunterProProtocol extends BaseProtocol {
+
+ public HunterProProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "\r"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new HunterProProtocolDecoder(HunterProProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/HunterProProtocolDecoder.java b/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java
index b1e3f84a2..06bc12d59 100644
--- a/src/org/traccar/protocol/HunterProProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HunterProProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class HunterProProtocolDecoder extends BaseProtocolDecoder {
- public HunterProProtocolDecoder(HunterProProtocol protocol) {
+ public HunterProProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -56,8 +57,7 @@ public class HunterProProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/IdplProtocol.java b/src/main/java/org/traccar/protocol/IdplProtocol.java
new file mode 100644
index 000000000..418178756
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/IdplProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class IdplProtocol extends BaseProtocol {
+
+ public IdplProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new IdplProtocolDecoder(IdplProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/IdplProtocolDecoder.java b/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java
index e56a0f022..cf3c03d7f 100644
--- a/src/org/traccar/protocol/IdplProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IdplProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -18,7 +18,7 @@ package org.traccar.protocol;
import java.net.SocketAddress;
import java.util.regex.Pattern;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
@@ -69,8 +69,7 @@ public class IdplProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_TYPE, parser.nextInt(0));
diff --git a/src/org/traccar/protocol/IntellitracFrameDecoder.java b/src/main/java/org/traccar/protocol/IntellitracFrameDecoder.java
index 49f8b4a5d..8322e65ba 100644
--- a/src/org/traccar/protocol/IntellitracFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/IntellitracFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,10 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import org.traccar.NetworkMessage;
public class IntellitracFrameDecoder extends LineBasedFrameDecoder {
@@ -31,7 +31,7 @@ public class IntellitracFrameDecoder extends LineBasedFrameDecoder {
// example of sync header: 0xFA 0xF8 0x1B 0x01 0x81 0x60 0x33 0x3C
@Override
- protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ protected Object decode(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
// Check minimum length
if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
@@ -40,13 +40,13 @@ public class IntellitracFrameDecoder extends LineBasedFrameDecoder {
// Check for sync packet
if (buf.getUnsignedShort(buf.readerIndex()) == 0xFAF8) {
- ChannelBuffer syncMessage = buf.readBytes(8);
- if (channel != null) {
- channel.write(syncMessage);
+ ByteBuf syncMessage = buf.readRetainedSlice(8);
+ if (ctx != null && ctx.channel() != null) {
+ ctx.channel().writeAndFlush(new NetworkMessage(syncMessage, ctx.channel().remoteAddress()));
}
}
- return super.decode(ctx, channel, buf);
+ return super.decode(ctx, buf);
}
}
diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocol.java b/src/main/java/org/traccar/protocol/IntellitracProtocol.java
new file mode 100644
index 000000000..3abf40da7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/IntellitracProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class IntellitracProtocol extends BaseProtocol {
+
+ public IntellitracProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new IntellitracFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new IntellitracProtocolDecoder(IntellitracProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
index 8f4e4c0d6..897606270 100644
--- a/src/org/traccar/protocol/IntellitracProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.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;
@@ -27,7 +29,7 @@ import java.util.regex.Pattern;
public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
- public IntellitracProtocolDecoder(IntellitracProtocol protocol) {
+ public IntellitracProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -72,8 +74,7 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -83,34 +84,32 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
position.setTime(parser.nextDateTime());
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
+ position.setValid(true);
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
- int satellites = parser.nextInt(0);
- position.setValid(satellites >= 3);
- position.set(Position.KEY_SATELLITES, satellites);
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_INDEX, parser.nextLong());
+ position.set(Position.KEY_INPUT, parser.nextInt());
+ position.set(Position.KEY_OUTPUT, parser.nextInt());
- position.set(Position.KEY_INDEX, parser.nextLong(0));
- position.set(Position.KEY_INPUT, parser.nextInt(0));
- position.set(Position.KEY_OUTPUT, parser.nextInt(0));
-
- position.set(Position.PREFIX_ADC + 1, parser.nextDouble(0));
- position.set(Position.PREFIX_ADC + 2, parser.nextDouble(0));
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
// J1939 data
- position.set(Position.KEY_OBD_SPEED, parser.nextInt(0));
- position.set(Position.KEY_RPM, parser.nextInt(0));
- position.set("coolant", parser.nextInt(0));
- position.set(Position.KEY_FUEL_LEVEL, parser.nextInt(0));
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt(0));
- position.set(Position.PREFIX_TEMP + 1, parser.nextInt(0));
- position.set("chargerPressure", parser.nextInt(0));
- position.set("tpl", parser.nextInt(0));
- position.set("axle", parser.nextInt(0));
- position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0));
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set("coolant", parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set("chargerPressure", parser.nextInt());
+ position.set("tpl", parser.nextInt());
+ position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt());
+ position.set(Position.KEY_OBD_ODOMETER, parser.nextInt());
return position;
}
diff --git a/src/main/java/org/traccar/protocol/ItsFrameDecoder.java b/src/main/java/org/traccar/protocol/ItsFrameDecoder.java
new file mode 100644
index 000000000..cebfdca5f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ItsFrameDecoder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
+
+public class ItsFrameDecoder extends BaseFrameDecoder {
+
+ private static final int MINIMUM_LENGTH = 20;
+
+ private ByteBuf readFrame(ByteBuf buf, int delimiterIndex, int skip) {
+ int headerIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) '$');
+ if (headerIndex > 0 && headerIndex < delimiterIndex) {
+ return buf.readRetainedSlice(headerIndex - buf.readerIndex());
+ } else {
+ ByteBuf frame = buf.readRetainedSlice(delimiterIndex - buf.readerIndex());
+ buf.skipBytes(skip);
+ return frame;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ while (buf.isReadable() && buf.getByte(buf.readerIndex()) != '$') {
+ buf.skipBytes(1);
+ }
+
+ int delimiterIndex = BufferUtil.indexOf("\r\n", buf);
+ if (delimiterIndex > MINIMUM_LENGTH) {
+ return readFrame(buf, delimiterIndex, 2);
+ } else {
+ delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*');
+ if (delimiterIndex > MINIMUM_LENGTH) {
+ if (buf.writerIndex() > delimiterIndex + 1 && buf.getByte(delimiterIndex + 1) == '*') {
+ delimiterIndex += 1;
+ }
+ if (buf.getByte(delimiterIndex - 2) == ',') {
+ return readFrame(buf, delimiterIndex - 1, 2); // skip binary checksum
+ } else {
+ return readFrame(buf, delimiterIndex, 1);
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocol.java b/src/main/java/org/traccar/protocol/ItsProtocol.java
new file mode 100644
index 000000000..45df3da11
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ItsProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 - 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 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.model.Command;
+
+public class ItsProtocol extends BaseProtocol {
+
+ public ItsProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new ItsFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new ItsProtocolDecoder(ItsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
new file mode 100644
index 000000000..e8d77f1a8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2018 - 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.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class ItsProtocolDecoder extends BaseProtocolDecoder {
+
+ public ItsProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("[^$]*")
+ .text("$")
+ .expression(",?[^,]+,") // event
+ .groupBegin()
+ .expression("[^,]*,") // vendor
+ .expression("[^,]+,") // firmware version
+ .expression("(..),") // status
+ .number("(d+),").optional() // event
+ .expression("([LH]),") // history
+ .or()
+ .expression("([^,]+),") // type
+ .groupEnd()
+ .number("(d{15}),") // imei
+ .groupBegin()
+ .expression("(..),") // status
+ .or()
+ .expression("[^,]*,") // vehicle registration
+ .number("([01]),").optional() // valid
+ .groupEnd()
+ .number("(dd),?(dd),?(d{2,4}),") // date (ddmmyyyy)
+ .number("(dd),?(dd),?(dd),") // time (hhmmss)
+ .expression("([01AV]),").optional() // valid
+ .number("(d+.d+),([NS]),") // latitude
+ .number("(d+.d+),([EW]),") // longitude
+ .groupBegin()
+ .number("(d+.?d*),") // speed
+ .number("(d+.?d*),") // course
+ .number("(d+),") // satellites
+ .groupBegin()
+ .number("(d+.?d*),") // altitude
+ .number("d+.?d*,") // pdop
+ .number("d+.?d*,") // hdop
+ .expression("[^,]*,") // operator
+ .number("([01]),") // ignition
+ .number("([01]),") // charging
+ .number("(d+.?d*),") // power
+ .number("(d+.?d*),") // battery
+ .number("([01]),") // emergency
+ .expression("[CO]?,") // tamper
+ .expression("(.*),") // cells
+ .number("([012]{4}),") // inputs
+ .number("([01]{2}),") // outputs
+ .groupBegin()
+ .number("d+,") // index
+ .number("(d+.?d*),") // adc1
+ .number("(d+.?d*),") // adc2
+ .groupEnd("?")
+ .groupEnd("?")
+ .or()
+ .number("(-?d+.d+),") // altitude
+ .number("(d+.d+),") // speed
+ .groupEnd()
+ .any()
+ .compile();
+
+ private String decodeAlarm(String status) {
+ switch (status) {
+ case "WD":
+ case "EA":
+ return Position.ALARM_SOS;
+ case "BL":
+ return Position.ALARM_LOW_BATTERY;
+ case "HB":
+ return Position.ALARM_BRAKING;
+ case "HA":
+ return Position.ALARM_ACCELERATION;
+ case "RT":
+ return Position.ALARM_CORNERING;
+ case "OS":
+ return Position.ALARM_OVERSPEED;
+ case "TA":
+ return Position.ALARM_TAMPERING;
+ case "BD":
+ return Position.ALARM_POWER_CUT;
+ case "BR":
+ return Position.ALARM_POWER_RESTORED;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (channel != null && sentence.startsWith("$,01,")) {
+ channel.writeAndFlush(new NetworkMessage("$,1,*", remoteAddress));
+ }
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String status = parser.next();
+ Integer event = parser.nextInt();
+ boolean history = "H".equals(parser.next());
+ String type = parser.next();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (type != null && type.equals("EMR")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ if (event != null) {
+ position.set(Position.KEY_EVENT, event);
+ }
+ if (history) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (parser.hasNext()) {
+ status = parser.next();
+ }
+ if (status != null) {
+ if (status.equals("IN")) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (status.equals("IF")) {
+ position.set(Position.KEY_IGNITION, false);
+ } else {
+ position.set(Position.KEY_ALARM, decodeAlarm(status));
+ }
+ }
+
+ if (parser.hasNext()) {
+ position.setValid(parser.nextInt() == 1);
+ }
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ if (parser.hasNext()) {
+ position.setValid(parser.next().matches("[1A]"));
+ }
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+
+ if (parser.hasNext(3)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ }
+
+ if (parser.hasNext(8)) {
+ position.setAltitude(parser.nextDouble());
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+ position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ position.set("emergency", parser.nextInt() > 0);
+
+ String cellsString = parser.next();
+ if (!cellsString.contains("x")) {
+ String[] cells = cellsString.split(",");
+ int mcc = Integer.parseInt(cells[1]);
+ int mnc = Integer.parseInt(cells[2]);
+ int lac = Integer.parseInt(cells[3], 16);
+ int cid = Integer.parseInt(cells[4], 16);
+ Network network = new Network(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(cells[0])));
+ if (!cells[5].startsWith("(")) {
+ for (int i = 0; i < 4; i++) {
+ lac = Integer.parseInt(cells[5 + 3 * i + 1], 16);
+ cid = Integer.parseInt(cells[5 + 3 * i + 2], 16);
+ if (lac > 0 && cid > 0) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid));
+ }
+ }
+ }
+ position.setNetwork(network);
+ }
+
+ String input = parser.next();
+ if (input.charAt(input.length() - 1) == '2') {
+ input = input.substring(0, input.length() - 1) + '0';
+ }
+ position.set(Position.KEY_INPUT, Integer.parseInt(input, 2));
+ position.set(Position.KEY_OUTPUT, parser.nextBinInt());
+ }
+
+ if (parser.hasNext(2)) {
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
+ }
+
+ if (parser.hasNext(2)) {
+ position.setAltitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolEncoder.java b/src/main/java/org/traccar/protocol/ItsProtocolEncoder.java
new file mode 100644
index 000000000..f82b8e3ac
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ItsProtocolEncoder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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 ItsProtocolEncoder extends StringProtocolEncoder {
+
+ public ItsProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_ENGINE_STOP:
+ return "@SET#RLP,OP1,";
+ case Command.TYPE_ENGINE_RESUME:
+ return "@CLR#RLP,OP1,";
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Ivt401Protocol.java b/src/main/java/org/traccar/protocol/Ivt401Protocol.java
new file mode 100644
index 000000000..fb44e4fe9
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Ivt401Protocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Ivt401Protocol extends BaseProtocol {
+
+ public Ivt401Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..63556e7a9
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Ivt401ProtocolDecoder.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.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 Ivt401ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Ivt401ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("(")
+ .expression("TL[ABLN],") // header
+ .number("(d+),") // imei
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("([-+]d+.d+),") // latitude
+ .number("([-+]d+.d+),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(-?d+.?d*),") // altitude
+ .number("d+,") // satellites or battery status
+ .number("(d),") // gps status
+ .number("(d+),") // rssi
+ .number("(d+),") // input
+ .number("(d+),") // output
+ .number("(d+.d+),") // adc
+ .number("(d+.d+),") // power
+ .number("(d+.d+),") // battery
+ .number("(-?d+.?d*),") // pcb temp
+ .expression("([^,]+),") // temp
+ .number("(d+),") // movement
+ .number("(d+.d+),") // acceleration
+ .number("(-?d+),") // tilt
+ .number("(d+),") // trip
+ .number("(d+),") // odometer
+ .groupBegin()
+ .number("([01]),") // overspeed
+ .number("[01],") // input 2 misuse
+ .number("[01],") // immobilizer
+ .number("[01],") // temperature alert
+ .number("[0-2]+,") // geofence
+ .number("([0-3]),") // harsh driving
+ .number("[01],") // reconnect
+ .number("([01]),") // low battery
+ .number("([01]),") // power disconnected
+ .number("[01],") // gps failure
+ .number("([01]),") // towing
+ .number("[01],") // server unreachable
+ .number("[128],") // sleep mode
+ .expression("([^,]+)?,") // driver id
+ .number("d+,") // sms count
+ .groupEnd("?")
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextDouble());
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ String input = parser.next();
+ for (int i = 0; i < input.length(); i++) {
+ int value = Character.getNumericValue(input.charAt(i));
+ if (value < 2) {
+ position.set(Position.PREFIX_IN + (i + 1), value > 0);
+ }
+ }
+
+ String output = parser.next();
+ for (int i = 0; i < output.length(); i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), Character.getNumericValue(output.charAt(i)) > 0);
+ }
+
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_DEVICE_TEMP, parser.nextDouble());
+
+ String temp = parser.next();
+ if (temp.startsWith("M")) {
+ int index = 1;
+ int startIndex = 1;
+ int endIndex;
+ while (startIndex < temp.length()) {
+ endIndex = temp.indexOf('-', startIndex + 1);
+ if (endIndex < 0) {
+ endIndex = temp.indexOf('+', startIndex + 1);
+ }
+ if (endIndex < 0) {
+ endIndex = temp.length();
+ }
+ if (endIndex > 0) {
+ double value = Double.parseDouble(temp.substring(startIndex, endIndex));
+ position.set(Position.PREFIX_TEMP + index++, value);
+ }
+ startIndex = endIndex;
+ }
+ } else {
+ position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(temp));
+ }
+
+ position.set(Position.KEY_MOTION, parser.nextInt() > 0);
+ position.set(Position.KEY_ACCELERATION, parser.nextDouble());
+
+ parser.nextInt(); // tilt
+ parser.nextInt(); // trip state
+
+ position.set(Position.KEY_ODOMETER, parser.nextLong());
+
+ if (parser.hasNext(6)) {
+ position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_OVERSPEED : null);
+ switch (parser.nextInt()) {
+ 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;
+ }
+ position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_LOW_BATTERY : null);
+ position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_POWER_CUT : null);
+ position.set(Position.KEY_ALARM, parser.nextInt() == 1 ? Position.ALARM_TOW : null);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/JpKorjarFrameDecoder.java b/src/main/java/org/traccar/protocol/JpKorjarFrameDecoder.java
index 33a1b3f36..0eb65c8ef 100644
--- a/src/org/traccar/protocol/JpKorjarFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/JpKorjarFrameDecoder.java
@@ -1,5 +1,6 @@
/*
* 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.
@@ -15,16 +16,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class JpKorjarFrameDecoder extends FrameDecoder {
+public class JpKorjarFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 80) {
return null;
@@ -40,7 +41,7 @@ public class JpKorjarFrameDecoder extends FrameDecoder {
return null;
}
- return buf.readBytes(endIndex + 1);
+ return buf.readRetainedSlice(endIndex + 1);
}
}
diff --git a/src/org/traccar/protocol/JpKorjarProtocol.java b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
index c54994708..fe5b2480d 100644
--- a/src/org/traccar/protocol/JpKorjarProtocol.java
+++ b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java
@@ -1,5 +1,6 @@
/*
* 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.
@@ -15,29 +16,20 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class JpKorjarProtocol extends BaseProtocol {
public JpKorjarProtocol() {
- super("jpkorjar");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
+ addServer(new TrackerServer(false, this.getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
-
- pipeline.addLast("frameDecoder", new JpKorjarFrameDecoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new JpKorjarProtocolDecoder(JpKorjarProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new JpKorjarFrameDecoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new JpKorjarProtocolDecoder(JpKorjarProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/JpKorjarProtocolDecoder.java b/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java
index 654b3e3d0..33026918a 100644
--- a/src/org/traccar/protocol/JpKorjarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/JpKorjarProtocolDecoder.java
@@ -1,89 +1,89 @@
-/*
- * Copyright 2016 Nyash (nyashh@gmail.com)
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.util.regex.Pattern;
-
-public class JpKorjarProtocolDecoder extends BaseProtocolDecoder {
-
- public JpKorjarProtocolDecoder(JpKorjarProtocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("KORJAR.PL,")
- .number("(d+),") // imei
- .number("(dd)(dd)(dd)") // date (yymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(d+.d+)([NS]),") // latitude
- .number("(d+.d+)([EW]),") // longitude
- .number("(d+.d+),") // speed
- .number("(d+),") // course
- .number("[FL]:(d+.d+)V,") // battery
- .number("([01]) ") // valid
- .number("(d+) ") // mcc
- .number("(d+) ") // mnc
- .number("(x+) ") // lac
- .number("(x+),") // cid
- .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();
- position.setProtocol(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.setTime(parser.nextDateTime());
-
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
-
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
-
- position.setValid(parser.nextInt(0) == 1);
-
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
-
- return position;
- }
-
-}
+/*
+ * Copyright 2016 Nyash (nyashh@gmail.com)
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class JpKorjarProtocolDecoder extends BaseProtocolDecoder {
+
+ public JpKorjarProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("KORJAR.PL,")
+ .number("(d+),") // imei
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+.d+)([NS]),") // latitude
+ .number("(d+.d+)([EW]),") // longitude
+ .number("(d+.d+),") // speed
+ .number("(d+),") // course
+ .number("[FL]:(d+.d+)V,") // battery
+ .number("([01]) ") // valid
+ .number("(d+) ") // mcc
+ .number("(d+) ") // mnc
+ .number("(x+) ") // lac
+ .number("(x+),") // cid
+ .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());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime());
+
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble(0));
+
+ position.setValid(parser.nextInt(0) == 1);
+
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Jt600FrameDecoder.java b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
index 5606ae1fc..bfefb94a7 100644
--- a/src/org/traccar/protocol/Jt600FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,18 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
import java.text.ParseException;
-public class Jt600FrameDecoder extends FrameDecoder {
+public class Jt600FrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 10) {
return null;
@@ -35,15 +35,15 @@ public class Jt600FrameDecoder extends FrameDecoder {
char type = (char) buf.getByte(buf.readerIndex());
if (type == '$') {
- boolean longFormat = buf.getUnsignedByte(buf.readerIndex() + 1) == 0x75;
+ boolean longFormat = Jt600ProtocolDecoder.isLongFormat(buf, buf.readerIndex() + 1);
int length = buf.getUnsignedShort(buf.readerIndex() + (longFormat ? 8 : 7)) + 10;
- if (length >= buf.readableBytes()) {
- return buf.readBytes(length);
+ if (length <= buf.readableBytes()) {
+ return buf.readRetainedSlice(length);
}
} else if (type == '(') {
int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ')');
if (endIndex != -1) {
- return buf.readBytes(endIndex + 1);
+ return buf.readRetainedSlice(endIndex + 1);
}
} else {
throw new ParseException(null, 0); // unknown message
diff --git a/src/org/traccar/protocol/Jt600Protocol.java b/src/main/java/org/traccar/protocol/Jt600Protocol.java
index 8c71ca4f6..37c82f741 100644
--- a/src/org/traccar/protocol/Jt600Protocol.java
+++ b/src/main/java/org/traccar/protocol/Jt600Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,35 +15,27 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class Jt600Protocol extends BaseProtocol {
public Jt600Protocol() {
- super("jt600");
setSupportedDataCommands(
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ENGINE_STOP,
Command.TYPE_SET_TIMEZONE,
Command.TYPE_REBOOT_DEVICE);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Jt600FrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new Jt600ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Jt600ProtocolDecoder(Jt600Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Jt600FrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Jt600ProtocolEncoder(Jt600Protocol.this));
+ pipeline.addLast(new Jt600ProtocolDecoder(Jt600Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index f76fd8069..f456cd1ef 100644
--- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,12 +15,15 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitBuffer;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -38,7 +41,7 @@ import java.util.regex.Pattern;
public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
- public Jt600ProtocolDecoder(Jt600Protocol protocol) {
+ public Jt600ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -48,22 +51,62 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes / 60;
}
- private List<Position> decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ private void decodeStatus(Position position, ByteBuf buf) {
+
+ int value = buf.readUnsignedByte();
+
+ position.set(Position.KEY_IGNITION, BitUtil.check(value, 0));
+ position.set(Position.KEY_DOOR, BitUtil.check(value, 6));
+
+ value = buf.readUnsignedByte();
+
+ position.set(Position.KEY_CHARGE, BitUtil.check(value, 0));
+ position.set(Position.KEY_BLOCKED, BitUtil.check(value, 1));
+
+ if (BitUtil.check(value, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ if (BitUtil.check(value, 3) || BitUtil.check(value, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GPS_ANTENNA_CUT);
+ }
+ if (BitUtil.check(value, 4)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+
+ value = buf.readUnsignedByte();
+
+ if (BitUtil.check(value, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING);
+ }
+ if (BitUtil.check(value, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ }
+
+ buf.readUnsignedByte(); // reserved
+
+ }
+
+ static boolean isLongFormat(ByteBuf buf, int flagIndex) {
+ return buf.getUnsignedByte(flagIndex) >> 4 == 0x7;
+ }
+
+ private List<Position> decodeBinary(ByteBuf buf, Channel channel, SocketAddress remoteAddress) {
List<Position> positions = new LinkedList<>();
buf.readByte(); // header
- boolean longFormat = buf.getUnsignedByte(buf.readerIndex()) == 0x75;
+ boolean longFormat = isLongFormat(buf, buf.readerIndex());
- String id = String.valueOf(Long.parseLong(ChannelBuffers.hexDump(buf.readBytes(5))));
+ String id = String.valueOf(Long.parseLong(ByteBufUtil.hexDump(buf.readSlice(5))));
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
+ int protocolVersion = 0;
if (longFormat) {
- buf.readUnsignedByte(); // protocol
+ protocolVersion = buf.readUnsignedByte();
}
int version = BitUtil.from(buf.readUnsignedByte(), 4);
@@ -71,8 +114,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
while (buf.readableBytes() > 1) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -108,7 +150,15 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedInt(); // vehicle id combined
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+ int status = buf.readUnsignedShort();
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 1) ? Position.ALARM_GEOFENCE_ENTER : null);
+ 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);
+ 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);
+ position.set(Position.KEY_STATUS, status);
int battery = buf.readUnsignedByte();
if (battery == 0xff) {
@@ -121,6 +171,12 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
cellTower.setSignalStrength((int) buf.readUnsignedByte());
position.setNetwork(new Network(cellTower));
+ if (protocolVersion == 0x17) {
+ buf.readUnsignedByte(); // geofence id
+ buf.skipBytes(3); // reserved
+ buf.skipBytes(buf.readableBytes() - 1);
+ }
+
} else if (version == 1) {
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
@@ -146,12 +202,25 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
int fuel = buf.readUnsignedByte() << 8;
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
+ decodeStatus(position, buf);
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
fuel += buf.readUnsignedByte();
position.set(Position.KEY_FUEL_LEVEL, fuel);
+ } else if (version == 3) {
+
+ BitBuffer bitBuffer = new BitBuffer(buf);
+
+ position.set("fuel1", bitBuffer.readUnsigned(12));
+ position.set("fuel2", bitBuffer.readUnsigned(12));
+ position.set("fuel3", bitBuffer.readUnsigned(12));
+ position.set(Position.KEY_ODOMETER, bitBuffer.readUnsigned(20) * 1000);
+
+ int status = bitBuffer.readUnsigned(24);
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_STATUS, status);
+
}
positions.add(position);
@@ -195,8 +264,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setLongitude(parser.nextCoordinate());
@@ -254,8 +322,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
String type = parser.next();
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
@@ -280,9 +347,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
if (channel != null) {
if (type.equals("U01") || type.equals("U02") || type.equals("U03")) {
- channel.write("(S39)");
+ channel.writeAndFlush(new NetworkMessage("(S39)", remoteAddress));
} else if (type.equals("U06")) {
- channel.write("(S20)");
+ channel.writeAndFlush(new NetworkMessage("(S20)", remoteAddress));
}
}
@@ -293,7 +360,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
char first = (char) buf.getByte(0);
if (first == '$') {
diff --git a/src/org/traccar/protocol/Jt600ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolEncoder.java
index 377f104a3..199467a38 100644
--- a/src/org/traccar/protocol/Jt600ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -18,10 +18,15 @@ package org.traccar.protocol;
import java.util.TimeZone;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class Jt600ProtocolEncoder extends StringProtocolEncoder {
+
+ public Jt600ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -36,10 +41,8 @@ public class Jt600ProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_REBOOT_DEVICE:
return "(S17)";
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
+
}
diff --git a/src/main/java/org/traccar/protocol/KenjiProtocol.java b/src/main/java/org/traccar/protocol/KenjiProtocol.java
new file mode 100644
index 000000000..90c0c511c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/KenjiProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Carlos Alvarez (carlos.alvarez.rozas@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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class KenjiProtocol extends BaseProtocol {
+
+ public KenjiProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new KenjiProtocolDecoder(KenjiProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/KenjiProtocolDecoder.java b/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java
index 4216da0c3..63812242a 100644
--- a/src/org/traccar/protocol/KenjiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KenjiProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -29,7 +30,7 @@ import java.util.regex.Pattern;
public class KenjiProtocolDecoder extends BaseProtocolDecoder {
- public KenjiProtocolDecoder(KenjiProtocol protocol) {
+ public KenjiProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -76,8 +77,7 @@ public class KenjiProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/KhdProtocol.java b/src/main/java/org/traccar/protocol/KhdProtocol.java
index 167727191..f77f4c311 100644
--- a/src/org/traccar/protocol/KhdProtocol.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,32 +15,24 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class KhdProtocol extends BaseProtocol {
public KhdProtocol() {
- super("khd");
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(512, 3, 2));
- pipeline.addLast("objectEncoder", new KhdProtocolEncoder());
- pipeline.addLast("objectDecoder", new KhdProtocolDecoder(KhdProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(512, 3, 2));
+ pipeline.addLast(new KhdProtocolEncoder(KhdProtocol.this));
+ pipeline.addLast(new KhdProtocolDecoder(KhdProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
index d63219736..0dd5b085a 100644
--- a/src/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
@@ -30,23 +32,16 @@ import java.net.SocketAddress;
public class KhdProtocolDecoder extends BaseProtocolDecoder {
- public KhdProtocolDecoder(KhdProtocol protocol) {
+ public KhdProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private String readSerialNumber(ChannelBuffer buf) {
+ private String readSerialNumber(ByteBuf buf) {
int b1 = buf.readUnsignedByte();
- int b2 = buf.readUnsignedByte();
- if (b2 > 0x80) {
- b2 -= 0x80;
- }
- int b3 = buf.readUnsignedByte();
- if (b3 > 0x80) {
- b3 -= 0x80;
- }
+ int b2 = buf.readUnsignedByte() - 0x80;
+ int b3 = buf.readUnsignedByte() - 0x80;
int b4 = buf.readUnsignedByte();
- String serialNumber = String.format("%02d%02d%02d%02d", b1, b2, b3, b4);
- return String.valueOf(Long.parseLong(serialNumber));
+ return String.format("%02d%02d%02d%02d", b1, b2, b3, b4);
}
public static final int MSG_LOGIN = 0xB1;
@@ -55,24 +50,46 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_POSITION_UPLOAD = 0x80;
public static final int MSG_POSITION_REUPLOAD = 0x8E;
public static final int MSG_ALARM = 0x82;
+ public static final int MSG_ADMIN_NUMBER = 0x83;
+ public static final int MSG_SEND_TEXT = 0x84;
public static final int MSG_REPLY = 0x85;
+ public static final int MSG_SMS_ALARM_SWITCH = 0x86;
public static final int MSG_PERIPHERAL = 0xA3;
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
int type = buf.readUnsignedByte();
buf.readUnsignedShort(); // size
+ if (type == MSG_LOGIN || type == MSG_ADMIN_NUMBER || type == MSG_SEND_TEXT
+ || type == MSG_SMS_ALARM_SWITCH || type == MSG_POSITION_REUPLOAD) {
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x29);
+ response.writeByte(0x29); // header
+ response.writeByte(MSG_CONFIRMATION);
+ response.writeShort(5); // size
+ response.writeByte(buf.getByte(buf.writerIndex() - 2));
+ response.writeByte(type);
+ response.writeByte(buf.writerIndex() > 9 ? buf.getByte(9) : 0); // 10th byte
+ response.writeByte(Checksum.xor(response.nioBuffer()));
+ response.writeByte(0x0D); // ending
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ }
+
if (type == MSG_ON_DEMAND || type == MSG_POSITION_UPLOAD || type == MSG_POSITION_REUPLOAD
|| type == MSG_ALARM || type == MSG_REPLY || type == MSG_PERIPHERAL) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, readSerialNumber(buf));
if (deviceSession == null) {
@@ -132,22 +149,6 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
return position;
- } else if (type == MSG_LOGIN && channel != null) {
-
- buf.skipBytes(4); // serial number
- buf.readByte(); // reserved
-
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
- response.writeByte(0x29); response.writeByte(0x29); // header
- response.writeByte(MSG_CONFIRMATION);
- response.writeShort(5); // size
- response.writeByte(buf.readUnsignedByte());
- response.writeByte(type);
- response.writeByte(0); // reserved
- response.writeByte(Checksum.xor(response.toByteBuffer()));
- response.writeByte(0x0D); // ending
- channel.write(response);
-
}
return null;
diff --git a/src/org/traccar/protocol/KhdProtocolEncoder.java b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
index 618e43dad..4a8df26c8 100644
--- a/src/org/traccar/protocol/KhdProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,21 +15,25 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class KhdProtocolEncoder extends BaseProtocolEncoder {
public static final int MSG_CUT_OIL = 0x39;
public static final int MSG_RESUME_OIL = 0x38;
- private ChannelBuffer encodeCommand(int command) {
+ public KhdProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeCommand(int command, String uniqueId) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
buf.writeByte(0x29);
buf.writeByte(0x29);
@@ -37,9 +41,14 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder {
buf.writeByte(command);
buf.writeShort(6); // size
- buf.writeInt(0); // terminal id
+ uniqueId = "00000000".concat(uniqueId);
+ uniqueId = uniqueId.substring(uniqueId.length() - 8);
+ buf.writeByte(Integer.parseInt(uniqueId.substring(0, 2)));
+ buf.writeByte(Integer.parseInt(uniqueId.substring(2, 4)) + 0x80);
+ buf.writeByte(Integer.parseInt(uniqueId.substring(4, 6)) + 0x80);
+ buf.writeByte(Integer.parseInt(uniqueId.substring(6, 8)));
- buf.writeByte(Checksum.xor(buf.toByteBuffer()));
+ buf.writeByte(Checksum.xor(buf.nioBuffer()));
buf.writeByte(0x0D); // ending
return buf;
@@ -48,17 +57,16 @@ public class KhdProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
+ String uniqueId = getUniqueId(command.getDeviceId());
+
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return encodeCommand(MSG_CUT_OIL);
+ return encodeCommand(MSG_CUT_OIL, uniqueId);
case Command.TYPE_ENGINE_RESUME:
- return encodeCommand(MSG_RESUME_OIL);
+ return encodeCommand(MSG_RESUME_OIL, uniqueId);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/L100FrameDecoder.java b/src/main/java/org/traccar/protocol/L100FrameDecoder.java
new file mode 100644
index 000000000..158461895
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/L100FrameDecoder.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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 L100FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ if (buf.getCharSequence(buf.readerIndex(), 4, StandardCharsets.US_ASCII).toString().equals("ATL,")) {
+ return decodeNew(buf);
+ } else {
+ return decodeOld(buf);
+ }
+ }
+
+ private Object decodeOld(ByteBuf buf) {
+
+ int header = buf.getByte(buf.readerIndex());
+ boolean obd = header == 'L' || header == 'H';
+
+ int index;
+ if (obd) {
+ index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*');
+ } else {
+ index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x02);
+ if (index < 0) {
+ index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x04);
+ if (index < 0) {
+ return null;
+ }
+ }
+ }
+
+ index += 2; // checksum
+
+ if (buf.writerIndex() >= index) {
+ if (!obd) {
+ buf.skipBytes(2); // header
+ }
+ ByteBuf frame = buf.readRetainedSlice(index - buf.readerIndex() - 2);
+ buf.skipBytes(2); // footer
+ return frame;
+ }
+
+ return null;
+ }
+
+ private Object decodeNew(ByteBuf buf) {
+
+ int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '@');
+ if (index < 0) {
+ return null;
+ }
+
+ if (buf.writerIndex() >= index + 1) {
+ ByteBuf frame = buf.readRetainedSlice(index - buf.readerIndex());
+ buf.skipBytes(1); // delimiter
+ return frame;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/L100Protocol.java b/src/main/java/org/traccar/protocol/L100Protocol.java
index 2bcef4caa..942029307 100644
--- a/src/org/traccar/protocol/L100Protocol.java
+++ b/src/main/java/org/traccar/protocol/L100Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,26 +15,22 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
+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 java.util.List;
-
public class L100Protocol extends BaseProtocol {
public L100Protocol() {
- super("l100");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new L100FrameDecoder());
- pipeline.addLast("objectDecoder", new L100ProtocolDecoder(L100Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new L100FrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new L100ProtocolDecoder(L100Protocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
new file mode 100644
index 000000000..9868de435
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2016 - 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.ObdDecoder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class L100ProtocolDecoder extends BaseProtocolDecoder {
+
+ public L100ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("ATL")
+ .expression(",[^,]+,").optional()
+ .number("(d{15}),") // imei
+ .text("$GPRMC,")
+ .number("(dd)(dd)(dd)") // time (hhmmss.sss)
+ .number(".(ddd)").optional()
+ .expression(",([AV]),") // validity
+ .number("(d+)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(d+)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.?d*)?,") // speed
+ .number("(d+.?d*)?,") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .any()
+ .text("#")
+ .number("([01]+),") // io status
+ .number("(d+.?d*|N.C),") // adc
+ .expression("[^,]*,") // reserved
+ .expression("[^,]*,") // reserved
+ .number("(d+.?d*),") // odometer
+ .number("(d+.?d*),") // temperature
+ .number("(d+.?d*),") // battery
+ .number("(d+),") // rssi
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+)") // cid
+ .any()
+ .text("ATL")
+ .compile();
+
+ private static final Pattern PATTERN_OBD_LOCATION = new PatternBuilder()
+ .expression("[LH],") // archive
+ .text("ATL,")
+ .number("(d{15}),") // imei
+ .number("(d+),") // type
+ .number("(d+),") // index
+ .groupBegin()
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .expression("([AV]),") // validity
+ .number("(d+.d+);([NS]),") // latitude
+ .number("(d+.d+);([EW]),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(d+.d+),") // odometer
+ .number("(d+.d+),") // battery
+ .number("(d+),") // rssi
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(d+),") // lac
+ .number("(x+),") // cid
+ .number("#(d)(d)(d)(d),") // status
+ .number("(d),") // overspeed
+ .text("ATL,")
+ .groupEnd("?")
+ .compile();
+
+ private static final Pattern PATTERN_OBD_DATA = new PatternBuilder()
+ .expression("[LH],") // archive
+ .text("ATLOBD,")
+ .number("(d{15}),") // imei
+ .number("d+,") // type
+ .number("d+,") // index
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .expression("[^,]+,") // obd protocol
+ .expression("(.+)") // data
+ .compile();
+
+ private static final Pattern PATTERN_NEW = new PatternBuilder()
+ .groupBegin()
+ .text("ATL,")
+ .expression("[LH],") // archive
+ .number("(d{15}),") // imei
+ .groupEnd("?")
+ .expression("([NPT]),") // alarm
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AV]),") // validity
+ .number("(d+.d+),([NS]),") // latitude
+ .number("(d+.d+),([EW]),") // longitude
+ .number("(d+.?d*),") // speed
+ .expression("(?:GPS|GSM|INV),")
+ .number("(d+),") // battery
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(d+),") // lac
+ .number("(d+)") // cid
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (sentence.startsWith("L") || sentence.startsWith("H")) {
+ if (sentence.substring(2, 8).equals("ATLOBD")) {
+ return decodeObdData(channel, remoteAddress, sentence);
+ } else {
+ return decodeObdLocation(channel, remoteAddress, sentence);
+ }
+ } else if (!sentence.contains("$GPRMC")) {
+ return decodeNew(channel, remoteAddress, sentence);
+ } else {
+ return decodeNormal(channel, remoteAddress, sentence);
+ }
+ }
+
+ private Object decodeNormal(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt(0));
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.set(Position.KEY_STATUS, parser.next());
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ int rssi = parser.nextInt();
+ if (rssi > 0) {
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
+ }
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(String.valueOf((char) 0x01), remoteAddress));
+ }
+
+ return position;
+ }
+
+ private Object decodeObdLocation(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_OBD_LOCATION, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String imei = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type = parser.nextInt();
+ int index = parser.nextInt();
+
+ if (type == 1) {
+ if (channel != null) {
+ String response = "@" + imei + ",00," + index + ",";
+ response += "*" + (char) Checksum.xor(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(parser.nextInt());
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ int rssi = parser.nextInt();
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextHexInt(), rssi)));
+
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ parser.next(); // reserved
+
+ switch (parser.nextInt()) {
+ case 0:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 2:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 1:
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ break;
+ default:
+ break;
+ }
+
+ position.set(Position.KEY_CHARGE, parser.nextInt() == 1);
+
+ if (parser.nextInt() == 1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+
+ return position;
+ }
+
+ private Object decodeObdData(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_OBD_DATA, 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, parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
+
+ for (String entry : parser.next().split(",")) {
+ String[] values = entry.split(":");
+ if (values.length == 2 && values[1].charAt(0) != 'X') {
+ position.add(ObdDecoder.decodeData(
+ Integer.parseInt(values[0].substring(2), 16), Integer.parseInt(values[1], 16), true));
+ }
+ }
+
+ return position;
+ }
+
+ private Object decodeNew(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_NEW, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String imei = parser.next();
+ DeviceSession deviceSession;
+ if (imei != null) {
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ } else {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ }
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ switch (parser.next()) {
+ case "P":
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ break;
+ case "T":
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ break;
+ default:
+ break;
+ }
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(parser.nextDouble());
+
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
+
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextHexInt())));
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocol.java b/src/main/java/org/traccar/protocol/LaipacProtocol.java
new file mode 100644
index 000000000..1d561dbd2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LaipacProtocol.java
@@ -0,0 +1,47 @@
+/*
+ * 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 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;
+
+public class LaipacProtocol extends BaseProtocol {
+
+ public LaipacProtocol() {
+
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_REBOOT_DEVICE);
+
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new LaipacProtocolEncoder(LaipacProtocol.this));
+ pipeline.addLast(new LaipacProtocolDecoder(LaipacProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
new file mode 100644
index 000000000..0c72568f3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2013 - 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 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.Checksum;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class LaipacProtocolDecoder extends BaseProtocolDecoder {
+
+ public LaipacProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final String DEFAULT_DEVICE_PASSWORD = "00000000";
+
+ private static final Pattern PATTERN_EAVSYS = new PatternBuilder()
+ .text("$EAVSYS,")
+ .expression("([^,]+),") // identifier
+ .expression("([0-9]+),") // iccid
+ .expression("(\\+?[0-9]+)?,") // sim phone number
+ .expression("(?:[^,]*),") // owner name
+ .expression("([^,]*)?") // firmware version
+ .text("*")
+ .number("(xx)") // checksum
+ .compile();
+
+ private static final Pattern PATTERN_AVRMC = new PatternBuilder()
+ .text("$AVRMC,")
+ .expression("([^,]+),") // identifier
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AVRPavrp]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),") // latitude hemisphere
+ .number("(ddd)(dd.d+),") // longitude
+ .number("([EW]),") // longitude hemisphere
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .expression("([0-9A-Za-z]),") // event code
+ .expression("([\\d.]+),") // battery voltage
+ .number("(d+),") // current mileage
+ .number("(d),") // gps status
+ .number("(d+),") // adc1
+ .number("(d+)") // adc2
+ .number(",(xxxx|x)") // lac | lac+cid = 0
+ .number("(xxxx),") // cid | nothing
+ .number("(ddd|d)") // mcc | mcc+mnc = 0
+ .number("(ddd)") // mnc | nothing
+ .optional(4)
+ .expression(",([^*]*)") // anything remaining (be forward compatible)
+ .optional(1)
+ .text("*")
+ .number("(xx)") // checksum
+ .compile();
+
+ private String decodeAlarm(String event) {
+ switch (event) {
+ case "Z":
+ return Position.ALARM_LOW_BATTERY;
+ case "Y":
+ return Position.ALARM_TOW;
+ case "X":
+ return Position.ALARM_GEOFENCE_ENTER;
+ case "T":
+ return Position.ALARM_TAMPERING;
+ case "H":
+ return Position.ALARM_POWER_OFF;
+ case "8":
+ return Position.ALARM_SHOCK;
+ case "7":
+ case "4":
+ return Position.ALARM_GEOFENCE_EXIT;
+ case "6":
+ return Position.ALARM_OVERSPEED;
+ case "5":
+ return Position.ALARM_POWER_CUT;
+ case "3":
+ return Position.ALARM_SOS;
+ default:
+ return null;
+ }
+ }
+
+ private String decodeEvent(String event, Position position) {
+
+ if (event.length() == 1) {
+ char inputStatus = event.charAt(0);
+ if (inputStatus >= 'A' && inputStatus <= 'D') {
+ int inputStatusInt = inputStatus - 'A';
+ position.set(Position.PREFIX_IN + 1, inputStatusInt & 1);
+ position.set(Position.PREFIX_IN + 2, inputStatusInt & 2);
+ return null;
+ }
+ }
+
+ return event;
+ }
+
+ private void sendEventResponse(
+ String event, String devicePassword, Channel channel, SocketAddress remoteAddress) {
+
+ String responseCode = null;
+
+ switch (event) {
+ case "3":
+ responseCode = "d";
+ break;
+ case "S":
+ case "T":
+ responseCode = "t";
+ break;
+ case "X":
+ case "4":
+ responseCode = "x";
+ break;
+ case "Y":
+ responseCode = "y";
+ break;
+ case "Z":
+ responseCode = "z";
+ break;
+ default:
+ break;
+ }
+
+ if (responseCode != null) {
+ String response = "$AVCFG," + devicePassword + "," + responseCode;
+ response += Checksum.nmea(response) + "\r\n";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ }
+
+ private void sendAcknowledge(
+ String status, String event, String checksum, Channel channel, SocketAddress remoteAddress) {
+
+ if (Character.isLowerCase(status.charAt(0))) {
+ String response = "$EAVACK," + event + "," + checksum;
+ response += Checksum.nmea(response) + "\r\n";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ }
+
+ protected Object decodeEavsys(
+ String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_EAVSYS, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession =
+ getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_ICCID, parser.next());
+ position.set(Position.KEY_PHONE, parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.next());
+
+ return position;
+ }
+
+ protected Object decodeAvrmc(
+ String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_AVRMC, 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());
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ String status = parser.next();
+ String upperCaseStatus = status.toUpperCase();
+ position.setValid(upperCaseStatus.equals("A") || upperCaseStatus.equals("R") || upperCaseStatus.equals("P"));
+ position.set(Position.KEY_STATUS, status);
+
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ position.setTime(dateBuilder.getDate());
+
+ String event = parser.next();
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, decodeEvent(event, position));
+ position.set(Position.KEY_BATTERY, Double.parseDouble(parser.next().replaceAll("\\.", "")) * 0.001);
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ position.set(Position.KEY_GPS, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble() * 0.001);
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001);
+
+ Integer lac = parser.nextHexInt();
+ Integer cid = parser.nextHexInt();
+ Integer mcc = parser.nextInt();
+ Integer mnc = parser.nextInt();
+ if (lac != null && cid != null && mcc != null && mnc != null) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid)));
+ }
+
+ parser.next(); // unused
+
+ String checksum = parser.next();
+
+ if (channel != null) {
+
+ sendAcknowledge(status, event, checksum, channel, remoteAddress);
+
+ String devicePassword = Context.getIdentityManager()
+ .getDevicePassword(deviceSession.getDeviceId(), getProtocolName(), DEFAULT_DEVICE_PASSWORD);
+ sendEventResponse(event, devicePassword, channel, remoteAddress);
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (sentence.startsWith("$ECHK")) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(sentence + "\r\n", remoteAddress));
+ }
+ } else if (sentence.startsWith("$EAVSYS")) {
+ return decodeEavsys(sentence, channel, remoteAddress);
+ } else if (sentence.startsWith("$AVRMC")) {
+ return decodeAvrmc(sentence, channel, remoteAddress);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
new file mode 100644
index 000000000..0c9f8ebb8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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.helper.Checksum;
+import org.traccar.Protocol;
+
+public class LaipacProtocolEncoder extends StringProtocolEncoder {
+
+ public LaipacProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected String formatCommand(Command command, String format, String... keys) {
+ String sentence = super.formatCommand(command, "$" + format, keys);
+ sentence += Checksum.nmea(sentence) + "\r\n";
+ return sentence;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ initDevicePassword(command, LaipacProtocolDecoder.DEFAULT_DEVICE_PASSWORD);
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "%s",
+ Command.KEY_DATA);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, "AVREQ,%s,1",
+ Command.KEY_DEVICE_PASSWORD);
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatCommand(command, "AVRESET,%s,%s",
+ Command.KEY_UNIQUE_ID, Command.KEY_DEVICE_PASSWORD);
+ default:
+ return null;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
new file mode 100644
index 000000000..05f63a2d7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 Jesse Hills (jesserockz@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.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;
+
+public class LeafSpyProtocol extends BaseProtocol {
+
+ public LeafSpyProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new LeafSpyProtocolDecoder(LeafSpyProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
new file mode 100644
index 000000000..5b352a961
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 Jesse Hills (jesserockz@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.QueryStringDecoder;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+import org.traccar.NetworkMessage;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class LeafSpyProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public LeafSpyProtocolDecoder(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.uri());
+ Map<String, List<String>> params = decoder.parameters();
+ if (params.isEmpty()) {
+ decoder = new QueryStringDecoder(request.content().toString(StandardCharsets.US_ASCII), false);
+ params = decoder.parameters();
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setValid(true);
+
+ for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+ for (String value : entry.getValue()) {
+ switch (entry.getKey()) {
+ case "pass":
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case "Lat":
+ position.setLatitude(Double.parseDouble(value));
+ break;
+ case "Long":
+ position.setLongitude(Double.parseDouble(value));
+ break;
+ case "RPM":
+ position.set(Position.KEY_RPM, Integer.parseInt(value));
+ position.setSpeed(convertSpeed(Double.parseDouble(value) / 63, "kmh"));
+ break;
+ case "Elv":
+ position.setAltitude(Double.parseDouble(value));
+ break;
+ case "SOC":
+ position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value));
+ break;
+ case "user":
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
+ break;
+ case "ChrgMode":
+ position.set(Position.KEY_CHARGE, Integer.parseInt(value) != 0);
+ break;
+ case "Odo":
+ position.set(Position.KEY_OBD_ODOMETER, Integer.parseInt(value) * 1000);
+ break;
+ default:
+ try {
+ position.set(entry.getKey(), Double.parseDouble(value));
+ } catch (NumberFormatException e) {
+ switch (value) {
+ case "true":
+ position.set(entry.getKey(), true);
+ break;
+ case "false":
+ position.set(entry.getKey(), false);
+ break;
+ default:
+ position.set(entry.getKey(), value);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (position.getFixTime() == null) {
+ position.setTime(new Date());
+ }
+
+ if (position.getLatitude() == 0 && position.getLongitude() == 0) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ if (position.getDeviceId() != 0) {
+ if (channel != null) {
+ HttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1,
+ HttpResponseStatus.OK,
+ Unpooled.copiedBuffer("\"status\":\"0\"", StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ return position;
+ } else {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/M2cProtocol.java b/src/main/java/org/traccar/protocol/M2cProtocol.java
new file mode 100644
index 000000000..9de8526c3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/M2cProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class M2cProtocol extends BaseProtocol {
+
+ public M2cProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(32 * 1024, ']'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new M2cProtocolDecoder(M2cProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java b/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java
new file mode 100644
index 000000000..1460bb176
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/M2cProtocolDecoder.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 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.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.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.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class M2cProtocolDecoder extends BaseProtocolDecoder {
+
+ public M2cProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("#M2C,")
+ .expression("[^,]+,") // model
+ .expression("[^,]+,") // firmware
+ .number("d+,") // protocol
+ .number("(d+),") // imei
+ .number("(d+),") // index
+ .expression("([LH]),") // archive
+ .number("d+,") // priority
+ .number("(d+),") // event
+ .number("(dd)(dd)(dd),") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+),") // longitude
+ .number("(-?d+),") // altitude
+ .number("(d+),") // course
+ .number("(d+.d+),") // speed
+ .number("(d+),") // satellites
+ .number("(d+),") // odometer
+ .number("(d+),") // input
+ .number("(d+),") // output
+ .number("(d+),") // power
+ .number("(d+),") // battery
+ .number("(d+),") // adc 1
+ .number("(d+),") // adc 2
+ .number("(d+.?d*),") // temperature
+ .any()
+ .compile();
+
+ private Position decodePosition(Channel channel, SocketAddress remoteAddress, String line) {
+
+ Parser parser = new Parser(PATTERN, line);
+ 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_INDEX, parser.nextInt());
+
+ if (parser.next().equals("H")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ position.set(Position.KEY_EVENT, parser.nextInt());
+
+ position.setValid(true);
+ position.setTime(parser.nextDateTime());
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setAltitude(parser.nextInt());
+ position.setCourse(parser.nextInt());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextLong());
+ position.set(Position.KEY_INPUT, parser.nextInt());
+ position.set(Position.KEY_OUTPUT, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.001);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
+ position.set(Position.PREFIX_ADC + 1, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 2, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble());
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ sentence = sentence.substring(1); // remove start symbol
+
+ List<Position> positions = new LinkedList<>();
+ for (String line : sentence.split("\r\n")) {
+ if (!line.isEmpty()) {
+ Position position = decodePosition(channel, remoteAddress, line);
+ if (position != null) {
+ positions.add(position);
+ }
+ }
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/org/traccar/protocol/M2mProtocol.java b/src/main/java/org/traccar/protocol/M2mProtocol.java
index 09393fed0..dda328a59 100644
--- a/src/org/traccar/protocol/M2mProtocol.java
+++ b/src/main/java/org/traccar/protocol/M2mProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,27 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.FixedLengthFrameDecoder;
+import io.netty.handler.codec.FixedLengthFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class M2mProtocol extends BaseProtocol {
public M2mProtocol() {
- super("m2m");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new FixedLengthFrameDecoder(23));
- pipeline.addLast("objectDecoder", new M2mProtocolDecoder(M2mProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FixedLengthFrameDecoder(23));
+ pipeline.addLast(new M2mProtocolDecoder(M2mProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/M2mProtocolDecoder.java b/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java
index a3c2ada2f..21e4a2fd0 100644
--- a/src/org/traccar/protocol/M2mProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/M2mProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.model.Position;
@@ -26,7 +27,7 @@ import java.net.SocketAddress;
public class M2mProtocolDecoder extends BaseProtocolDecoder {
- public M2mProtocolDecoder(M2mProtocol protocol) {
+ public M2mProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -36,7 +37,7 @@ public class M2mProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
// Remove offset
for (int i = 0; i < buf.readableBytes(); i++) {
@@ -68,8 +69,7 @@ public class M2mProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/main/java/org/traccar/protocol/MaestroProtocol.java b/src/main/java/org/traccar/protocol/MaestroProtocol.java
new file mode 100644
index 000000000..87453ce7d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MaestroProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.FixedLengthFrameDecoder;
+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;
+
+public class MaestroProtocol extends BaseProtocol {
+
+ public MaestroProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FixedLengthFrameDecoder(160));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MaestroProtocolDecoder(MaestroProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/MaestroProtocolDecoder.java b/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java
index 7d779a0a0..37b097414 100644
--- a/src/org/traccar/protocol/MaestroProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MaestroProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class MaestroProtocolDecoder extends BaseProtocolDecoder {
- public MaestroProtocolDecoder(MaestroProtocol protocol) {
+ public MaestroProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -70,8 +71,7 @@ public class MaestroProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setValid(parser.nextInt(0) == 1);
diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocol.java b/src/main/java/org/traccar/protocol/ManPowerProtocol.java
new file mode 100644
index 000000000..49d8b1e9f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ManPowerProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class ManPowerProtocol extends BaseProtocol {
+
+ public ManPowerProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new ManPowerProtocolDecoder(ManPowerProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ManPowerProtocolDecoder.java b/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java
index 6cff8b961..2c7b7eb40 100644
--- a/src/org/traccar/protocol/ManPowerProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ManPowerProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class ManPowerProtocolDecoder extends BaseProtocolDecoder {
- public ManPowerProtocolDecoder(ManPowerProtocol protocol) {
+ public ManPowerProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -57,8 +58,7 @@ public class ManPowerProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/MegastekFrameDecoder.java b/src/main/java/org/traccar/protocol/MegastekFrameDecoder.java
index d9cb07108..347fa24b1 100644
--- a/src/org/traccar/protocol/MegastekFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.StringFinder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
import java.nio.charset.StandardCharsets;
-public class MegastekFrameDecoder extends FrameDecoder {
+public class MegastekFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 10) {
return null;
@@ -36,18 +36,18 @@ public class MegastekFrameDecoder extends FrameDecoder {
if (Character.isDigit(buf.getByte(buf.readerIndex()))) {
int length = 4 + Integer.parseInt(buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII));
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
} else {
while (buf.getByte(buf.readerIndex()) == '\r' || buf.getByte(buf.readerIndex()) == '\n') {
buf.skipBytes(1);
}
- int delimiter = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("\r\n"));
+ int delimiter = BufferUtil.indexOf("\r\n", buf);
if (delimiter == -1) {
delimiter = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '!');
}
if (delimiter != -1) {
- ChannelBuffer result = buf.readBytes(delimiter - buf.readerIndex());
+ ByteBuf result = buf.readRetainedSlice(delimiter - buf.readerIndex());
buf.skipBytes(1);
return result;
}
diff --git a/src/main/java/org/traccar/protocol/MegastekProtocol.java b/src/main/java/org/traccar/protocol/MegastekProtocol.java
new file mode 100644
index 000000000..e9f5f9fde
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MegastekProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class MegastekProtocol extends BaseProtocol {
+
+ public MegastekProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new MegastekFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MegastekProtocolDecoder(MegastekProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
index 3ef52acd1..d81cc0eda 100644
--- a/src/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -30,7 +31,7 @@ import java.util.regex.Pattern;
public class MegastekProtocolDecoder extends BaseProtocolDecoder {
- public MegastekProtocolDecoder(MegastekProtocol protocol) {
+ public MegastekProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -147,8 +148,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
if (!parseLocation(location, position)) {
return null;
}
@@ -250,14 +250,22 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+)?,") // odometer
.number("(d+),") // mcc
.number("(d+),") // mnc
- .number("(xxxx),") // lac
- .number("(xxxx),") // cid
+ .number("(xxxx)?,") // lac
+ .number("(x+)?,") // cid
.number("(d+)?,") // gsm
- .expression("([01]+)?,") // input
- .expression("([01]+)?,") // output
+ .groupBegin()
+ .number("([01]{4})?,") // input
+ .number("([01]{4})?,") // output
.number("(d+)?,") // adc1
.number("(d+)?,") // adc2
.number("(d+)?,") // adc3
+ .or()
+ .number("(d+),") // input
+ .number("(d+),") // output
+ .number("(d+),") // adc1
+ .number("(d+),") // adc2
+ .number("(d+),") // adc3
+ .groupEnd()
.groupBegin()
.number("(-?d+.?d*)") // temperature 1
.or().text(" ")
@@ -269,7 +277,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)?,") // rfid
.expression("[^,]*,")
.number("(d+)?,") // battery
- .expression("([^,]*);") // alert
+ .expression("([^,]*)") // alert
.any()
.compile();
@@ -285,8 +293,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.next().equals("S")) {
@@ -310,14 +317,33 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
}
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0), parser.nextInt(0))));
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+ Integer lac = parser.nextHexInt();
+ Integer cid = parser.nextHexInt();
+ Integer rssi = parser.nextInt();
+ if (lac != null && cid != null) {
+ CellTower tower = CellTower.from(mcc, mnc, lac, cid);
+ if (rssi != null) {
+ tower.setSignalStrength(rssi);
+ }
+ position.setNetwork(new Network(tower));
+ }
- position.set(Position.KEY_INPUT, parser.nextBinInt(0));
- position.set(Position.KEY_OUTPUT, parser.nextBinInt(0));
+ if (parser.hasNext(5)) {
+ position.set(Position.KEY_INPUT, parser.nextBinInt(0));
+ position.set(Position.KEY_OUTPUT, parser.nextBinInt(0));
+ for (int i = 1; i <= 3; i++) {
+ position.set(Position.PREFIX_ADC + i, parser.nextInt(0));
+ }
+ }
- for (int i = 1; i <= 3; i++) {
- position.set(Position.PREFIX_ADC + i, parser.nextInt(0));
+ if (parser.hasNext(5)) {
+ position.set(Position.KEY_HEART_RATE, parser.nextInt());
+ position.set(Position.KEY_STEPS, parser.nextInt());
+ position.set("activityTime", parser.nextInt());
+ position.set("lightSleepTime", parser.nextInt());
+ position.set("deepSleepTime", parser.nextInt());
}
for (int i = 1; i <= 2; i++) {
@@ -340,30 +366,38 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
private String decodeAlarm(String value) {
+ value = value.toLowerCase();
+ if (value.startsWith("geo")) {
+ if (value.endsWith("in")) {
+ return Position.ALARM_GEOFENCE_ENTER;
+ } else if (value.endsWith("out")) {
+ return Position.ALARM_GEOFENCE_EXIT;
+ }
+ }
switch (value) {
- case "SOS":
- case "Help":
+ case "poweron":
+ return Position.ALARM_POWER_ON;
+ case "poweroff":
+ return Position.ALARM_POWER_ON;
+ case "sos":
+ case "help":
return Position.ALARM_SOS;
- case "Over Speed":
- case "OverSpeed":
+ case "over speed":
+ case "overspeed":
return Position.ALARM_OVERSPEED;
- case "LowSpeed":
+ case "lowspeed":
return Position.ALARM_LOW_SPEED;
- case "Low Battery":
- case "LowBattery":
+ case "low battery":
+ case "lowbattery":
return Position.ALARM_LOW_BATTERY;
- case "VIB":
+ case "vib":
return Position.ALARM_VIBRATION;
- case "Move in":
- case "Geo in":
- case "Geo1 in":
- case "Geo2 in":
+ case "move in":
return Position.ALARM_GEOFENCE_ENTER;
- case "Move out":
- case "Geo out":
- case "Geo1 out":
- case "Geo2 out":
+ case "move out":
return Position.ALARM_GEOFENCE_EXIT;
+ case "error":
+ return Position.ALARM_FAULT;
default:
return null;
}
diff --git a/src/org/traccar/protocol/MeiligaoFrameDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoFrameDecoder.java
index 9340b6198..52f9ae26d 100644
--- a/src/org/traccar/protocol/MeiligaoFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,21 +15,21 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class MeiligaoFrameDecoder extends FrameDecoder {
+public class MeiligaoFrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_HEADER = 4;
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
// Strip not '$' (0x24) bytes from the beginning
- while (buf.readable() && buf.getUnsignedByte(buf.readerIndex()) != 0x24) {
+ while (buf.isReadable() && buf.getUnsignedByte(buf.readerIndex()) != 0x24) {
buf.readByte();
}
@@ -37,7 +37,7 @@ public class MeiligaoFrameDecoder extends FrameDecoder {
if (buf.readableBytes() >= MESSAGE_HEADER) {
int length = buf.getUnsignedShort(buf.readerIndex() + 2);
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocol.java b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
index dbdd2619a..e8a66e49f 100644
--- a/src/org/traccar/protocol/MeiligaoProtocol.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,19 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class MeiligaoProtocol extends BaseProtocol {
public MeiligaoProtocol() {
- super("meiligao");
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
@@ -35,24 +30,21 @@ public class MeiligaoProtocol extends BaseProtocol {
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ALARM_GEOFENCE,
Command.TYPE_SET_TIMEZONE,
+ Command.TYPE_REQUEST_PHOTO,
Command.TYPE_REBOOT_DEVICE);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new MeiligaoFrameDecoder());
- pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder());
- pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new MeiligaoFrameDecoder());
+ pipeline.addLast(new MeiligaoProtocolEncoder(MeiligaoProtocol.this));
+ pipeline.addLast(new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
}
});
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder());
- pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new MeiligaoProtocolEncoder(MeiligaoProtocol.this));
+ pipeline.addLast(new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
index b0793037f..bd66cdc4b 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,33 +15,39 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
-import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
- public MeiligaoProtocolDecoder(MeiligaoProtocol protocol) {
+ private Map<Byte, ByteBuf> photos = new HashMap<>();
+
+ public MeiligaoProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN = new PatternBuilder()
- .number("(dd)(dd)(dd).?d*,") // time (hhmmss)
+ .number("(d+)(dd)(dd).?d*,") // time (hhmmss)
.expression("([AV]),") // validity
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
@@ -69,9 +75,11 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("|(x{8})") // odometer
.groupBegin()
.number("|(xx)") // satellites
+ .text("|")
+ .expression("(.*)") // driver
.groupEnd("?")
.or()
- .number("|(x{9})") // odometer
+ .number("|(d{1,9})") // odometer
.groupBegin()
.number("|(x{5,})") // rfid
.groupEnd("?")
@@ -133,7 +141,20 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_OBD_RT = 0x9901;
public static final int MSG_OBD_RTA = 0x9902;
- private DeviceSession identify(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ public static final int MSG_TRACK_ON_DEMAND = 0x4101;
+ public static final int MSG_TRACK_BY_INTERVAL = 0x4102;
+ public static final int MSG_MOVEMENT_ALARM = 0x4106;
+ public static final int MSG_OUTPUT_CONTROL = 0x4115;
+ public static final int MSG_TIME_ZONE = 0x4132;
+ public static final int MSG_TAKE_PHOTO = 0x4151;
+ public static final int MSG_UPLOAD_PHOTO = 0x0800;
+ public static final int MSG_UPLOAD_PHOTO_RESPONSE = 0x8801;
+ public static final int MSG_DATA_PHOTO = 0x9988;
+ public static final int MSG_POSITION_IMAGE = 0x9977;
+ public static final int MSG_UPLOAD_COMPLETE = 0x0f80;
+ public static final int MSG_REBOOT_GPS = 0x4902;
+
+ private DeviceSession identify(ByteBuf buf, Channel channel, SocketAddress remoteAddress) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 7; i++) {
@@ -164,10 +185,10 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
private static void sendResponse(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer id, int type, ChannelBuffer msg) {
+ Channel channel, SocketAddress remoteAddress, ByteBuf id, int type, ByteBuf msg) {
if (channel != null) {
- ChannelBuffer buf = ChannelBuffers.buffer(
+ ByteBuf buf = Unpooled.buffer(
2 + 2 + id.readableBytes() + 2 + msg.readableBytes() + 2 + 2);
buf.writeByte('@');
@@ -176,23 +197,15 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
buf.writeBytes(id);
buf.writeShort(type);
buf.writeBytes(msg);
- buf.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.toByteBuffer()));
+ msg.release();
+ buf.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.nioBuffer()));
buf.writeByte('\r');
buf.writeByte('\n');
- channel.write(buf, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(buf, remoteAddress));
}
}
- private String getServer(Channel channel) {
- String server = Context.getConfig().getString(getProtocolName() + ".server");
- if (server == null) {
- InetSocketAddress address = (InetSocketAddress) channel.getLocalAddress();
- server = address.getAddress().getHostAddress() + ":" + address.getPort();
- }
- return server;
- }
-
private String decodeAlarm(short value) {
switch (value) {
case 0x01:
@@ -250,7 +263,15 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextDouble(0));
}
- position.set(Position.KEY_STATUS, parser.next());
+ if (parser.hasNext()) {
+ int status = parser.nextHexInt();
+ for (int i = 1; i <= 5; i++) {
+ position.set(Position.PREFIX_OUT + i, BitUtil.check(status, i - 1));
+ }
+ for (int i = 1; i <= 5; i++) {
+ position.set(Position.PREFIX_IN + i, BitUtil.check(status, i - 1 + 8));
+ }
+ }
for (int i = 1; i <= 8; i++) {
position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
@@ -259,7 +280,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, parser.nextHexInt());
position.set(Position.KEY_ODOMETER, parser.nextHexLong());
position.set(Position.KEY_SATELLITES, parser.nextHexInt());
- position.set(Position.KEY_ODOMETER, parser.nextHexLong());
+ position.set("driverLicense", parser.next());
+ position.set(Position.KEY_ODOMETER, parser.nextLong());
position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
return position;
@@ -299,7 +321,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.set("drivingRange", parser.nextDouble());
position.set(Position.KEY_ODOMETER, parser.nextDouble());
position.set("singleFuelConsumption", parser.nextDouble());
- position.set("totalFuelConsumption", parser.nextDouble());
+ position.set(Position.KEY_FUEL_USED, parser.nextDouble());
position.set(Position.KEY_DTCS, parser.nextInt());
position.set("hardAccelerationCount", parser.nextInt());
position.set("hardBrakingCount", parser.nextInt());
@@ -328,7 +350,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private List<Position> decodeRetransmission(ChannelBuffer buf, DeviceSession deviceSession) {
+ private List<Position> decodeRetransmission(ByteBuf buf, DeviceSession deviceSession) {
List<Position> positions = new LinkedList<>();
int count = buf.readUnsignedByte();
@@ -341,10 +363,9 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
endIndex = buf.writerIndex() - 4;
}
- String sentence = buf.readBytes(endIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
+ String sentence = buf.readSlice(endIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position = decodeRegular(position, sentence);
@@ -366,27 +387,35 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
buf.readShort(); // length
- ChannelBuffer id = buf.readBytes(7);
+ ByteBuf id = buf.readSlice(7);
int command = buf.readUnsignedShort();
- ChannelBuffer response;
- if (channel != null) {
- if (command == MSG_LOGIN) {
- response = ChannelBuffers.wrappedBuffer(new byte[]{0x01});
- sendResponse(channel, remoteAddress, id, MSG_LOGIN_RESPONSE, response);
- return null;
- } else if (command == MSG_HEARTBEAT) {
- response = ChannelBuffers.wrappedBuffer(new byte[]{0x01});
- sendResponse(channel, remoteAddress, id, MSG_HEARTBEAT, response);
- return null;
- } else if (command == MSG_SERVER) {
- response = ChannelBuffers.copiedBuffer(getServer(channel), StandardCharsets.US_ASCII);
- sendResponse(channel, remoteAddress, id, MSG_SERVER, response);
- return null;
- }
+ if (command == MSG_LOGIN) {
+ ByteBuf response = Unpooled.wrappedBuffer(new byte[]{0x01});
+ sendResponse(channel, remoteAddress, id, MSG_LOGIN_RESPONSE, response);
+ return null;
+ } else if (command == MSG_HEARTBEAT) {
+ ByteBuf response = Unpooled.wrappedBuffer(new byte[]{0x01});
+ sendResponse(channel, remoteAddress, id, MSG_HEARTBEAT, response);
+ return null;
+ } else if (command == MSG_SERVER) {
+ ByteBuf response = Unpooled.copiedBuffer(getServer(channel, ':'), StandardCharsets.US_ASCII);
+ sendResponse(channel, remoteAddress, id, MSG_SERVER, response);
+ return null;
+ } else if (command == MSG_UPLOAD_PHOTO) {
+ byte imageIndex = buf.readByte();
+ photos.put(imageIndex, Unpooled.buffer());
+ ByteBuf response = Unpooled.copiedBuffer(new byte[]{imageIndex});
+ sendResponse(channel, remoteAddress, id, MSG_UPLOAD_PHOTO_RESPONSE, response);
+ return null;
+ } else if (command == MSG_UPLOAD_COMPLETE) {
+ byte imageIndex = buf.readByte();
+ ByteBuf response = Unpooled.copiedBuffer(new byte[]{imageIndex, 0, 0});
+ sendResponse(channel, remoteAddress, id, MSG_RETRANSMISSION, response);
+ return null;
}
DeviceSession deviceSession = identify(id, channel, remoteAddress);
@@ -394,14 +423,26 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- if (command == MSG_RETRANSMISSION) {
+ if (command == MSG_DATA_PHOTO) {
+
+ byte imageIndex = buf.readByte();
+ buf.readUnsignedShort(); // image footage
+ buf.readUnsignedByte(); // total packets
+ buf.readUnsignedByte(); // packet index
+
+ photos.get(imageIndex).writeBytes(buf, buf.readableBytes() - 2 - 2);
+
+ return null;
+
+ } else if (command == MSG_RETRANSMISSION) {
return decodeRetransmission(buf, deviceSession);
} else {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
+
+ position.setDeviceId(deviceSession.getDeviceId());
if (command == MSG_ALARM) {
short alarmCode = buf.readUnsignedByte();
@@ -413,11 +454,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
} else if (command == MSG_POSITION_LOGGED) {
buf.skipBytes(6);
- }
-
- position.setDeviceId(deviceSession.getDeviceId());
-
- if (command == MSG_RFID) {
+ } else if (command == MSG_RFID) {
for (int i = 0; i < 15; i++) {
long rfid = buf.readUnsignedInt();
if (rfid != 0) {
@@ -426,23 +463,37 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_DRIVER_UNIQUE_ID, card);
}
}
+ } 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"));
+ } finally {
+ photo.release();
+ }
}
String sentence = buf.toString(buf.readerIndex(), buf.readableBytes() - 4, StandardCharsets.US_ASCII);
- if (command == MSG_POSITION || command == MSG_POSITION_LOGGED || command == MSG_ALARM) {
- return decodeRegular(position, sentence);
- } else if (command == MSG_RFID) {
- return decodeRfid(position, sentence);
- } else if (command == MSG_OBD_RT) {
- return decodeObd(position, sentence);
- } else if (command == MSG_OBD_RTA) {
- return decodeObdA(position, sentence);
+ switch (command) {
+ case MSG_POSITION:
+ case MSG_POSITION_LOGGED:
+ case MSG_ALARM:
+ case MSG_POSITION_IMAGE:
+ return decodeRegular(position, sentence);
+ case MSG_RFID:
+ return decodeRfid(position, sentence);
+ case MSG_OBD_RT:
+ return decodeObd(position, sentence);
+ case MSG_OBD_RTA:
+ return decodeObdA(position, sentence);
+ default:
+ return null;
}
}
-
- return null;
}
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
index 2e0a1e84c..36e94195c 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,42 +15,39 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
+import org.traccar.Protocol;
-import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.util.TimeZone;
public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
- public static final int MSG_TRACK_ON_DEMAND = 0x4101;
- public static final int MSG_TRACK_BY_INTERVAL = 0x4102;
- public static final int MSG_MOVEMENT_ALARM = 0x4106;
- public static final int MSG_OUTPUT_CONTROL = 0x4115;
- public static final int MSG_TIME_ZONE = 0x4132;
- public static final int MSG_REBOOT_GPS = 0x4902;
+ public MeiligaoProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
- private ChannelBuffer encodeContent(long deviceId, int type, ChannelBuffer content) {
+ private ByteBuf encodeContent(long deviceId, int type, ByteBuf content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
buf.writeByte('@');
buf.writeByte('@');
buf.writeShort(2 + 2 + 7 + 2 + content.readableBytes() + 2 + 2); // message length
- buf.writeBytes(DatatypeConverter.parseHexBinary((getUniqueId(deviceId) + "FFFFFFFFFFFFFF").substring(0, 14)));
+ buf.writeBytes(DataConverter.parseHex((getUniqueId(deviceId) + "FFFFFFFFFFFFFF").substring(0, 14)));
buf.writeShort(type);
buf.writeBytes(content);
- buf.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.toByteBuffer()));
+ buf.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.nioBuffer()));
buf.writeByte('\r');
buf.writeByte('\n');
@@ -61,35 +58,34 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- ChannelBuffer content = ChannelBuffers.dynamicBuffer();
+ ByteBuf content = Unpooled.buffer();
switch (command.getType()) {
case Command.TYPE_POSITION_SINGLE:
- return encodeContent(command.getDeviceId(), MSG_TRACK_ON_DEMAND, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_TRACK_ON_DEMAND, content);
case Command.TYPE_POSITION_PERIODIC:
content.writeShort(command.getInteger(Command.KEY_FREQUENCY) / 10);
- return encodeContent(command.getDeviceId(), MSG_TRACK_BY_INTERVAL, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_TRACK_BY_INTERVAL, content);
case Command.TYPE_ENGINE_STOP:
content.writeByte(0x01);
- return encodeContent(command.getDeviceId(), MSG_OUTPUT_CONTROL, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL, content);
case Command.TYPE_ENGINE_RESUME:
content.writeByte(0x00);
- return encodeContent(command.getDeviceId(), MSG_OUTPUT_CONTROL, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_OUTPUT_CONTROL, content);
case Command.TYPE_ALARM_GEOFENCE:
content.writeShort(command.getInteger(Command.KEY_RADIUS));
- return encodeContent(command.getDeviceId(), MSG_MOVEMENT_ALARM, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_MOVEMENT_ALARM, content);
case Command.TYPE_SET_TIMEZONE:
int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
content.writeBytes(String.valueOf(offset).getBytes(StandardCharsets.US_ASCII));
- return encodeContent(command.getDeviceId(), MSG_TIME_ZONE, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_TIME_ZONE, content);
+ case Command.TYPE_REQUEST_PHOTO:
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_TAKE_PHOTO, content);
case Command.TYPE_REBOOT_DEVICE:
- return encodeContent(command.getDeviceId(), MSG_REBOOT_GPS, content);
+ return encodeContent(command.getDeviceId(), MeiligaoProtocolDecoder.MSG_REBOOT_GPS, content);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/org/traccar/protocol/MeitrackFrameDecoder.java b/src/main/java/org/traccar/protocol/MeitrackFrameDecoder.java
index 1eb0dae0f..d122bca0c 100644
--- a/src/org/traccar/protocol/MeitrackFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,18 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+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 MeitrackFrameDecoder extends FrameDecoder {
+public class MeitrackFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 10) {
return null;
@@ -37,7 +37,7 @@ public class MeitrackFrameDecoder extends FrameDecoder {
int length = index - buf.readerIndex() + Integer.parseInt(
buf.toString(buf.readerIndex() + 3, index - buf.readerIndex() - 3, StandardCharsets.US_ASCII));
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
}
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocol.java b/src/main/java/org/traccar/protocol/MeitrackProtocol.java
new file mode 100644
index 000000000..7439ea611
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocol.java
@@ -0,0 +1,54 @@
+/*
+ * 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class MeitrackProtocol extends BaseProtocol {
+
+ public MeitrackProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_ALARM_ARM,
+ Command.TYPE_ALARM_DISARM,
+ Command.TYPE_REQUEST_PHOTO,
+ Command.TYPE_SEND_SMS);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new MeitrackProtocolEncoder(MeitrackProtocol.this));
+ pipeline.addLast(new MeitrackProtocolDecoder(MeitrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 5b67aebe3..55260ef0c 100644
--- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,12 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -38,9 +40,9 @@ import java.util.regex.Pattern;
public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
- private ChannelBuffer photo;
+ private ByteBuf photo;
- public MeitrackProtocolDecoder(MeitrackProtocol protocol) {
+ public MeitrackProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -75,13 +77,20 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)|") // battery
.number("(x+)?,") // power
.groupBegin()
- .expression("([^,]+)?,") // event specific
+ .expression("([^,]+)?,").optional() // event specific
.expression("[^,]*,") // reserved
.number("(d+)?,") // protocol
.number("(x{4})?") // fuel
- .number("(?:,(x{6}(?:|x{6})*))?") // temperature
+ .groupBegin()
+ .number(",(x{6}(?:|x{6})*)?") // temperature
+ .groupBegin()
+ .number(",(d+)") // data count
+ .expression(",([^*]*)") // data
+ .groupEnd("?")
.groupEnd("?")
+ .or()
.any()
+ .groupEnd()
.text("*")
.number("xx")
.text("\r\n").optional()
@@ -107,20 +116,32 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_POWER_CUT;
case 36:
return Position.ALARM_TOW;
+ case 44:
+ return Position.ALARM_JAMMING;
+ case 78:
+ return Position.ALARM_ACCIDENT;
+ case 90:
+ case 91:
+ return Position.ALARM_CORNERING;
+ case 129:
+ return Position.ALARM_BRAKING;
+ case 130:
+ return Position.ALARM_ACCELERATION;
+ case 135:
+ return Position.ALARM_FATIGUE_DRIVING;
default:
return null;
}
}
- private Position decodeRegular(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Position decodeRegular(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
Parser parser = new Parser(PATTERN, buf.toString(StandardCharsets.US_ASCII));
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -234,10 +255,37 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
}
+ if (parser.hasNext(2)) {
+ parser.nextInt(); // count
+ decodeDataFields(position, parser.next().split(","));
+ }
+
return position;
}
- private List<Position> decodeBinaryC(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private void decodeDataFields(Position position, String[] values) {
+
+ if (values.length > 1 && !values[1].isEmpty()) {
+ position.set("tempData", values[1]);
+ }
+
+ if (values.length > 5 && !values[5].isEmpty()) {
+ String[] data = values[5].split("\\|");
+ boolean started = data[0].charAt(1) == '0';
+ position.set("taximeterOn", started);
+ position.set("taximeterStart", data[1]);
+ if (data.length > 2) {
+ position.set("taximeterEnd", data[2]);
+ position.set("taximeterDistance", Integer.parseInt(data[3]));
+ position.set("taximeterFare", Integer.parseInt(data[4]));
+ position.set("taximeterTrip", data[5]);
+ position.set("taximeterWait", data[6]);
+ }
+ }
+
+ }
+
+ private List<Position> decodeBinaryC(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
List<Position> positions = new LinkedList<>();
String flag = buf.toString(2, 1, StandardCharsets.US_ASCII);
@@ -253,44 +301,43 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
while (buf.readableBytes() >= 0x34) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_EVENT, buf.readUnsignedByte());
- position.setLatitude(buf.readInt() * 0.000001);
- position.setLongitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readIntLE() * 0.000001);
+ position.setLongitude(buf.readIntLE() * 0.000001);
- position.setTime(new Date((946684800 + buf.readUnsignedInt()) * 1000)); // 946684800 = 2000-01-01
+ position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 946684800 = 2000-01-01
position.setValid(buf.readUnsignedByte() == 1);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
int rssi = buf.readUnsignedByte();
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
- position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE());
- position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
+ position.set(Position.KEY_HDOP, buf.readUnsignedShortLE() * 0.1);
- position.setAltitude(buf.readUnsignedShort());
+ position.setAltitude(buf.readUnsignedShortLE());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- position.set("runtime", buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set("runtime", buf.readUnsignedIntLE());
position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShort(), buf.readUnsignedShort(),
- buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
+ buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
rssi)));
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+ position.set(Position.KEY_STATUS, buf.readUnsignedShortLE());
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01);
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE());
- buf.readUnsignedInt(); // geo-fence
+ buf.readUnsignedIntLE(); // geo-fence
positions.add(position);
}
@@ -305,17 +352,17 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
command.append(String.format("%02x", checksum & 0xff).toUpperCase());
command.append("\r\n");
- channel.write(command.toString()); // delete processed data
+ channel.writeAndFlush(new NetworkMessage(command.toString(), remoteAddress)); // delete processed data
}
return positions;
}
- private List<Position> decodeBinaryE(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private List<Position> decodeBinaryE(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
List<Position> positions = new LinkedList<>();
buf.readerIndex(buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',') + 1);
- String imei = buf.readBytes(15).toString(StandardCharsets.US_ASCII);
+ String imei = buf.readSlice(15).toString(StandardCharsets.US_ASCII);
buf.skipBytes(1 + 3 + 1);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
@@ -323,16 +370,15 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- buf.readUnsignedInt(); // remaining cache
- int count = buf.readUnsignedShort();
+ buf.readUnsignedIntLE(); // remaining cache
+ int count = buf.readUnsignedShortLE();
for (int i = 0; i < count; i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- buf.readUnsignedShort(); // length
- buf.readUnsignedShort(); // index
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // index
int paramCount = buf.readUnsignedByte();
for (int j = 0; j < paramCount; j++) {
@@ -347,6 +393,9 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case 0x06:
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case 0x07:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ break;
default:
buf.readUnsignedByte();
break;
@@ -358,16 +407,22 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
int id = buf.readUnsignedByte();
switch (id) {
case 0x08:
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
break;
case 0x09:
- position.setCourse(buf.readUnsignedShort() * 0.1);
+ position.setCourse(buf.readUnsignedShortLE());
break;
case 0x0B:
- position.setAltitude(buf.readShort());
+ position.setAltitude(buf.readShortLE());
+ break;
+ case 0x19:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01);
+ break;
+ case 0x1A:
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01);
break;
default:
- buf.readUnsignedShort();
+ buf.readUnsignedShortLE();
break;
}
}
@@ -377,16 +432,19 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
int id = buf.readUnsignedByte();
switch (id) {
case 0x02:
- position.setLatitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readIntLE() * 0.000001);
break;
case 0x03:
- position.setLongitude(buf.readInt() * 0.000001);
+ position.setLongitude(buf.readIntLE() * 0.000001);
break;
case 0x04:
- position.setTime(new Date((946684800 + buf.readUnsignedInt()) * 1000)); // 2000-01-01
+ position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 2000-01-01
+ break;
+ case 0x0D:
+ position.set("runtime", buf.readUnsignedIntLE());
break;
default:
- buf.readUnsignedInt();
+ buf.readUnsignedIntLE();
break;
}
}
@@ -403,13 +461,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
return positions;
}
- private void requestPhotoPacket(Channel channel, String imei, int index) {
+ private void requestPhotoPacket(Channel channel, SocketAddress socketAddress, String imei, String file, int index) {
if (channel != null) {
- String content = "D00,camera_picture.jpg," + index;
+ 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.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, socketAddress));
}
}
@@ -417,7 +475,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
String imei = buf.toString(index + 1, 15, StandardCharsets.US_ASCII);
@@ -426,36 +484,43 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
switch (type) {
case "D00":
- index = buf.indexOf(index + 1 + type.length() + 1, buf.writerIndex(), (byte) ',') + 1;
+ if (photo == null) {
+ photo = Unpooled.buffer();
+ }
+
+ index = index + 1 + type.length() + 1;
int endIndex = buf.indexOf(index, buf.writerIndex(), (byte) ',');
+ String file = buf.toString(index, endIndex - index, StandardCharsets.US_ASCII);
+ index = endIndex + 1;
+ endIndex = buf.indexOf(index, buf.writerIndex(), (byte) ',');
int total = Integer.parseInt(buf.toString(index, endIndex - index, StandardCharsets.US_ASCII));
index = endIndex + 1;
endIndex = buf.indexOf(index, buf.writerIndex(), (byte) ',');
int current = Integer.parseInt(buf.toString(index, endIndex - index, StandardCharsets.US_ASCII));
buf.readerIndex(endIndex + 1);
- photo.writeBytes(buf.readBytes(buf.readableBytes() - 1 - 2 - 2));
+ photo.writeBytes(buf.readSlice(buf.readableBytes() - 1 - 2 - 2));
if (current == total - 1) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ 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"));
+ photo.release();
photo = null;
return position;
} else {
if ((current + 1) % 8 == 0) {
- requestPhotoPacket(channel, imei, current + 1);
+ requestPhotoPacket(channel, remoteAddress, imei, file, current + 1);
}
return null;
}
case "D03":
- photo = ChannelBuffers.dynamicBuffer();
- requestPhotoPacket(channel, imei, 0);
+ photo = Unpooled.buffer();
+ requestPhotoPacket(channel, remoteAddress, imei, "camera_picture.jpg", 0);
return null;
case "CCC":
return decodeBinaryC(channel, remoteAddress, buf);
diff --git a/src/org/traccar/protocol/MeitrackProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java
index b10a751c1..354e81434 100644
--- a/src/org/traccar/protocol/MeitrackProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,15 +15,20 @@
*/
package org.traccar.protocol;
+import org.traccar.Context;
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
import java.util.Map;
public class MeitrackProtocolEncoder extends StringProtocolEncoder {
+ public MeitrackProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
private Object formatCommand(Command command, char dataId, String content) {
String uniqueId = getUniqueId(command.getDeviceId());
int length = 1 + uniqueId.length() + 1 + content.length() + 5;
@@ -37,6 +42,9 @@ public class MeitrackProtocolEncoder extends StringProtocolEncoder {
Map<String, Object> attributes = command.getAttributes();
+ boolean alternative = Context.getIdentityManager().lookupAttributeBoolean(
+ command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+
switch (command.getType()) {
case Command.TYPE_POSITION_SINGLE:
return formatCommand(command, 'Q', "A10");
@@ -45,20 +53,18 @@ public class MeitrackProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ENGINE_RESUME:
return formatCommand(command, 'M', "C01,0,02222");
case Command.TYPE_ALARM_ARM:
- return formatCommand(command, 'M', "C01,0,22122");
+ return formatCommand(command, 'M', alternative ? "B21,1" : "C01,0,22122");
case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, 'M', "C01,0,22022");
+ return formatCommand(command, 'M', alternative ? "B21,0" : "C01,0,22022");
case Command.TYPE_REQUEST_PHOTO:
- return formatCommand(command, 'D', "D03,1,camera_picture.jpg");
+ int index = command.getInteger(Command.KEY_INDEX);
+ return formatCommand(command, 'D', "D03," + (index > 0 ? index : 1) + ",camera_picture.jpg");
case Command.TYPE_SEND_SMS:
return formatCommand(command, 'f', "C02,0,"
+ attributes.get(Command.KEY_PHONE) + "," + attributes.get(Command.KEY_MESSAGE));
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java
new file mode 100644
index 000000000..c8d64fd81
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 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;
+
+public class MictrackProtocol extends BaseProtocol {
+
+ public MictrackProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..a2fccb707
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.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;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class MictrackProtocolDecoder extends BaseProtocolDecoder {
+
+ public MictrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private Date decodeTime(String data) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(data);
+ }
+
+ private String decodeAlarm(int event) {
+ switch (event) {
+ case 5:
+ return Position.ALARM_SOS;
+ case 8:
+ return Position.ALARM_LOW_BATTERY;
+ case 9:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 10:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 12:
+ return Position.ALARM_POWER_OFF;
+ default:
+ return null;
+ }
+ }
+
+ private void decodeLocation(Position position, String data) throws ParseException {
+ int index = 0;
+ String[] values = data.split("\\+");
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+
+ position.setValid(true);
+ position.setTime(decodeTime(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Integer.parseInt(values[index++]));
+
+ int event = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.001);
+ }
+
+ private void decodeCell(Network network, String data) {
+ String[] values = data.split(",");
+ int length = values.length % 5 == 0 ? 5 : 4;
+ for (int i = 0; i < values.length / length; i++) {
+ int mnc = Integer.parseInt(values[i * length]);
+ int cid = Integer.parseInt(values[i * length + 1]);
+ int lac = Integer.parseInt(values[i * length + 2]);
+ int mcc = Integer.parseInt(values[i * length + 3]);
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid));
+ }
+ }
+
+ private void decodeWifi(Network network, String data) {
+ 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])));
+ }
+ }
+
+ private void decodeNetwork(Position position, String data, boolean hasWifi, boolean hasCell) throws ParseException {
+ int index = 0;
+ String[] values = data.split("\\+");
+
+ getLastLocation(position, decodeTime(values[index++]));
+
+ Network network = new Network();
+
+ if (hasWifi) {
+ decodeWifi(network, values[index++]);
+ }
+
+ if (hasCell) {
+ decodeCell(network, values[index++]);
+ }
+
+ position.setNetwork(network);
+
+ int event = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.001);
+ }
+
+ private void decodeStatus(Position position, String data) throws ParseException {
+ int index = 0;
+ String[] values = data.split("\\+");
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+
+ getLastLocation(position, decodeTime(values[index++]));
+
+ index += 4; // fix values
+
+ int event = Integer.parseInt(values[index++]);
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.001);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String[] fragments = ((String) msg).split(";");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, fragments[2]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ switch (fragments[3]) {
+ case "R0":
+ decodeLocation(position, fragments[4]);
+ break;
+ case "R1":
+ decodeNetwork(position, fragments[4], true, false);
+ break;
+ case "R2":
+ case "R3":
+ decodeNetwork(position, fragments[4], false, true);
+ break;
+ case "R12":
+ case "R13":
+ decodeNetwork(position, fragments[4], true, true);
+ break;
+ case "RH":
+ decodeStatus(position, fragments[4]);
+ break;
+ default:
+ return null;
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocol.java b/src/main/java/org/traccar/protocol/MilesmateProtocol.java
new file mode 100644
index 000000000..822711603
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MilesmateProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class MilesmateProtocol extends BaseProtocol {
+
+ public MilesmateProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MilesmateProtocolDecoder(MilesmateProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java b/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java
new file mode 100644
index 000000000..901ceb8f7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MilesmateProtocolDecoder.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+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 MilesmateProtocolDecoder extends BaseProtocolDecoder {
+
+ public MilesmateProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("ApiString={")
+ .number("A:(d+),") // imei
+ .number("B:(d+.d+),") // battery
+ .number("C:(d+.d+),") // adc
+ .number("D:(dd)(dd)(dd),") // time (hhmmss)
+ .number("E:(dd)(dd.d+)([NS]),") // latitude
+ .number("F:(ddd)(dd.d+)([EW]),") // longitude
+ .number("G:(d+.d+),") // speed
+ .number("H:(dd)(dd)(dd),") // date (ddmmyy)
+ .expression("I:[GL],") // location source
+ .number("J:(d{8}),") // flags
+ .number("K:(d{7})") // flags
+ .expression("([AV]),") // validity
+ .number("L:d{4},") // pin
+ .number("M:(d+.d+)") // course
+ .text("}")
+ .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;
+ }
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("+##Received OK\n", remoteAddress));
+ }
+
+ 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_BATTERY, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ String flags = parser.next();
+ position.set(Position.KEY_IGNITION, flags.charAt(0) == '1');
+ position.set(Position.KEY_ALARM, flags.charAt(1) == '1' ? Position.ALARM_SOS : null);
+ position.set(Position.KEY_CHARGE, flags.charAt(5) == '1');
+ position.set(Position.KEY_ALARM, flags.charAt(7) == '1' ? Position.ALARM_OVERSPEED : null);
+
+ flags = parser.next();
+ position.set(Position.KEY_BLOCKED, flags.charAt(0) == '1');
+ position.set(Position.KEY_ALARM, flags.charAt(1) == '1' ? Position.ALARM_TOW : null);
+
+ position.setValid(parser.next().equals("A"));
+
+ position.setCourse(parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/MiniFinderProtocol.java b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
index c36acb238..82534ecd8 100644
--- a/src/org/traccar/protocol/MiniFinderProtocol.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,21 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+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.model.Command;
-import java.util.List;
-
public class MiniFinderProtocol extends BaseProtocol {
public MiniFinderProtocol() {
- super("minifinder");
setSupportedDataCommands(
Command.TYPE_SET_TIMEZONE,
Command.TYPE_VOICE_MONITORING,
@@ -42,18 +38,14 @@ public class MiniFinderProtocol extends BaseProtocol {
Command.TYPE_MODE_DEEP_SLEEP,
Command.TYPE_SOS_NUMBER,
Command.TYPE_SET_INDICATOR);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new MiniFinderProtocolEncoder());
- pipeline.addLast("objectDecoder", new MiniFinderProtocolDecoder(MiniFinderProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MiniFinderProtocolEncoder(MiniFinderProtocol.this));
+ pipeline.addLast(new MiniFinderProtocolDecoder(MiniFinderProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
index 05994b697..2b7a960c4 100644
--- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -29,7 +30,7 @@ import java.util.regex.Pattern;
public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
- public MiniFinderProtocolDecoder(MiniFinderProtocol protocol) {
+ public MiniFinderProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -123,7 +124,7 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextDouble(0));
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
}
@Override
@@ -142,18 +143,25 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
}
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null || !sentence.matches("![A-D],.*")) {
+ if (deviceSession == null || !sentence.matches("![3A-D],.*")) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
String type = sentence.substring(1, 2);
position.set(Position.KEY_TYPE, type);
- if (type.equals("B") || type.equals("D")) {
+ if (type.equals("3")) {
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RESULT, sentence.substring(3));
+
+ return position;
+
+ } else if (type.equals("B") || type.equals("D")) {
Parser parser = new Parser(PATTERN_BD, sentence);
if (!parser.matches()) {
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
new file mode 100644
index 000000000..059f688f7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 - 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 java.util.TimeZone;
+
+import org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
+
+ public MiniFinderProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ public String formatValue(String key, Object value) {
+ switch (key) {
+ case Command.KEY_ENABLE:
+ return (Boolean) value ? "1" : "0";
+ case Command.KEY_TIMEZONE:
+ return String.format("%+03d", TimeZone.getTimeZone((String) value).getRawOffset() / 3600000);
+ case Command.KEY_INDEX:
+ switch (((Number) value).intValue()) {
+ case 0:
+ return "A";
+ case 1:
+ return "B";
+ case 2:
+ return "C";
+ default:
+ return null;
+ }
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ initDevicePassword(command, "123456");
+
+ switch (command.getType()) {
+ case Command.TYPE_SET_TIMEZONE:
+ return formatCommand(command, "%sL%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_TIMEZONE);
+ case Command.TYPE_VOICE_MONITORING:
+ return formatCommand(command, "%sP%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ case Command.TYPE_ALARM_SPEED:
+ return formatCommand(command, "%sJ1%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ case Command.TYPE_ALARM_GEOFENCE:
+ return formatCommand(command, "%sR1%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_RADIUS);
+ case Command.TYPE_ALARM_VIBRATION:
+ return formatCommand(command, "%sW1,%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ case Command.TYPE_SET_AGPS:
+ return formatCommand(command, "%sAGPS%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ case Command.TYPE_ALARM_FALL:
+ return formatCommand(command, "%sF%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ case Command.TYPE_MODE_POWER_SAVING:
+ return formatCommand(command, "%sSP%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ case Command.TYPE_MODE_DEEP_SLEEP:
+ return formatCommand(command, "%sDS%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ case Command.TYPE_SOS_NUMBER:
+ return formatCommand(command, "%s%s1,%s", this,
+ Command.KEY_DEVICE_PASSWORD, Command.KEY_INDEX, Command.KEY_PHONE);
+ case Command.TYPE_SET_INDICATOR:
+ return formatCommand(command, "%sLED%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java
new file mode 100644
index 000000000..957b8f4d0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class Minifinder2Protocol extends BaseProtocol {
+
+ public Minifinder2Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 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
new file mode 100644
index 000000000..b8ab134c5
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 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 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.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;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Minifinder2ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_DATA = 0x01;
+ public static final int MSG_RESPONSE = 0x7F;
+
+ private String decodeAlarm(int code) {
+ if (BitUtil.check(code, 0)) {
+ return Position.ALARM_LOW_BATTERY;
+ }
+ if (BitUtil.check(code, 1)) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if (BitUtil.check(code, 2)) {
+ return Position.ALARM_FALL_DOWN;
+ }
+ if (BitUtil.check(code, 8)) {
+ return Position.ALARM_POWER_OFF;
+ }
+ if (BitUtil.check(code, 9)) {
+ return Position.ALARM_POWER_ON;
+ }
+ if (BitUtil.check(code, 12)) {
+ return Position.ALARM_SOS;
+ }
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ int flags = buf.readUnsignedByte();
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // checksum
+ int index = buf.readUnsignedShortLE();
+ int type = buf.readUnsignedByte();
+
+ if (BitUtil.check(flags, 4) && channel != null) {
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(MSG_RESPONSE);
+ content.writeByte(1); // key length
+ content.writeByte(0); // success
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xAB); // header
+ response.writeByte(0x00); // properties
+ response.writeShortLE(content.readableBytes());
+ response.writeShortLE(Checksum.crc16(Checksum.CRC16_XMODEM, content.nioBuffer()));
+ response.writeShortLE(index);
+ response.writeBytes(content);
+ content.release();
+
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ if (type == MSG_DATA) {
+
+ Position position = new Position(getProtocolName());
+
+ while (buf.isReadable()) {
+ int endIndex = buf.readUnsignedByte() + buf.readerIndex();
+ int key = buf.readUnsignedByte();
+ switch (key) {
+ case 0x01:
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case 0x02:
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readIntLE()));
+ break;
+ case 0x14:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+ break;
+ case 0x20:
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+ position.setAltitude(buf.readShortLE());
+ position.setValid(buf.readUnsignedShortLE() > 0);
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x21:
+ int mcc = buf.readUnsignedShortLE();
+ int mnc = buf.readUnsignedByte();
+ if (position.getNetwork() == null) {
+ position.setNetwork(new Network());
+ }
+ while (buf.readerIndex() < endIndex) {
+ int rssi = buf.readByte();
+ position.getNetwork().addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShortLE(), buf.readUnsignedShortLE(), rssi));
+ }
+ break;
+ case 0x22:
+ if (position.getNetwork() == null) {
+ position.setNetwork(new Network());
+ }
+ while (buf.readerIndex() < endIndex) {
+ int rssi = buf.readByte();
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ position.getNetwork().addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), rssi));
+ }
+ break;
+ case 0x23:
+ if (endIndex > buf.readerIndex()) {
+ buf.skipBytes(6); // mac
+ }
+ if (endIndex > buf.readerIndex()) {
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ }
+ break;
+ case 0x24:
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ long status = buf.readUnsignedIntLE();
+ position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24));
+ position.set(Position.KEY_STATUS, status);
+ break;
+ case 0x40:
+ buf.readUnsignedIntLE(); // timestamp
+ int heartRate = buf.readUnsignedByte();
+ if (heartRate > 1) {
+ position.set(Position.KEY_HEART_RATE, heartRate);
+ }
+ break;
+ default:
+ break;
+ }
+ buf.readerIndex(endIndex);
+ }
+
+ if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) {
+ getLastLocation(position, null);
+ }
+
+ return position.getDeviceId() > 0 ? position : null;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/MotorProtocol.java b/src/main/java/org/traccar/protocol/MotorProtocol.java
new file mode 100644
index 000000000..680687e15
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MotorProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class MotorProtocol extends BaseProtocol {
+
+ public MotorProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..8ce4fe8b1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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 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.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DataConverter;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class MotorProtocolDecoder extends BaseProtocolDecoder {
+
+ public MotorProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(sentence));
+
+ String id = String.format("%08x", buf.readUnsignedIntLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.skipBytes(2); // divider
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedShortLE());
+
+ buf.skipBytes(2); // divider
+ buf.readUnsignedMediumLE(); // command
+
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 7));
+ if (BitUtil.check(flags, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ position.setLatitude(BcdUtil.readInteger(buf, 2) + BcdUtil.readInteger(buf, 6) * 0.0001 / 60);
+ position.setLongitude(BcdUtil.readInteger(buf, 4) + BcdUtil.readInteger(buf, 6) * 0.0001 / 60);
+ position.setSpeed(BcdUtil.readInteger(buf, 4) * 0.1);
+ position.setCourse(BcdUtil.readInteger(buf, 4) * 0.1);
+
+ position.setTime(new DateBuilder()
+ .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)).getDate());
+
+ position.set(Position.KEY_RSSI, BcdUtil.readInteger(buf, 2));
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Mta6Protocol.java b/src/main/java/org/traccar/protocol/Mta6Protocol.java
index 65a48808d..632a7df80 100644
--- a/src/org/traccar/protocol/Mta6Protocol.java
+++ b/src/main/java/org/traccar/protocol/Mta6Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,30 +15,24 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
+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 java.util.List;
-
public class Mta6Protocol extends BaseProtocol {
public Mta6Protocol() {
- super("mta6");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("objectDecoder", new Mta6ProtocolDecoder(
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new Mta6ProtocolDecoder(
Mta6Protocol.this, !Context.getConfig().getBoolean(getName() + ".can")));
}
});
diff --git a/src/org/traccar/protocol/Mta6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
index 0fda94eef..88419b871 100644
--- a/src/org/traccar/protocol/Mta6ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,20 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+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.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Log;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -39,6 +42,8 @@ import java.util.List;
public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Mta6ProtocolDecoder.class);
+
private final boolean simple;
public Mta6ProtocolDecoder(Protocol protocol, boolean simple) {
@@ -47,30 +52,28 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
}
private void sendContinue(Channel channel) {
- HttpResponse response = new DefaultHttpResponse(
+ FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
private void sendResponse(Channel channel, short packetId, short packetCount) {
- HttpResponse response = new DefaultHttpResponse(
- HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
-
- ChannelBuffer begin = ChannelBuffers.copiedBuffer("#ACK#", StandardCharsets.US_ASCII);
- ChannelBuffer end = ChannelBuffers.directBuffer(3);
+ ByteBuf begin = Unpooled.copiedBuffer("#ACK#", StandardCharsets.US_ASCII);
+ ByteBuf end = Unpooled.buffer(3);
end.writeByte(packetId);
end.writeByte(packetCount);
end.writeByte(0);
- response.setContent(ChannelBuffers.wrappedBuffer(begin, end));
- channel.write(response);
+ FullHttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(begin, end));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
private static class FloatReader {
private int previousFloat;
- public float readFloat(ChannelBuffer buf) {
+ public float readFloat(ByteBuf buf) {
switch (buf.getUnsignedByte(buf.readerIndex()) >> 6) {
case 0:
previousFloat = buf.readInt() << 2;
@@ -85,7 +88,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
previousFloat = (previousFloat & 0xff000000) + ((buf.readUnsignedMedium() & 0x3fffff) << 2);
break;
default:
- Log.warning(new IllegalArgumentException());
+ LOGGER.warn("MTA6 float decoding error", new IllegalArgumentException());
break;
}
return Float.intBitsToFloat(previousFloat);
@@ -97,7 +100,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
private long weekNumber;
- public Date readTime(ChannelBuffer buf) {
+ public Date readTime(ByteBuf buf) {
long weekTime = (long) (readFloat(buf) * 1000);
if (weekNumber == 0) {
weekNumber = buf.readUnsignedShort();
@@ -111,7 +114,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
}
- private List<Position> parseFormatA(DeviceSession deviceSession, ChannelBuffer buf) {
+ private List<Position> parseFormatA(DeviceSession deviceSession, ByteBuf buf) {
List<Position> positions = new LinkedList<>();
FloatReader latitudeReader = new FloatReader();
@@ -119,9 +122,8 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
TimeReader timeReader = new TimeReader();
try {
- while (buf.readable()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ while (buf.isReadable()) {
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
short flags = buf.readUnsignedByte();
@@ -193,16 +195,15 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
positions.add(position);
}
} catch (IndexOutOfBoundsException error) {
- Log.warning(error);
+ LOGGER.warn("MTA6 parsing error", error);
}
return positions;
}
- private Position parseFormatA1(DeviceSession deviceSession, ChannelBuffer buf) {
- Position position = new Position();
+ private Position parseFormatA1(DeviceSession deviceSession, ByteBuf buf) {
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
short flags = buf.readUnsignedByte();
@@ -233,7 +234,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(flags, 1)) {
position.set(Position.KEY_FUEL_CONSUMPTION, new FloatReader().readFloat(buf));
- position.set(Position.KEY_HOURS, new FloatReader().readFloat(buf));
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(new FloatReader().readFloat(buf)));
position.set("tank", buf.readUnsignedByte() * 0.4);
}
@@ -279,8 +280,8 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- HttpRequest request = (HttpRequest) msg;
- ChannelBuffer buf = request.getContent();
+ FullHttpRequest request = (FullHttpRequest) msg;
+ ByteBuf buf = request.content();
buf.skipBytes("id=".length());
int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '&');
@@ -310,7 +311,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder {
} else {
return parseFormatA(deviceSession, buf);
}
- } //else if (0x34 0x38 0x4F 0x59)
+ } // else if (0x34 0x38 0x4F 0x59)
return null;
}
diff --git a/src/main/java/org/traccar/protocol/MtxProtocol.java b/src/main/java/org/traccar/protocol/MtxProtocol.java
new file mode 100644
index 000000000..44372ce83
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MtxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class MtxProtocol extends BaseProtocol {
+
+ public MtxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MtxProtocolDecoder(MtxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/MtxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java
index d7b572670..d1207bedf 100644
--- a/src/org/traccar/protocol/MtxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MtxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +29,7 @@ import java.util.regex.Pattern;
public class MtxProtocolDecoder extends BaseProtocolDecoder {
- public MtxProtocolDecoder(MtxProtocol protocol) {
+ public MtxProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -60,7 +62,7 @@ public class MtxProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
if (channel != null) {
- channel.write("#ACK");
+ channel.writeAndFlush(new NetworkMessage("#ACK", remoteAddress));
}
Parser parser = new Parser(PATTERN, (String) msg);
@@ -68,8 +70,7 @@ public class MtxProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/MxtFrameDecoder.java b/src/main/java/org/traccar/protocol/MxtFrameDecoder.java
index b2cd6de93..d70e92da1 100644
--- a/src/org/traccar/protocol/MxtFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/MxtFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,21 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-import java.nio.ByteOrder;
-
-public class MxtFrameDecoder extends FrameDecoder {
+public class MxtFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 2) {
return null;
@@ -37,7 +33,7 @@ public class MxtFrameDecoder extends FrameDecoder {
int index = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 0x04);
if (index != -1) {
- ChannelBuffer result = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, index + 1 - buf.readerIndex());
+ ByteBuf result = Unpooled.buffer(index + 1 - buf.readerIndex());
while (buf.readerIndex() <= index) {
int b = buf.readUnsignedByte();
diff --git a/src/main/java/org/traccar/protocol/MxtProtocol.java b/src/main/java/org/traccar/protocol/MxtProtocol.java
new file mode 100644
index 000000000..dbe43fe45
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MxtProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class MxtProtocol extends BaseProtocol {
+
+ public MxtProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new MxtFrameDecoder());
+ pipeline.addLast(new MxtProtocolDecoder(MxtProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
index 6d82e4a4b..7bde85f87 100644
--- a/src/org/traccar/protocol/MxtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
@@ -27,11 +29,10 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
public class MxtProtocolDecoder extends BaseProtocolDecoder {
- public MxtProtocolDecoder(MxtProtocol protocol) {
+ public MxtProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -41,17 +42,17 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
private static void sendResponse(Channel channel, int device, long id, int crc) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
+ ByteBuf response = Unpooled.buffer();
response.writeByte(device);
response.writeByte(MSG_ACK);
- response.writeInt((int) id);
- response.writeShort(crc);
- response.writeShort(Checksum.crc16(
- Checksum.CRC16_XMODEM, response.toByteBuffer()));
+ response.writeIntLE((int) id);
+ response.writeShortLE(crc);
+ response.writeShortLE(Checksum.crc16(
+ Checksum.CRC16_XMODEM, response.nioBuffer()));
- ChannelBuffer encoded = ChannelBuffers.dynamicBuffer();
+ ByteBuf encoded = Unpooled.buffer();
encoded.writeByte(0x01); // header
- while (response.readable()) {
+ while (response.isReadable()) {
int b = response.readByte();
if (b == 0x01 || b == 0x04 || b == 0x10 || b == 0x11 || b == 0x13) {
encoded.writeByte(0x10);
@@ -59,8 +60,9 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
}
encoded.writeByte(b);
}
+ response.release();
encoded.writeByte(0x04); // ending
- channel.write(encoded);
+ channel.writeAndFlush(new NetworkMessage(encoded, channel.remoteAddress()));
}
}
@@ -68,13 +70,13 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedByte(); // start
int device = buf.readUnsignedByte(); // device descriptor
int type = buf.readUnsignedByte();
- long id = buf.readUnsignedInt();
+ long id = buf.readUnsignedIntLE();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
if (deviceSession == null) {
return null;
@@ -82,18 +84,17 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_POSITION) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
buf.readUnsignedByte(); // protocol
int infoGroups = buf.readUnsignedByte();
- position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ position.set(Position.KEY_INDEX, buf.readUnsignedShortLE());
DateBuilder dateBuilder = new DateBuilder().setDate(2000, 1, 1);
- long date = buf.readUnsignedInt();
+ long date = buf.readUnsignedIntLE();
long days = BitUtil.from(date, 6 + 6 + 5);
long hours = BitUtil.between(date, 6 + 6, 6 + 6 + 5);
@@ -105,10 +106,10 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
position.setTime(dateBuilder.getDate());
position.setValid(true);
- position.setLatitude(buf.readInt() / 1000000.0);
- position.setLongitude(buf.readInt() / 1000000.0);
+ position.setLatitude(buf.readIntLE() / 1000000.0);
+ position.setLongitude(buf.readIntLE() / 1000000.0);
- long flags = buf.readUnsignedInt();
+ long flags = buf.readUnsignedIntLE();
position.set(Position.KEY_IGNITION, BitUtil.check(flags, 0));
if (BitUtil.check(flags, 1)) {
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
@@ -116,7 +117,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_INPUT, BitUtil.between(flags, 2, 7));
position.set(Position.KEY_OUTPUT, BitUtil.between(flags, 7, 10));
position.setCourse(BitUtil.between(flags, 10, 13) * 45);
- //position.setValid(BitUtil.check(flags, 15));
+ // position.setValid(BitUtil.check(flags, 15));
position.set(Position.KEY_CHARGE, BitUtil.check(flags, 20));
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
@@ -136,34 +137,34 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_HDOP, buf.readUnsignedByte());
position.setAccuracy(buf.readUnsignedByte());
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- buf.readUnsignedShort(); // time since boot
+ buf.readUnsignedShortLE(); // time since boot
position.set(Position.KEY_POWER, buf.readUnsignedByte());
position.set(Position.PREFIX_TEMP + 1, buf.readByte());
}
if (BitUtil.check(infoGroups, 3)) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
}
if (BitUtil.check(infoGroups, 4)) {
- position.set(Position.KEY_HOURS, buf.readUnsignedInt());
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromMinutes(buf.readUnsignedIntLE()));
}
if (BitUtil.check(infoGroups, 5)) {
- buf.readUnsignedInt(); // reason
+ buf.readUnsignedIntLE(); // reason
}
if (BitUtil.check(infoGroups, 6)) {
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE());
}
if (BitUtil.check(infoGroups, 7)) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedInt()));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedIntLE()));
}
buf.readerIndex(buf.writerIndex() - 3);
- sendResponse(channel, device, id, buf.readUnsignedShort());
+ sendResponse(channel, device, id, buf.readUnsignedShortLE());
return position;
}
diff --git a/src/org/traccar/protocol/NavigilFrameDecoder.java b/src/main/java/org/traccar/protocol/NavigilFrameDecoder.java
index 34eb28941..e8b6bea52 100644
--- a/src/org/traccar/protocol/NavigilFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavigilFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,21 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class NavigilFrameDecoder extends FrameDecoder {
+public class NavigilFrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_HEADER = 20;
private static final long PREAMBLE = 0x2477F5F6;
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
// Check minimum length
if (buf.readableBytes() < MESSAGE_HEADER) {
@@ -38,18 +36,18 @@ public class NavigilFrameDecoder extends FrameDecoder {
// Check for preamble
boolean hasPreamble = false;
- if (buf.getUnsignedInt(buf.readerIndex()) == PREAMBLE) {
+ if (buf.getUnsignedIntLE(buf.readerIndex()) == PREAMBLE) {
hasPreamble = true;
}
// Check length and return buffer
- int length = buf.getUnsignedShort(buf.readerIndex() + 6);
+ int length = buf.getUnsignedShortLE(buf.readerIndex() + 6);
if (buf.readableBytes() >= length) {
if (hasPreamble) {
- buf.readUnsignedInt();
+ buf.readUnsignedIntLE();
length -= 4;
}
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/NavigilProtocol.java b/src/main/java/org/traccar/protocol/NavigilProtocol.java
new file mode 100644
index 000000000..2c946c39f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavigilProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class NavigilProtocol extends BaseProtocol {
+
+ public NavigilProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new NavigilFrameDecoder());
+ pipeline.addLast(new NavigilProtocolDecoder(NavigilProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/NavigilProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java
index 360b9c81c..db5521201 100644
--- a/src/org/traccar/protocol/NavigilProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavigilProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,23 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.util.Date;
public class NavigilProtocolDecoder extends BaseProtocolDecoder {
- public NavigilProtocolDecoder(NavigilProtocol protocol) {
+ public NavigilProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -60,109 +60,106 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
private int senderSequenceNumber = 1;
private void sendAcknowledgment(Channel channel, int sequenceNumber) {
- ChannelBuffer data = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 4);
- data.writeShort(sequenceNumber);
- data.writeShort(0); // OK
+ ByteBuf data = Unpooled.buffer(4);
+ data.writeShortLE(sequenceNumber);
+ data.writeShortLE(0); // OK
- ChannelBuffer header = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 20);
+ ByteBuf header = Unpooled.buffer(20);
header.writeByte(1); header.writeByte(0);
- header.writeShort(senderSequenceNumber++);
- header.writeShort(MSG_ACKNOWLEDGEMENT);
- header.writeShort(header.capacity() + data.capacity());
- header.writeShort(0);
- header.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, data.toByteBuffer()));
- header.writeInt(0);
- header.writeInt((int) (System.currentTimeMillis() / 1000) + LEAP_SECONDS_DELTA);
+ header.writeShortLE(senderSequenceNumber++);
+ header.writeShortLE(MSG_ACKNOWLEDGEMENT);
+ header.writeShortLE(header.capacity() + data.capacity());
+ header.writeShortLE(0);
+ header.writeShortLE(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, data.nioBuffer()));
+ header.writeIntLE(0);
+ header.writeIntLE((int) (System.currentTimeMillis() / 1000) + LEAP_SECONDS_DELTA);
if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer(header, data));
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(header, data), channel.remoteAddress()));
}
}
private Position parseUnitReport(
- DeviceSession deviceSession, ChannelBuffer buf, int sequenceNumber) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DeviceSession deviceSession, ByteBuf buf, int sequenceNumber) {
+ Position position = new Position(getProtocolName());
position.setValid(true);
position.set(Position.KEY_INDEX, sequenceNumber);
position.setDeviceId(deviceSession.getDeviceId());
- buf.readUnsignedShort(); // report trigger
- position.set(Position.KEY_FLAGS, buf.readUnsignedShort());
+ buf.readUnsignedShortLE(); // report trigger
+ position.set(Position.KEY_FLAGS, buf.readUnsignedShortLE());
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
- position.setAltitude(buf.readUnsignedShort());
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setAltitude(buf.readUnsignedShortLE());
- position.set(Position.KEY_SATELLITES, buf.readUnsignedShort());
- position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedShort());
- position.set("gpsAntennaState", buf.readUnsignedShort());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedShortLE());
+ position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedShortLE());
+ position.set("gpsAntennaState", buf.readUnsignedShortLE());
- position.setSpeed(buf.readUnsignedShort() * 0.194384);
- position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(buf.readUnsignedShortLE() * 0.194384);
+ position.setCourse(buf.readUnsignedShortLE());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- position.set(Position.KEY_DISTANCE, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_DISTANCE, buf.readUnsignedIntLE());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
- position.set(Position.KEY_CHARGE, buf.readUnsignedShort());
+ position.set(Position.KEY_CHARGE, buf.readUnsignedShortLE());
- position.setTime(convertTimestamp(buf.readUnsignedInt()));
+ position.setTime(convertTimestamp(buf.readUnsignedIntLE()));
return position;
}
private Position parseTg2Report(
- DeviceSession deviceSession, ChannelBuffer buf, int sequenceNumber) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DeviceSession deviceSession, ByteBuf buf, int sequenceNumber) {
+ Position position = new Position(getProtocolName());
position.setValid(true);
position.set(Position.KEY_INDEX, sequenceNumber);
position.setDeviceId(deviceSession.getDeviceId());
- buf.readUnsignedShort(); // report trigger
+ buf.readUnsignedShortLE(); // report trigger
buf.readUnsignedByte(); // reserved
buf.readUnsignedByte(); // assisted GPS age
- position.setTime(convertTimestamp(buf.readUnsignedInt()));
+ position.setTime(convertTimestamp(buf.readUnsignedIntLE()));
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
- position.setAltitude(buf.readUnsignedShort());
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setAltitude(buf.readUnsignedShortLE());
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
- position.setSpeed(buf.readUnsignedShort() * 0.194384);
- position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(buf.readUnsignedShortLE() * 0.194384);
+ position.setCourse(buf.readUnsignedShortLE());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- position.set("maximumSpeed", buf.readUnsignedShort());
- position.set("minimumSpeed", buf.readUnsignedShort());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set("maximumSpeed", buf.readUnsignedShortLE());
+ position.set("minimumSpeed", buf.readUnsignedShortLE());
- position.set(Position.PREFIX_IO + 1, buf.readUnsignedShort()); // VSAUT1 voltage
- position.set(Position.PREFIX_IO + 2, buf.readUnsignedShort()); // VSAUT2 voltage
- position.set(Position.PREFIX_IO + 3, buf.readUnsignedShort()); // solar voltage
+ position.set(Position.PREFIX_IO + 1, buf.readUnsignedShortLE()); // VSAUT1 voltage
+ position.set(Position.PREFIX_IO + 2, buf.readUnsignedShortLE()); // VSAUT2 voltage
+ position.set(Position.PREFIX_IO + 3, buf.readUnsignedShortLE()); // solar voltage
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
return position;
}
private Position parsePositionReport(
- DeviceSession deviceSession, ChannelBuffer buf, int sequenceNumber, long timestamp) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DeviceSession deviceSession, ByteBuf buf, int sequenceNumber, long timestamp) {
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_INDEX, sequenceNumber);
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(convertTimestamp(timestamp));
- position.setLatitude(buf.readMedium() * 0.00002);
- position.setLongitude(buf.readMedium() * 0.00002);
+ position.setLatitude(buf.readMediumLE() * 0.00002);
+ position.setLongitude(buf.readMediumLE() * 0.00002);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
position.setCourse(buf.readUnsignedByte() * 2);
@@ -176,16 +173,15 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
}
private Position parsePositionReport2(
- DeviceSession deviceSession, ChannelBuffer buf, int sequenceNumber, long timestamp) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DeviceSession deviceSession, ByteBuf buf, int sequenceNumber, long timestamp) {
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_INDEX, sequenceNumber);
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(convertTimestamp(timestamp));
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
buf.readUnsignedByte(); // report trigger
@@ -195,15 +191,14 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
position.setValid((flags & 0x80) == 0x80 && (flags & 0x40) == 0x40);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
return position;
}
private Position parseSnapshot4(
- DeviceSession deviceSession, ChannelBuffer buf, int sequenceNumber) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DeviceSession deviceSession, ByteBuf buf, int sequenceNumber) {
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_INDEX, sequenceNumber);
position.setDeviceId(deviceSession.getDeviceId());
@@ -213,36 +208,35 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // GNSS fix quality
buf.readUnsignedByte(); // GNSS assistance age
- long flags = buf.readUnsignedInt();
+ long flags = buf.readUnsignedIntLE();
position.setValid((flags & 0x0400) == 0x0400);
- position.setTime(convertTimestamp(buf.readUnsignedInt()));
+ position.setTime(convertTimestamp(buf.readUnsignedIntLE()));
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
- position.setAltitude(buf.readUnsignedShort());
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setAltitude(buf.readUnsignedShortLE());
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
- position.setSpeed(buf.readUnsignedShort() * 0.194384);
- position.setCourse(buf.readUnsignedShort() * 0.1);
+ position.setSpeed(buf.readUnsignedShortLE() * 0.194384);
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
position.set("maximumSpeed", buf.readUnsignedByte());
position.set("minimumSpeed", buf.readUnsignedByte());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
position.set(Position.PREFIX_IO + 1, buf.readUnsignedByte()); // supply voltage 1
position.set(Position.PREFIX_IO + 2, buf.readUnsignedByte()); // supply voltage 2
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
return position;
}
private Position parseTrackingData(
- DeviceSession deviceSession, ChannelBuffer buf, int sequenceNumber, long timestamp) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DeviceSession deviceSession, ByteBuf buf, int sequenceNumber, long timestamp) {
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_INDEX, sequenceNumber);
position.setDeviceId(deviceSession.getDeviceId());
@@ -253,17 +247,17 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
short flags = buf.readUnsignedByte();
position.setValid((flags & 0x01) == 0x01);
- buf.readUnsignedShort(); // duration
+ buf.readUnsignedShortLE(); // duration
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
position.setCourse(buf.readUnsignedByte() * 2.0);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
return position;
}
@@ -272,22 +266,22 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedByte(); // protocol version
buf.readUnsignedByte(); // version id
- int sequenceNumber = buf.readUnsignedShort();
- int messageId = buf.readUnsignedShort();
- buf.readUnsignedShort(); // length
- int flags = buf.readUnsignedShort();
- buf.readUnsignedShort(); // checksum
+ int sequenceNumber = buf.readUnsignedShortLE();
+ int messageId = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // length
+ int flags = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // checksum
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(buf.readUnsignedInt()));
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(buf.readUnsignedIntLE()));
if (deviceSession == null) {
return null;
}
- long timestamp = buf.readUnsignedInt();
+ long timestamp = buf.readUnsignedIntLE();
if ((flags & 0x1) == 0x0) {
sendAcknowledgment(channel, sequenceNumber);
@@ -307,11 +301,8 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder {
case MSG_TRACKING_DATA:
return parseTrackingData(deviceSession, buf, sequenceNumber, timestamp);
default:
- Log.warning(new UnsupportedOperationException(String.valueOf(messageId)));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/NavisFrameDecoder.java b/src/main/java/org/traccar/protocol/NavisFrameDecoder.java
new file mode 100644
index 000000000..8a0bb0b9a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavisFrameDecoder.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import java.nio.charset.StandardCharsets;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.BasePipelineFactory;
+
+public class NavisFrameDecoder extends BaseFrameDecoder {
+
+ private static final int NTCB_HEADER_LENGTH = 16;
+ private static final int NTCB_LENGTH_OFFSET = 12;
+ private static final int FLEX_HEADER_LENGTH = 2;
+
+ private int flexDataSize;
+
+ public void setFlexDataSize(int flexDataSize) {
+ this.flexDataSize = flexDataSize;
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.getByte(buf.readerIndex()) == 0x7F) {
+ return buf.readRetainedSlice(1); // keep alive
+ }
+
+ if (ctx != null && flexDataSize == 0) {
+ NavisProtocolDecoder protocolDecoder =
+ BasePipelineFactory.getHandler(ctx.pipeline(), NavisProtocolDecoder.class);
+ if (protocolDecoder != null) {
+ flexDataSize = protocolDecoder.getFlexDataSize();
+ }
+ }
+
+ if (flexDataSize > 0) {
+
+ if (buf.readableBytes() > FLEX_HEADER_LENGTH) {
+ int length = 0;
+ String type = buf.toString(buf.readerIndex(), 2, StandardCharsets.US_ASCII);
+ switch (type) {
+ // FLEX 1.0
+ case "~A":
+ length = flexDataSize * buf.getByte(buf.readerIndex() + FLEX_HEADER_LENGTH) + 1 + 1;
+ break;
+ case "~T":
+ length = flexDataSize + 4 + 1;
+ break;
+ case "~C":
+ length = flexDataSize + 1;
+ break;
+ // FLEX 2.0 (Extra packages)
+ case "~E":
+ length++;
+ for (int i = 0; i < buf.getByte(buf.readerIndex() + FLEX_HEADER_LENGTH); i++) {
+ if (buf.readableBytes() > FLEX_HEADER_LENGTH + length + 1) {
+ length += buf.getUnsignedShort(length + FLEX_HEADER_LENGTH) + 2;
+ } else {
+ return null;
+ }
+ }
+ length++;
+ break;
+ case "~X":
+ length = buf.getUnsignedShortLE(buf.readerIndex() + FLEX_HEADER_LENGTH) + 4 + 1;
+ break;
+ default:
+ return null;
+ }
+
+ if (buf.readableBytes() >= FLEX_HEADER_LENGTH + length) {
+ return buf.readRetainedSlice(buf.readableBytes());
+ }
+ }
+
+ } else {
+
+ if (buf.readableBytes() < NTCB_HEADER_LENGTH) {
+ return null;
+ }
+
+ int length = NTCB_HEADER_LENGTH + buf.getUnsignedShortLE(buf.readerIndex() + NTCB_LENGTH_OFFSET);
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NavisProtocol.java b/src/main/java/org/traccar/protocol/NavisProtocol.java
new file mode 100644
index 000000000..d5af6838d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavisProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class NavisProtocol extends BaseProtocol {
+
+ public NavisProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..7ba474ae0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2012 - 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 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.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;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Date;
+
+public class NavisProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final int[] FLEX_FIELDS_SIZES = {
+ 4, 2, 4, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 2, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2,
+ 4, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 2, 1, 4, 2, 2, 2, 2, 2, 1, 1, 1, 2, 4, 2, 1,
+ /* FLEX 2.0 */
+ 8, 2, 1, 16, 4, 2, 4, 37, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 12, 24, 48, 1, 1, 1, 1, 4, 4,
+ 1, 4, 2, 6, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1
+ };
+
+ private String prefix;
+ private long deviceUniqueId, serverId;
+ private int flexDataSize;
+ private int flexBitFieldSize;
+ private final byte[] flexBitField = new byte[16];
+
+ public NavisProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int F10 = 0x01;
+ public static final int F20 = 0x02;
+ public static final int F30 = 0x03;
+ public static final int F40 = 0x04;
+ public static final int F50 = 0x05;
+ public static final int F51 = 0x15;
+ public static final int F52 = 0x25;
+ public static final int F60 = 0x06;
+
+ public int getFlexDataSize() {
+ return flexDataSize;
+ }
+
+ private static boolean isFormat(int type, int... types) {
+ for (int i : types) {
+ if (type == i) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Position parseNtcbPosition(DeviceSession deviceSession, ByteBuf buf) {
+ Position position = new Position(getProtocolName());
+
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int format;
+ if (buf.getUnsignedByte(buf.readerIndex()) == 0) {
+ format = buf.readUnsignedShortLE();
+ } else {
+ format = buf.readUnsignedByte();
+ }
+ position.set("format", format);
+
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
+ position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
+
+ buf.skipBytes(6); // event time
+
+ short armedStatus = buf.readUnsignedByte();
+ if (isFormat(format, F10, F20, F30, F40, F50, F51, F52)) {
+ position.set(Position.KEY_ARMED, BitUtil.to(armedStatus, 7));
+ if (BitUtil.check(armedStatus, 7)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+ } else if (isFormat(format, F60)) {
+ position.set(Position.KEY_ARMED, BitUtil.check(armedStatus, 0));
+ if (BitUtil.check(armedStatus, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+ }
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedByte());
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+
+ if (isFormat(format, F10, F20, F30)) {
+ int output = buf.readUnsignedShortLE();
+ position.set(Position.KEY_OUTPUT, output);
+ for (int i = 0; i < 16; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(output, i));
+ }
+ } else if (isFormat(format, F50, F51, F52)) {
+ short extField = buf.readUnsignedByte();
+ position.set(Position.KEY_OUTPUT, BitUtil.to(extField, 2));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(extField, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(extField, 1));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(extField, 2));
+ } else if (isFormat(format, F40, F60)) {
+ short output = buf.readUnsignedByte();
+ position.set(Position.KEY_OUTPUT, BitUtil.to(output, 4));
+ for (int i = 0; i < 4; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(output, i));
+ }
+ }
+
+ if (isFormat(format, F10, F20, F30, F40)) {
+ int input = buf.readUnsignedShortLE();
+ position.set(Position.KEY_INPUT, input);
+ if (!isFormat(format, F40)) {
+ for (int i = 0; i < 16; i++) {
+ position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(input, i));
+ }
+ } else {
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 0));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 1));
+ position.set(Position.PREFIX_IN + 3, BitUtil.check(input, 2));
+ position.set(Position.PREFIX_IN + 4, BitUtil.check(input, 3));
+ position.set(Position.PREFIX_IN + 5, BitUtil.between(input, 4, 7));
+ position.set(Position.PREFIX_IN + 6, BitUtil.between(input, 7, 10));
+ position.set(Position.PREFIX_IN + 7, BitUtil.between(input, 10, 12));
+ position.set(Position.PREFIX_IN + 8, BitUtil.between(input, 12, 14));
+ }
+ } else if (isFormat(format, F50, F51, F52, F60)) {
+ short input = buf.readUnsignedByte();
+ position.set(Position.KEY_INPUT, input);
+ for (int i = 0; i < 8; i++) {
+ position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(input, i));
+ }
+ }
+
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+
+ if (isFormat(format, F10, F20, F30)) {
+ position.set(Position.PREFIX_TEMP + 1, buf.readShortLE());
+ }
+
+ if (isFormat(format, F10, F20, F50, F51, F52, F60)) {
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE());
+ }
+ if (isFormat(format, F60)) {
+ position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE());
+ }
+
+ // Impulse counters
+ if (isFormat(format, F20, F50, F51, F52, F60)) {
+ buf.readUnsignedIntLE();
+ buf.readUnsignedIntLE();
+ }
+
+ if (isFormat(format, F60)) {
+ // Fuel
+ buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+
+ position.set(Position.PREFIX_TEMP + 1, buf.readByte());
+ position.set(Position.PREFIX_TEMP + 2, buf.readByte());
+ position.set(Position.PREFIX_TEMP + 3, buf.readByte());
+ position.set(Position.PREFIX_TEMP + 4, buf.readByte());
+ position.set(Position.KEY_AXLE_WEIGHT, buf.readIntLE());
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE());
+ }
+
+ if (isFormat(format, F20, F50, F51, F52, F60)) {
+ int navSensorState = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(navSensorState, 1));
+ if (isFormat(format, F60)) {
+ position.set(Position.KEY_SATELLITES, BitUtil.from(navSensorState, 2));
+ }
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte() + 1, buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ if (isFormat(format, F60)) {
+ position.setLatitude(buf.readIntLE() / 600000.0);
+ position.setLongitude(buf.readIntLE() / 600000.0);
+ position.setAltitude(buf.readIntLE() * 0.1);
+ } else {
+ position.setLatitude(buf.readFloatLE() / Math.PI * 180);
+ position.setLongitude(buf.readFloatLE() / Math.PI * 180);
+ }
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+
+ position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000);
+ position.set(Position.KEY_DISTANCE, buf.readFloatLE() * 1000);
+
+ // Segment times
+ buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE();
+ }
+
+ // Other
+ if (isFormat(format, F51, F52)) {
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE();
+ buf.readByte();
+ buf.readUnsignedShortLE();
+ }
+
+ // Four temperature sensors
+ if (isFormat(format, F40, F52)) {
+ position.set(Position.PREFIX_TEMP + 1, buf.readByte());
+ position.set(Position.PREFIX_TEMP + 2, buf.readByte());
+ position.set(Position.PREFIX_TEMP + 3, buf.readByte());
+ position.set(Position.PREFIX_TEMP + 4, buf.readByte());
+ }
+
+ return position;
+ }
+
+ private Object processNtcbSingle(DeviceSession deviceSession, Channel channel, ByteBuf buf) {
+ Position position = parseNtcbPosition(deviceSession, buf);
+
+ ByteBuf response = Unpooled.buffer(7);
+ response.writeCharSequence("*<T", StandardCharsets.US_ASCII);
+ response.writeIntLE((int) position.getLong(Position.KEY_INDEX));
+ sendNtcbReply(channel, response);
+
+ return position.getFixTime() != null ? position : null;
+ }
+
+ private Object processNtcbArray(DeviceSession deviceSession, Channel channel, ByteBuf buf) {
+ List<Position> positions = new LinkedList<>();
+ int count = buf.readUnsignedByte();
+
+ for (int i = 0; i < count; i++) {
+ Position position = parseNtcbPosition(deviceSession, buf);
+ if (position.getFixTime() != null) {
+ positions.add(position);
+ }
+ }
+
+ ByteBuf response = Unpooled.buffer(7);
+ response.writeCharSequence("*<A", StandardCharsets.US_ASCII);
+ response.writeByte(count);
+ sendNtcbReply(channel, response);
+
+ if (positions.isEmpty()) {
+ return null;
+ }
+
+ return positions;
+ }
+
+ private boolean checkFlexBitfield(int index) {
+ int byteIndex = Math.floorDiv(index, 8);
+ int bitIndex = Math.floorMod(index, 8);
+ return BitUtil.check(flexBitField[byteIndex], 7 - bitIndex);
+ }
+
+ private Position parseFlexPosition(DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int status = 0;
+ short input = 0;
+ short output = 0;
+
+ for (int i = 0; i < flexBitFieldSize; i++) {
+ if (!checkFlexBitfield(i)) {
+ continue;
+ }
+
+ switch (i) {
+ case 0:
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
+ break;
+ case 1:
+ position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
+ break;
+ case 3:
+ short armedStatus = buf.readUnsignedByte();
+ position.set(Position.KEY_ARMED, BitUtil.check(armedStatus, 0));
+ if (BitUtil.check(armedStatus, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+ break;
+ case 4:
+ status = buf.readUnsignedByte();
+ position.set(Position.KEY_STATUS, status);
+ break;
+ case 5:
+ int status2 = buf.readUnsignedByte();
+ position.set(Position.KEY_STATUS, (short) (BitUtil.to(status, 8) | (status2 << 8)));
+ break;
+ case 6:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ break;
+ case 7:
+ int navSensorState = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(navSensorState, 1));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(navSensorState, 2));
+ break;
+ case 8:
+ position.setTime(new DateBuilder(new Date(buf.readUnsignedIntLE() * 1000)).getDate());
+ break;
+ case 9:
+ position.setLatitude(buf.readIntLE() / 600000.0);
+ break;
+ case 10:
+ position.setLongitude(buf.readIntLE() / 600000.0);
+ break;
+ case 11:
+ position.setAltitude(buf.readIntLE() * 0.1);
+ break;
+ case 12:
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE()));
+ break;
+ case 13:
+ position.setCourse(buf.readUnsignedShortLE());
+ break;
+ case 14:
+ position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000);
+ break;
+ case 15:
+ position.set(Position.KEY_DISTANCE, buf.readFloatLE() * 1000);
+ break;
+ case 18:
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
+ break;
+ case 19:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ position.set(Position.PREFIX_ADC + (i - 19), buf.readUnsignedShortLE());
+ break;
+ case 28:
+ input = buf.readUnsignedByte();
+ position.set(Position.KEY_INPUT, input);
+ for (int k = 0; k < 8; k++) {
+ position.set(Position.PREFIX_IN + (k + 1), BitUtil.check(input, k));
+ }
+ break;
+ case 29:
+ short input2 = buf.readUnsignedByte();
+ position.set(Position.KEY_INPUT, (short) (BitUtil.to(input, 8) | (input2 << 8)));
+ for (int k = 0; k < 8; k++) {
+ position.set(Position.PREFIX_IN + (k + 9), BitUtil.check(input2, k));
+ }
+ break;
+ case 30:
+ output = buf.readUnsignedByte();
+ position.set(Position.KEY_OUTPUT, output);
+ for (int k = 0; k < 8; k++) {
+ position.set(Position.PREFIX_OUT + (k + 1), BitUtil.check(output, k));
+ }
+ break;
+ case 31:
+ short output2 = buf.readUnsignedByte();
+ position.set(Position.KEY_OUTPUT, (short) (BitUtil.to(output, 8) | (output2 << 8)));
+ for (int k = 0; k < 8; k++) {
+ position.set(Position.PREFIX_OUT + (k + 9), BitUtil.check(output2, k));
+ }
+ break;
+ case 36:
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000);
+ break;
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ position.set(Position.PREFIX_TEMP + (i - 43), buf.readByte());
+ break;
+ case 68:
+ position.set("can-speed", buf.readUnsignedByte());
+ break;
+ // FLEX 2.0
+ case 69:
+ int satVisible = 0;
+ for (int k = 0; k < 8; k++) {
+ satVisible += buf.readUnsignedByte();
+ }
+ position.set(Position.KEY_SATELLITES_VISIBLE, satVisible);
+ break;
+ case 70:
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
+ position.set(Position.KEY_PDOP, buf.readUnsignedByte() * 0.1);
+ break;
+ default:
+ if (i < FLEX_FIELDS_SIZES.length) {
+ buf.skipBytes(FLEX_FIELDS_SIZES[i]);
+ }
+ break;
+ }
+ }
+
+ return position;
+ }
+
+ private Position parseFlex20Position(DeviceSession deviceSession, ByteBuf buf) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int length = buf.readUnsignedShort();
+ if (length <= buf.readableBytes() && buf.readUnsignedByte() == 0x0A) {
+
+ buf.readUnsignedByte(); // length of static part
+
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
+ buf.readUnsignedInt(); // event time
+
+ int navSensorState = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(navSensorState, 1));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(navSensorState, 2));
+
+ position.setTime(new DateBuilder(new Date(buf.readUnsignedIntLE() * 1000)).getDate());
+ position.setLatitude(buf.readIntLE() / 600000.0);
+ position.setLongitude(buf.readIntLE() / 600000.0);
+ position.setAltitude(buf.readIntLE() * 0.1);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+ position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000);
+
+ buf.skipBytes(length - buf.readerIndex() - 1); // skip unused part
+ }
+
+ return position;
+ }
+
+ private interface FlexPositionParser {
+ Position parsePosition(DeviceSession deviceSession, ByteBuf buf);
+ }
+
+ private Object processFlexSingle(
+ FlexPositionParser parser, String flexHeader, DeviceSession deviceSession, Channel channel, ByteBuf buf) {
+
+ if (!flexHeader.equals("~C")) {
+ buf.readUnsignedInt(); // event index
+ }
+
+ Position position = parser.parsePosition(deviceSession, buf);
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence(flexHeader, StandardCharsets.US_ASCII);
+ response.writeIntLE((int) position.getLong(Position.KEY_INDEX));
+ sendFlexReply(channel, response);
+
+ return position.getFixTime() != null ? position : null;
+ }
+
+ private Object processFlexArray(
+ FlexPositionParser parser, String flexHeader, DeviceSession deviceSession, Channel channel, ByteBuf buf) {
+
+ List<Position> positions = new LinkedList<>();
+ int count = buf.readUnsignedByte();
+
+ for (int i = 0; i < count; i++) {
+ Position position = parser.parsePosition(deviceSession, buf);
+ if (position.getFixTime() != null) {
+ positions.add(position);
+ }
+ }
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence(flexHeader, StandardCharsets.US_ASCII);
+ response.writeByte(count);
+ sendFlexReply(channel, response);
+
+ return !positions.isEmpty() ? positions : null;
+ }
+
+ private Object processFlexNegotiation(Channel channel, ByteBuf buf) {
+ if ((byte) buf.readUnsignedByte() != (byte) 0xB0) {
+ return null;
+ }
+
+ short flexProtocolVersion = buf.readUnsignedByte();
+ short flexStructVersion = buf.readUnsignedByte();
+ if ((flexProtocolVersion == 0x0A || flexProtocolVersion == 0x14)
+ && (flexStructVersion == 0x0A || flexStructVersion == 0x14)) {
+
+ flexBitFieldSize = buf.readUnsignedByte();
+ if (flexBitFieldSize > 122) {
+ return null;
+ }
+
+ buf.readBytes(flexBitField, 0, (int) Math.ceil((double) flexBitFieldSize / 8));
+
+ flexDataSize = 0;
+ for (int i = 0; i < flexBitFieldSize; i++) {
+ if (checkFlexBitfield(i)) {
+ flexDataSize += FLEX_FIELDS_SIZES[i];
+ }
+ }
+ } else {
+ flexProtocolVersion = 0x14;
+ flexStructVersion = 0x14;
+ }
+
+ ByteBuf response = Unpooled.buffer(9);
+ response.writeCharSequence("*<FLEX", StandardCharsets.US_ASCII);
+ response.writeByte(0xB0);
+ response.writeByte(flexProtocolVersion);
+ response.writeByte(flexStructVersion);
+ sendNtcbReply(channel, response);
+
+ return null;
+ }
+
+ private Object processHandshake(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+ buf.readByte(); // colon
+ if (getDeviceSession(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII)) != null) {
+ sendNtcbReply(channel, Unpooled.copiedBuffer("*<S", StandardCharsets.US_ASCII));
+ }
+ return null;
+ }
+
+ private void sendNtcbReply(Channel channel, ByteBuf data) {
+ if (channel != null) {
+ ByteBuf header = Unpooled.buffer(16);
+ header.writeCharSequence(prefix, StandardCharsets.US_ASCII);
+ header.writeIntLE((int) deviceUniqueId);
+ header.writeIntLE((int) serverId);
+ header.writeShortLE(data.readableBytes());
+ header.writeByte(Checksum.xor(data.nioBuffer()));
+ header.writeByte(Checksum.xor(header.nioBuffer()));
+
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(header, data), channel.remoteAddress()));
+ }
+ }
+
+ 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()));
+ }
+ }
+
+ private Object decodeNtcb(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ prefix = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII);
+ buf.skipBytes(prefix.length()); // prefix @NTC by default
+ serverId = buf.readUnsignedIntLE();
+ deviceUniqueId = buf.readUnsignedIntLE();
+ int length = buf.readUnsignedShortLE();
+ buf.skipBytes(2); // header and data XOR checksum
+
+ if (length == 0) {
+ return null; // keep alive message
+ }
+
+ String type = buf.toString(buf.readerIndex(), 3, StandardCharsets.US_ASCII);
+ buf.skipBytes(type.length());
+
+ if (type.equals("*>S")) {
+ return processHandshake(channel, remoteAddress, buf);
+ } else {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession != null) {
+ switch (type) {
+ case "*>A":
+ return processNtcbArray(deviceSession, channel, buf);
+ case "*>T":
+ return processNtcbSingle(deviceSession, channel, buf);
+ case "*>F":
+ buf.skipBytes(3);
+ return processFlexNegotiation(channel, buf);
+ default:
+ break;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private Object decodeFlex(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ if (buf.getByte(buf.readerIndex()) == 0x7F) {
+ return null; // keep alive
+ }
+
+ String type = buf.toString(buf.readerIndex(), 2, StandardCharsets.US_ASCII);
+ buf.skipBytes(type.length());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession != null) {
+ switch (type) {
+ // FLEX 1.0
+ case "~A":
+ return processFlexArray(this::parseFlexPosition, type, deviceSession, channel, buf);
+ case "~T":
+ case "~C":
+ return processFlexSingle(this::parseFlexPosition, type, deviceSession, channel, buf);
+ // FLEX 2.0 (extra packages)
+ case "~E":
+ return processFlexArray(this::parseFlex20Position, type, deviceSession, channel, buf);
+ case "~X":
+ return processFlexSingle(this::parseFlex20Position, type, deviceSession, channel, buf);
+ default:
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (flexDataSize > 0) {
+ return decodeFlex(channel, remoteAddress, buf);
+ } else {
+ return decodeNtcb(channel, remoteAddress, buf);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NavisetFrameDecoder.java b/src/main/java/org/traccar/protocol/NavisetFrameDecoder.java
new file mode 100644
index 000000000..e5e13b305
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavisetFrameDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BitUtil;
+
+public class NavisetFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int length = 2 + BitUtil.to(buf.getUnsignedShortLE(buf.readerIndex()), 12) + 2;
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NavisetProtocol.java b/src/main/java/org/traccar/protocol/NavisetProtocol.java
new file mode 100644
index 000000000..78755ea4d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavisetProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class NavisetProtocol extends BaseProtocol {
+
+ public NavisetProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..10d71d76c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavisetProtocolDecoder.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 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 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.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.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class NavisetProtocolDecoder extends BaseProtocolDecoder {
+
+ public NavisetProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_HEADER = 0b00;
+ public static final int MSG_DATA = 0b01;
+ public static final int MSG_RESPONSE = 0b10;
+ public static final int MSG_RESERVE = 0b11;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x01);
+ response.writeShortLE(buf.getUnsignedShortLE(buf.writerIndex() - 2));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+
+ int length = buf.readUnsignedShortLE();
+ int type = BitUtil.between(length, 14, 16);
+ buf.readUnsignedShortLE(); // device number
+
+ if (type == MSG_HEADER) {
+
+ getDeviceSession(channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString());
+
+ } else if (type == MSG_DATA) {
+
+ List<Position> positions = new LinkedList<>();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int blockMask = buf.readUnsignedByte();
+
+ while (buf.readableBytes() > 2) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, buf.readUnsignedShortLE());
+ position.set(Position.KEY_STATUS, buf.readUnsignedByte());
+ position.setValid(true);
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLatitude(buf.readUnsignedIntLE() * 0.000001);
+ position.setLongitude(buf.readUnsignedIntLE() * 0.000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE() * 0.1));
+
+ if (BitUtil.check(blockMask, 0)) {
+ int dataMask = buf.readUnsignedByte();
+ if (BitUtil.check(dataMask, 0)) {
+ int satellites = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(satellites, 7));
+ position.set(Position.KEY_SATELLITES, BitUtil.to(satellites, 7));
+ }
+ if (BitUtil.check(dataMask, 1)) {
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
+ }
+ if (BitUtil.check(dataMask, 2)) {
+ position.setAltitude(buf.readShortLE());
+ }
+ if (BitUtil.check(dataMask, 3)) {
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
+ }
+ if (BitUtil.check(dataMask, 4)) {
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+ }
+ if (BitUtil.check(dataMask, 5)) {
+ position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+ }
+ if (BitUtil.check(dataMask, 6)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ }
+ if (BitUtil.check(dataMask, 7)) {
+ buf.skipBytes(6); // accelerometer
+ }
+ }
+
+ if (BitUtil.check(blockMask, 1)) {
+ int dataMask = buf.readUnsignedByte();
+ for (int i = 0; i < 8; i++) {
+ if (BitUtil.check(dataMask, i)) {
+ position.set(Position.PREFIX_ADC + (i + 1), buf.readUnsignedShortLE());
+ }
+ }
+ }
+
+ if (BitUtil.check(blockMask, 2)) {
+ int dataMask = buf.readUnsignedByte();
+ if (BitUtil.check(dataMask, 0)) {
+ position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte());
+ }
+ if (BitUtil.check(dataMask, 1)) {
+ buf.skipBytes(6); // key code
+ }
+ if (BitUtil.check(dataMask, 2)) {
+ position.set(Position.PREFIX_TEMP + 1, (int) buf.readByte());
+ position.set(Position.PREFIX_TEMP + 2, (int) buf.readByte());
+ }
+ if (BitUtil.check(dataMask, 3)) {
+ position.set(Position.PREFIX_TEMP + 3, (int) buf.readByte());
+ position.set(Position.PREFIX_TEMP + 4, (int) buf.readByte());
+ }
+ if (BitUtil.check(dataMask, 4)) {
+ position.set(Position.PREFIX_TEMP + 5, (int) buf.readByte());
+ position.set(Position.PREFIX_TEMP + 6, (int) buf.readByte());
+ position.set(Position.PREFIX_TEMP + 7, (int) buf.readByte());
+ position.set(Position.PREFIX_TEMP + 8, (int) buf.readByte());
+ }
+ if (BitUtil.check(dataMask, 5)) {
+ position.set(Position.KEY_HOURS, buf.readUnsignedShortLE() / 60.0);
+ }
+ if (BitUtil.check(dataMask, 6)) {
+ buf.readUnsignedByte(); // extra status
+ }
+ if (BitUtil.check(dataMask, 7)) {
+ buf.readUnsignedByte(); // geofence
+ }
+ }
+
+ if (BitUtil.check(blockMask, 3)) {
+ int dataMask = buf.readUnsignedByte();
+ if (BitUtil.check(dataMask, 0)) {
+ position.set("fuel1", buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(dataMask, 1)) {
+ position.set("fuel2", buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(dataMask, 2)) {
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShortLE());
+ }
+ if (BitUtil.check(dataMask, 3)) {
+ buf.skipBytes(18);
+ }
+ if (BitUtil.check(dataMask, 4)) {
+ buf.readUnsignedByte(); // fuel 1 temperature
+ }
+ if (BitUtil.check(dataMask, 5)) {
+ buf.readUnsignedByte(); // fuel 2 temperature
+ }
+ if (BitUtil.check(dataMask, 6)) {
+ buf.readUnsignedShortLE(); // fuel 1 frequency
+ }
+ if (BitUtil.check(dataMask, 7)) {
+ buf.readUnsignedShortLE(); // fuel 2 frequency
+ }
+ }
+
+ if (BitUtil.check(blockMask, 4)) {
+ int dataMask = buf.readUnsignedByte();
+ if (BitUtil.check(dataMask, 0)) {
+ buf.readUnsignedByte(); // fuel level (percentage)
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE());
+ position.set(Position.KEY_COOLANT_TEMP, (int) buf.readByte());
+ }
+ if (BitUtil.check(dataMask, 1)) {
+ buf.readUnsignedIntLE(); // fuel consumption
+ }
+ if (BitUtil.check(dataMask, 2)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ }
+ for (int i = 3; i < 8; i++) {
+ if (BitUtil.check(dataMask, i)) {
+ buf.readUnsignedShortLE(); // axle weight
+ }
+ }
+ }
+
+ if (BitUtil.check(blockMask, 4)) {
+ int dataMask = buf.readUnsignedByte();
+ if (BitUtil.check(dataMask, 0)) {
+ buf.readUnsignedByte(); // speed
+ }
+ if (BitUtil.check(dataMask, 1)) {
+ buf.readUnsignedMediumLE(); // prefix S
+ }
+ if (BitUtil.check(dataMask, 2)) {
+ buf.readUnsignedIntLE(); // prefix P
+ }
+ if (BitUtil.check(dataMask, 3)) {
+ buf.readUnsignedIntLE(); // prefix A or B
+ }
+ if (BitUtil.check(dataMask, 4)) {
+ buf.readUnsignedShortLE(); // prefix R
+ }
+ if (BitUtil.check(dataMask, 5)) {
+ buf.skipBytes(26);
+ }
+ if (BitUtil.check(dataMask, 6)) {
+ buf.readUnsignedIntLE(); // reserved
+ }
+ if (BitUtil.check(dataMask, 7)) {
+ buf.readUnsignedIntLE(); // reserved
+ }
+ }
+
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NeosProtocol.java b/src/main/java/org/traccar/protocol/NeosProtocol.java
new file mode 100644
index 000000000..e545a9969
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NeosProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 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;
+
+public class NeosProtocol extends BaseProtocol {
+
+ public NeosProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..6b5596dba
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NeosProtocolDecoder.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+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 NeosProtocolDecoder extends BaseProtocolDecoder {
+
+ public NeosProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text(">")
+ .number("(d{8}),") // id
+ .number("d+,") // status
+ .number("([01]),") // valid
+ .number("(dd)(dd)(dd),") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([EW])")
+ .number("(d+)(dd.d+),") // longitude
+ .expression("([NS])")
+ .number("(d+)(dd.d+),") // latitude
+ .expression("[^,]*,") // response
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(d+),") // rssi
+ .expression("[^,]*,") // event data
+ .number("(d+)-") // adc
+ .number("(d+),") // battery
+ .number("0,")
+ .number("d,")
+ .number("([01]{8})") // input
+ .text("*")
+ .number("xx!")
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("$OK!", remoteAddress));
+ }
+
+ 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.setValid(parser.nextInt() > 0);
+ position.setTime(parser.nextDateTime());
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
+ position.setSpeed(parser.nextInt());
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextInt());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.KEY_INPUT, parser.nextBinInt());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/NoranProtocol.java b/src/main/java/org/traccar/protocol/NoranProtocol.java
index 7d3dc4852..3df364c30 100644
--- a/src/org/traccar/protocol/NoranProtocol.java
+++ b/src/main/java/org/traccar/protocol/NoranProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,38 +15,27 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.nio.ByteOrder;
-import java.util.List;
-
public class NoranProtocol extends BaseProtocol {
public NoranProtocol() {
- super("noran");
setSupportedDataCommands(
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_POSITION_STOP,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectEncoder", new NoranProtocolEncoder());
- pipeline.addLast("objectDecoder", new NoranProtocolDecoder(NoranProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new NoranProtocolEncoder(NoranProtocol.this));
+ pipeline.addLast(new NoranProtocolDecoder(NoranProtocol.this));
}
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
+ });
}
}
diff --git a/src/org/traccar/protocol/NoranProtocolDecoder.java b/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java
index 990f50484..53dae7fd6 100644
--- a/src/org/traccar/protocol/NoranProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NoranProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,25 +15,26 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class NoranProtocolDecoder extends BaseProtocolDecoder {
- public NoranProtocolDecoder(NoranProtocol protocol) {
+ public NoranProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -51,24 +52,22 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- buf.readUnsignedShort(); // length
- int type = buf.readUnsignedShort();
+ buf.readUnsignedShortLE(); // length
+ int type = buf.readUnsignedShortLE();
if (type == MSG_SHAKE_HAND && channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 13);
- response.writeBytes(
- ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "\r\n*KW", StandardCharsets.US_ASCII));
+ ByteBuf response = Unpooled.buffer(13);
+ response.writeCharSequence("\r\n*KW", StandardCharsets.US_ASCII);
response.writeByte(0);
- response.writeShort(response.capacity());
- response.writeShort(MSG_SHAKE_HAND_RESPONSE);
+ response.writeShortLE(response.capacity());
+ response.writeShortLE(MSG_SHAKE_HAND_RESPONSE);
response.writeByte(1); // status
- response.writeBytes(
- ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "\r\n", StandardCharsets.US_ASCII));
+ response.writeCharSequence("\r\n", StandardCharsets.US_ASCII);
- channel.write(response, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
} else if (type == MSG_UPLOAD_POSITION || type == MSG_UPLOAD_POSITION_NEW
|| type == MSG_CONTROL_RESPONSE || type == MSG_ALARM) {
@@ -80,12 +79,11 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder {
newFormat = true;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
if (type == MSG_CONTROL_RESPONSE) {
- buf.readUnsignedInt(); // GIS ip
- buf.readUnsignedInt(); // GIS port
+ buf.readUnsignedIntLE(); // GIS ip
+ buf.readUnsignedIntLE(); // GIS port
}
position.setValid(BitUtil.check(buf.readUnsignedByte(), 0));
@@ -109,17 +107,17 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder {
}
if (newFormat) {
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedInt()));
- position.setCourse(buf.readFloat());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedIntLE()));
+ position.setCourse(buf.readFloatLE());
} else {
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
- position.setCourse(buf.readUnsignedShort());
+ position.setCourse(buf.readUnsignedShortLE());
}
- position.setLongitude(buf.readFloat());
- position.setLatitude(buf.readFloat());
+ position.setLongitude(buf.readFloatLE());
+ position.setLatitude(buf.readFloatLE());
if (!newFormat) {
- long timeValue = buf.readUnsignedInt();
+ long timeValue = buf.readUnsignedIntLE();
DateBuilder dateBuilder = new DateBuilder()
.setYear((int) BitUtil.from(timeValue, 26))
.setMonth((int) BitUtil.between(timeValue, 22, 26))
@@ -130,11 +128,11 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder {
position.setTime(dateBuilder.getDate());
}
- ChannelBuffer rawId;
+ ByteBuf rawId;
if (newFormat) {
- rawId = buf.readBytes(12);
+ rawId = buf.readSlice(12);
} else {
- rawId = buf.readBytes(11);
+ rawId = buf.readSlice(11);
}
String id = rawId.toString(StandardCharsets.US_ASCII).replaceAll("[^\\p{Print}]", "");
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
@@ -145,7 +143,7 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder {
if (newFormat) {
DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
- position.setTime(dateFormat.parse(buf.readBytes(17).toString(StandardCharsets.US_ASCII)));
+ position.setTime(dateFormat.parse(buf.readSlice(17).toString(StandardCharsets.US_ASCII)));
buf.readByte();
}
@@ -153,8 +151,8 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_IO + 1, buf.readUnsignedByte());
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
} else if (type == MSG_UPLOAD_POSITION_NEW) {
- position.set(Position.PREFIX_TEMP + 1, buf.readShort());
- position.set(Position.KEY_ODOMETER, buf.readFloat());
+ position.set(Position.PREFIX_TEMP + 1, buf.readShortLE());
+ position.set(Position.KEY_ODOMETER, buf.readFloatLE());
}
return position;
diff --git a/src/org/traccar/protocol/NoranProtocolEncoder.java b/src/main/java/org/traccar/protocol/NoranProtocolEncoder.java
index 25b510a73..e02a1313c 100644
--- a/src/org/traccar/protocol/NoranProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/NoranProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,32 +15,33 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
public class NoranProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeContent(String content) {
+ public NoranProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(String content) {
- ChannelBuffer buf = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, 12 + 56);
+ ByteBuf buf = Unpooled.buffer(12 + 56);
- buf.writeBytes(
- ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "\r\n*KW", StandardCharsets.US_ASCII));
+ buf.writeCharSequence("\r\n*KW", StandardCharsets.US_ASCII);
buf.writeByte(0);
- buf.writeShort(buf.capacity());
- buf.writeShort(NoranProtocolDecoder.MSG_CONTROL);
+ buf.writeShortLE(buf.capacity());
+ buf.writeShortLE(NoranProtocolDecoder.MSG_CONTROL);
buf.writeInt(0); // gis ip
- buf.writeShort(0); // gis port
+ buf.writeShortLE(0); // gis port
buf.writeBytes(content.getBytes(StandardCharsets.US_ASCII));
buf.writerIndex(buf.writerIndex() + 50 - content.length());
- buf.writeBytes(
- ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "\r\n", StandardCharsets.US_ASCII));
+ buf.writeCharSequence("\r\n", StandardCharsets.US_ASCII);
return buf;
}
@@ -61,11 +62,8 @@ public class NoranProtocolEncoder extends BaseProtocolEncoder {
case Command.TYPE_ENGINE_RESUME:
return encodeContent("*KW,000,007,000000,1#");
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/org/traccar/protocol/NvsFrameDecoder.java b/src/main/java/org/traccar/protocol/NvsFrameDecoder.java
index 598bb1e4c..e93a58cf6 100644
--- a/src/org/traccar/protocol/NvsFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/NvsFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,18 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class NvsFrameDecoder extends FrameDecoder {
+public class NvsFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 4 + 2) {
return null;
@@ -40,7 +38,7 @@ public class NvsFrameDecoder extends FrameDecoder {
}
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
return null;
diff --git a/src/org/traccar/protocol/NvsProtocol.java b/src/main/java/org/traccar/protocol/NvsProtocol.java
index fdcb2bbcf..d319b22f3 100644
--- a/src/org/traccar/protocol/NvsProtocol.java
+++ b/src/main/java/org/traccar/protocol/NvsProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class NvsProtocol extends BaseProtocol {
public NvsProtocol() {
- super("nvs");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new NvsFrameDecoder());
- pipeline.addLast("objectDecoder", new NvsProtocolDecoder(NvsProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new NvsFrameDecoder());
+ pipeline.addLast(new NvsProtocolDecoder(NvsProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/NvsProtocolDecoder.java b/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java
index 0e82fae69..5d1159f7d 100644
--- a/src/org/traccar/protocol/NvsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NvsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -31,13 +33,14 @@ import java.util.List;
public class NvsProtocolDecoder extends BaseProtocolDecoder {
- public NvsProtocolDecoder(NvsProtocol protocol) {
+ public NvsProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private void sendResponse(Channel channel, String response) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String response) {
if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer(response, StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.copiedBuffer(response, StandardCharsets.US_ASCII), remoteAddress));
}
}
@@ -45,7 +48,7 @@ public class NvsProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (buf.getUnsignedByte(buf.readerIndex()) == 0) {
@@ -55,9 +58,9 @@ public class NvsProtocolDecoder extends BaseProtocolDecoder {
String imei = buf.toString(buf.readerIndex(), 15, StandardCharsets.US_ASCII);
if (getDeviceSession(channel, remoteAddress, imei) != null) {
- sendResponse(channel, "OK");
+ sendResponse(channel, remoteAddress, "OK");
} else {
- sendResponse(channel, "NO01");
+ sendResponse(channel, remoteAddress, "NO01");
}
} else {
@@ -76,8 +79,7 @@ public class NvsProtocolDecoder extends BaseProtocolDecoder {
int count = buf.readUnsignedByte();
for (int i = 0; i < count; i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date(buf.readUnsignedInt() * 1000));
diff --git a/src/main/java/org/traccar/protocol/NyitechProtocol.java b/src/main/java/org/traccar/protocol/NyitechProtocol.java
new file mode 100644
index 000000000..58974be5c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NyitechProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class NyitechProtocol extends BaseProtocol {
+
+ public NyitechProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..62b41a2ea
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NyitechProtocolDecoder.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.ObdDecoder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class NyitechProtocolDecoder extends BaseProtocolDecoder {
+
+ public NyitechProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final short MSG_LOGIN = 0x1001;
+ public static final short MSG_COMPREHENSIVE_LIVE = 0x2001;
+ public static final short MSG_COMPREHENSIVE_HISTORY = 0x2002;
+ public static final short MSG_ALARM = 0x2003;
+ public static final short MSG_FIXED = 0x2004;
+
+ private void decodeLocation(Position position, ByteBuf buf) {
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.to(flags, 2) > 0);
+
+ double lat = buf.readUnsignedIntLE() / 3600000.0;
+ double lon = buf.readUnsignedIntLE() / 3600000.0;
+
+ position.setLatitude(BitUtil.check(flags, 2) ? lat : -lat);
+ position.setLongitude(BitUtil.check(flags, 3) ? lon : -lon);
+
+ position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE() * 0.1);
+ position.setAltitude(buf.readShortLE() * 0.1);
+ }
+
+ private String decodeAlarm(int type) {
+ switch (type) {
+ case 0x09:
+ return Position.ALARM_ACCELERATION;
+ case 0x0a:
+ return Position.ALARM_BRAKING;
+ case 0x0b:
+ return Position.ALARM_CORNERING;
+ case 0x0e:
+ return Position.ALARM_SOS;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ buf.readUnsignedShortLE(); // length
+
+ String id = buf.readCharSequence(12, StandardCharsets.US_ASCII).toString();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type = buf.readUnsignedShortLE();
+
+ if (type != MSG_LOGIN && type != MSG_COMPREHENSIVE_LIVE
+ && type != MSG_COMPREHENSIVE_HISTORY && type != MSG_ALARM && type != MSG_FIXED) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (type == MSG_COMPREHENSIVE_LIVE || type == MSG_COMPREHENSIVE_HISTORY) {
+
+ buf.skipBytes(6); // time
+ boolean includeLocation = buf.readUnsignedByte() > 0;
+ boolean includeObd = buf.readUnsignedByte() > 0;
+ buf.readUnsignedByte(); // include sensor
+
+ if (includeLocation) {
+ decodeLocation(position, buf);
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (includeObd) {
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ int pid = buf.readUnsignedShortLE();
+ int length = buf.readUnsignedByte();
+ switch (length) {
+ case 1:
+ position.add(ObdDecoder.decodeData(pid, buf.readByte(), true));
+ break;
+ case 2:
+ position.add(ObdDecoder.decodeData(pid, buf.readShortLE(), true));
+ break;
+ case 4:
+ position.add(ObdDecoder.decodeData(pid, buf.readIntLE(), true));
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+ }
+
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.01);
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
+
+
+ } else if (type == MSG_ALARM) {
+
+ buf.readUnsignedShortLE(); // random number
+ buf.readUnsignedByte(); // tag
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ buf.readUnsignedShortLE(); // threshold
+ buf.readUnsignedShortLE(); // value
+ buf.skipBytes(6); // time
+
+ decodeLocation(position, buf);
+
+ } else if (type == MSG_FIXED) {
+
+ buf.skipBytes(6); // time
+
+ decodeLocation(position, buf);
+
+ } else {
+
+ decodeLocation(position, buf);
+
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
new file mode 100644
index 000000000..10a55759b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class ObdDongleProtocol extends BaseProtocol {
+
+ public ObdDongleProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1099, 20, 2, 3, 0));
+ pipeline.addLast(new ObdDongleProtocolDecoder(ObdDongleProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ObdDongleProtocolDecoder.java b/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java
index e5ae5e93f..1c9771ce9 100644
--- a/src/org/traccar/protocol/ObdDongleProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ObdDongleProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -30,7 +32,7 @@ import java.util.Date;
public class ObdDongleProtocolDecoder extends BaseProtocolDecoder {
- public ObdDongleProtocolDecoder(ObdDongleProtocol protocol) {
+ public ObdDongleProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -42,18 +44,19 @@ public class ObdDongleProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_TYPE_PINGRESP = 0x0D;
public static final int MSG_TYPE_DISCONNECT = 0x0E;
- private static void sendResponse(Channel channel, int type, int index, String imei, ChannelBuffer content) {
+ private static void sendResponse(Channel channel, int type, int index, String imei, ByteBuf content) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeShort(0x5555); // header
response.writeShort(index);
response.writeBytes(imei.getBytes(StandardCharsets.US_ASCII));
response.writeByte(type);
response.writeShort(content.readableBytes());
response.writeBytes(content);
+ content.release();
response.writeByte(0); // checksum
response.writeShort(0xAAAA);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -61,12 +64,12 @@ public class ObdDongleProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
int index = buf.readUnsignedShort();
- String imei = buf.readBytes(15).toString(StandardCharsets.US_ASCII);
+ String imei = buf.readSlice(15).toString(StandardCharsets.US_ASCII);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
@@ -78,7 +81,7 @@ public class ObdDongleProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_TYPE_CONNECT) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(1);
response.writeShort(0);
response.writeInt(0);
@@ -91,8 +94,7 @@ public class ObdDongleProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // event id
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date(buf.readUnsignedInt() * 1000));
@@ -113,7 +115,7 @@ public class ObdDongleProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromMph(BitUtil.from(speedCourse, 10) * 0.1));
position.setCourse(BitUtil.to(speedCourse, 10));
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(typeMajor);
response.writeByte(typeMinor);
sendResponse(channel, MSG_TYPE_PUBACK, index, imei, response);
diff --git a/src/org/traccar/protocol/OigoProtocol.java b/src/main/java/org/traccar/protocol/OigoProtocol.java
index 4b6ad0dd0..5056f68aa 100644
--- a/src/org/traccar/protocol/OigoProtocol.java
+++ b/src/main/java/org/traccar/protocol/OigoProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,25 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class OigoProtocol extends BaseProtocol {
public OigoProtocol() {
- super("oigo");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new OigoProtocolDecoder(OigoProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new OigoProtocolDecoder(OigoProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/OigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java
index 54360c932..b9cc71e8c 100644
--- a/src/org/traccar/protocol/OigoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OigoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,11 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
@@ -30,7 +33,7 @@ import java.nio.charset.StandardCharsets;
public class OigoProtocolDecoder extends BaseProtocolDecoder {
- public OigoProtocolDecoder(OigoProtocol protocol) {
+ public OigoProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -39,7 +42,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_ACKNOWLEDGEMENT = 0xE0;
- private Position decodeArMessage(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Position decodeArMessage(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
buf.skipBytes(1); // header
buf.readUnsignedShort(); // length
@@ -51,12 +54,12 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
DeviceSession deviceSession;
switch (BitUtil.to(tag, 3)) {
case 0:
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
deviceSession = getDeviceSession(channel, remoteAddress, imei);
break;
case 1:
buf.skipBytes(1);
- String meid = buf.readBytes(14).toString(StandardCharsets.US_ASCII);
+ String meid = buf.readSlice(14).toString(StandardCharsets.US_ASCII);
deviceSession = getDeviceSession(channel, remoteAddress, meid);
break;
default:
@@ -68,8 +71,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_EVENT, buf.readUnsignedByte());
@@ -160,7 +162,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
return negative ? -degrees : degrees;
}
- private Position decodeMgMessage(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Position decodeMgMessage(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
buf.readUnsignedByte(); // tag
int flags = buf.getUnsignedByte(buf.readerIndex());
@@ -170,7 +172,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // flags
deviceSession = getDeviceSession(channel, remoteAddress);
} else {
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
deviceSession = getDeviceSession(channel, remoteAddress, imei);
}
@@ -178,8 +180,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
buf.skipBytes(8); // imsi
@@ -213,11 +214,11 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, (long) (buf.readUnsignedInt() * 1609.34));
if (channel != null && BitUtil.check(flags, 7)) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(MSG_ACKNOWLEDGEMENT);
response.writeByte(index);
response.writeByte(0x00);
- channel.write(response, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
return position;
@@ -227,7 +228,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (buf.getUnsignedByte(buf.readerIndex()) == 0x7e) {
return decodeArMessage(channel, remoteAddress, buf);
diff --git a/src/main/java/org/traccar/protocol/OkoProtocol.java b/src/main/java/org/traccar/protocol/OkoProtocol.java
new file mode 100644
index 000000000..9571ccc48
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OkoProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class OkoProtocol extends BaseProtocol {
+
+ public OkoProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..5adf61494
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+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 OkoProtocolDecoder extends BaseProtocolDecoder {
+
+ public OkoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("{")
+ .number("(d{15}),").optional() // imei
+ .number("(dd)(dd)(dd).d+,") // time
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.?d*)?,") // speed
+ .number("(d+.?d*)?,") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(d+),") // satellites
+ .number("(d+.d+),") // adc
+ .number("(xx),") // event
+ .number("(d+.d+),") // power
+ .number("d,") // memory status
+ .number("(xx)") // io
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession;
+ if (parser.hasNext()) {
+ deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ } else {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ }
+ if (deviceSession == null) {
+ 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(0));
+ position.setCourse(parser.nextDouble(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_EVENT, parser.next());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java b/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
new file mode 100644
index 000000000..314f19757
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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 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 OmnicommFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 6) {
+ return null;
+ }
+
+ int endIndex = buf.getUnsignedShortLE(buf.readerIndex() + 2) + buf.readerIndex() + 6;
+ if (buf.writerIndex() < endIndex) {
+ return null;
+ }
+
+ ByteBuf result = Unpooled.buffer();
+ result.writeByte(buf.readUnsignedByte());
+ while (buf.readerIndex() < endIndex) {
+ int b = buf.readUnsignedByte();
+ if (b == 0xDB) {
+ int ext = buf.readUnsignedByte();
+ if (ext == 0xDC) {
+ result.writeByte(0xC0);
+ } else if (ext == 0xDD) {
+ result.writeByte(0xDB);
+ }
+ endIndex += 1;
+ } else {
+ result.writeByte(b);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocol.java b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
new file mode 100644
index 000000000..96660cb59
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class OmnicommProtocol extends BaseProtocol {
+
+ public OmnicommProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..cd8b74c9a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 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 com.google.protobuf.InvalidProtocolBufferException;
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.protobuf.OmnicommMessageOuterClass;
+
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class OmnicommProtocolDecoder extends BaseProtocolDecoder {
+
+ public OmnicommProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_IDENTIFICATION = 0x80;
+ public static final int MSG_ARCHIVE_INQUIRY = 0x85;
+ public static final int MSG_ARCHIVE_DATA = 0x86;
+ public static final int MSG_REMOVE_ARCHIVE_INQUIRY = 0x87;
+
+ private OmnicommMessageOuterClass.OmnicommMessage parseProto(
+ ByteBuf buf, int length) throws InvalidProtocolBufferException {
+
+ final byte[] array;
+ final int offset;
+ if (buf.hasArray()) {
+ array = buf.array();
+ offset = buf.arrayOffset() + buf.readerIndex();
+ } else {
+ array = ByteBufUtil.getBytes(buf, buf.readerIndex(), length, false);
+ offset = 0;
+ }
+ buf.skipBytes(length);
+
+ return OmnicommMessageOuterClass.OmnicommMessage
+ .getDefaultInstance().getParserForType().parseFrom(array, offset, length);
+ }
+
+ private void sendResponse(Channel channel, int type, long index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xC0);
+ response.writeByte(type);
+ response.writeShortLE(4);
+ response.writeIntLE((int) index);
+ response.writeShortLE(Checksum.crc16(Checksum.CRC16_CCITT_FALSE,
+ response.nioBuffer(1, response.writerIndex() - 1)));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // prefix
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShortLE(); // length
+
+ if (type == MSG_IDENTIFICATION) {
+
+ getDeviceSession(channel, remoteAddress, String.valueOf(buf.readUnsignedIntLE()));
+ sendResponse(channel, MSG_ARCHIVE_INQUIRY, 0);
+
+ } else if (type == MSG_ARCHIVE_DATA) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ long index = buf.readUnsignedIntLE();
+ buf.readUnsignedIntLE(); // time
+ buf.readUnsignedByte(); // priority
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.readableBytes() > 2) {
+
+ OmnicommMessageOuterClass.OmnicommMessage message = parseProto(buf, buf.readUnsignedShortLE());
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (message.hasNAV()) {
+ OmnicommMessageOuterClass.OmnicommMessage.NAV nav = message.getNAV();
+ position.setValid(true);
+ position.setTime(new Date((nav.getGPSTime() + 1230768000) * 1000L)); // from 2009-01-01 12:00
+ position.setLatitude(nav.getLAT() * 0.0000001);
+ position.setLongitude(nav.getLON() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(nav.getGPSVel() * 0.1));
+ position.setCourse(nav.getGPSDir());
+ position.setAltitude(nav.getGPSAlt() * 0.1);
+ position.set(Position.KEY_SATELLITES, nav.getGPSNSat());
+ }
+
+ if (position.getFixTime() != null) {
+ positions.add(position);
+ }
+ }
+
+ if (positions.isEmpty()) {
+ sendResponse(channel, MSG_REMOVE_ARCHIVE_INQUIRY, index + 1);
+ return null;
+ } else {
+ return positions;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
new file mode 100644
index 000000000..5ef3260c6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class OpenGtsProtocol extends BaseProtocol {
+
+ public OpenGtsProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new OpenGtsProtocolDecoder(OpenGtsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java b/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java
new file mode 100644
index 000000000..b76cbfa85
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OpenGtsProtocolDecoder.java
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.QueryStringDecoder;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public class OpenGtsProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$GPRMC,")
+ .number("(dd)(dd)(dd)(?:.d+)?,") // time (hhmmss)
+ .expression("([AV]),") // validity
+ .number("(d+)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(d+)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+),") // speed
+ .number("(d+.d+)?,") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .any()
+ .compile();
+
+ public OpenGtsProtocolDecoder(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.uri());
+ Map<String, List<String>> params = decoder.parameters();
+
+ Position position = new Position(getProtocolName());
+
+ for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+ String value = entry.getValue().get(0);
+ switch (entry.getKey()) {
+ case "id":
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case "gprmc":
+ Parser parser = new Parser(PATTERN, value);
+ if (!parser.matches()) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ 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(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+ break;
+ case "alt":
+ position.setAltitude(Double.parseDouble(value));
+ break;
+ case "batt":
+ position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (position.getDeviceId() != 0) {
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ } else {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/OrionFrameDecoder.java b/src/main/java/org/traccar/protocol/OrionFrameDecoder.java
index f7371747a..948806609 100644
--- a/src/org/traccar/protocol/OrionFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/OrionFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,18 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
-public class OrionFrameDecoder extends FrameDecoder {
+public class OrionFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
int length = 6;
@@ -51,14 +49,14 @@ public class OrionFrameDecoder extends FrameDecoder {
}
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
} else if (type == OrionProtocolDecoder.MSG_SYSLOG && buf.readableBytes() >= length + 12) {
- length += buf.getUnsignedShort(buf.readerIndex() + 8);
+ length += buf.getUnsignedShortLE(buf.readerIndex() + 8);
if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
}
diff --git a/src/main/java/org/traccar/protocol/OrionProtocol.java b/src/main/java/org/traccar/protocol/OrionProtocol.java
new file mode 100644
index 000000000..8485ae638
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrionProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class OrionProtocol extends BaseProtocol {
+
+ public OrionProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new OrionFrameDecoder());
+ pipeline.addLast(new OrionProtocolDecoder(OrionProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/OrionProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java
index c65924337..af819989e 100644
--- a/src/org/traccar/protocol/OrionProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OrionProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.model.Position;
@@ -29,20 +31,20 @@ import java.util.List;
public class OrionProtocolDecoder extends BaseProtocolDecoder {
- public OrionProtocolDecoder(OrionProtocol protocol) {
+ public OrionProtocolDecoder(Protocol protocol) {
super(protocol);
}
public static final int MSG_USERLOG = 0;
public static final int MSG_SYSLOG = 3;
- private static void sendResponse(Channel channel, ChannelBuffer buf) {
+ private static void sendResponse(Channel channel, ByteBuf buf) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(4);
+ ByteBuf response = Unpooled.buffer(4);
response.writeByte('*');
response.writeShort(buf.getUnsignedShort(buf.writerIndex() - 2));
response.writeByte(buf.getUnsignedByte(buf.writerIndex() - 3));
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -56,7 +58,7 @@ public class OrionProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
int type = buf.readUnsignedByte() & 0x0f;
@@ -79,19 +81,18 @@ public class OrionProtocolDecoder extends BaseProtocolDecoder {
for (int i = 0; i < (header & 0x0f); i++) {
- Position position = new Position();
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
position.set(Position.KEY_EVENT, buf.readUnsignedByte());
buf.readUnsignedByte(); // length
- position.set(Position.KEY_FLAGS, buf.readUnsignedShort());
+ position.set(Position.KEY_FLAGS, buf.readUnsignedShortLE());
- position.setLatitude(convertCoordinate(buf.readInt()));
- position.setLongitude(convertCoordinate(buf.readInt()));
- position.setAltitude(buf.readShort() / 10.0);
- position.setCourse(buf.readUnsignedShort());
- position.setSpeed(buf.readUnsignedShort() * 0.0539957);
+ position.setLatitude(convertCoordinate(buf.readIntLE()));
+ position.setLongitude(convertCoordinate(buf.readIntLE()));
+ position.setAltitude(buf.readShortLE() / 10.0);
+ position.setCourse(buf.readUnsignedShortLE());
+ position.setSpeed(buf.readUnsignedShortLE() * 0.0539957);
DateBuilder dateBuilder = new DateBuilder()
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocol.java b/src/main/java/org/traccar/protocol/OsmAndProtocol.java
new file mode 100644
index 000000000..d3aa2fd6f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class OsmAndProtocol extends BaseProtocol {
+
+ public OsmAndProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new OsmAndProtocolDecoder(OsmAndProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
new file mode 100644
index 000000000..3bc71de81
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.QueryStringDecoder;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DateUtil;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public OsmAndProtocolDecoder(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.uri());
+ Map<String, List<String>> params = decoder.parameters();
+ if (params.isEmpty()) {
+ decoder = new QueryStringDecoder(request.content().toString(StandardCharsets.US_ASCII), false);
+ params = decoder.parameters();
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setValid(true);
+
+ Network network = new Network();
+
+ for (Map.Entry<String, List<String>> entry : params.entrySet()) {
+ for (String value : entry.getValue()) {
+ switch (entry.getKey()) {
+ case "id":
+ case "deviceid":
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case "valid":
+ position.setValid(Boolean.parseBoolean(value) || "1".equals(value));
+ break;
+ case "timestamp":
+ try {
+ long timestamp = Long.parseLong(value);
+ if (timestamp < Integer.MAX_VALUE) {
+ timestamp *= 1000;
+ }
+ position.setTime(new Date(timestamp));
+ } catch (NumberFormatException error) {
+ if (value.contains("T")) {
+ position.setTime(DateUtil.parseDate(value));
+ } else {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ position.setTime(dateFormat.parse(value));
+ }
+ }
+ break;
+ case "lat":
+ position.setLatitude(Double.parseDouble(value));
+ break;
+ case "lon":
+ position.setLongitude(Double.parseDouble(value));
+ break;
+ case "location":
+ String[] location = value.split(",");
+ position.setLatitude(Double.parseDouble(location[0]));
+ position.setLongitude(Double.parseDouble(location[1]));
+ break;
+ case "cell":
+ String[] cell = value.split(",");
+ if (cell.length > 4) {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(cell[0]), Integer.parseInt(cell[1]),
+ Integer.parseInt(cell[2]), Integer.parseInt(cell[3]), Integer.parseInt(cell[4])));
+ } else {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(cell[0]), Integer.parseInt(cell[1]),
+ Integer.parseInt(cell[2]), Integer.parseInt(cell[3])));
+ }
+ break;
+ case "wifi":
+ String[] wifi = value.split(",");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ wifi[0].replace('-', ':'), Integer.parseInt(wifi[1])));
+ break;
+ case "speed":
+ position.setSpeed(convertSpeed(Double.parseDouble(value), "kn"));
+ break;
+ case "bearing":
+ case "heading":
+ position.setCourse(Double.parseDouble(value));
+ break;
+ case "altitude":
+ position.setAltitude(Double.parseDouble(value));
+ break;
+ case "accuracy":
+ position.setAccuracy(Double.parseDouble(value));
+ break;
+ case "hdop":
+ position.set(Position.KEY_HDOP, Double.parseDouble(value));
+ break;
+ case "batt":
+ position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value));
+ break;
+ case "driverUniqueId":
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
+ break;
+ default:
+ try {
+ position.set(entry.getKey(), Double.parseDouble(value));
+ } catch (NumberFormatException e) {
+ switch (value) {
+ case "true":
+ position.set(entry.getKey(), true);
+ break;
+ case "false":
+ position.set(entry.getKey(), false);
+ break;
+ default:
+ position.set(entry.getKey(), value);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (position.getFixTime() == null) {
+ position.setTime(new Date());
+ }
+
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
+ if (position.getLatitude() == 0 && position.getLongitude() == 0) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ if (position.getDeviceId() != 0) {
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ } else {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocol.java b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
new file mode 100644
index 000000000..c728a404d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 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;
+
+public class OutsafeProtocol extends BaseProtocol {
+
+ public OutsafeProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new OutsafeProtocolDecoder(OutsafeProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
new file mode 100644
index 000000000..5e95270e8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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 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.Protocol;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class OutsafeProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public OutsafeProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("device"));
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date());
+ position.setValid(true);
+ position.setLatitude(json.getJsonNumber("latitude").doubleValue());
+ position.setLongitude(json.getJsonNumber("longitude").doubleValue());
+ position.setAltitude(json.getJsonNumber("altitude").doubleValue());
+ position.setCourse(json.getJsonNumber("heading").intValue());
+
+ position.set(Position.KEY_RSSI, json.getJsonNumber("rssi").intValue());
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/OwnTracksProtocol.java b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
index c4a6ab163..0086371d8 100644
--- a/src/org/traccar/protocol/OwnTracksProtocol.java
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java
@@ -1,6 +1,6 @@
/*
* Copyright 2017 Jan-Piet Mens (jpmens@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -16,29 +16,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
+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 java.util.List;
-
public class OwnTracksProtocol extends BaseProtocol {
public OwnTracksProtocol() {
- super("owntracks");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("objectDecoder", new OwnTracksProtocolDecoder(OwnTracksProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new OwnTracksProtocolDecoder(OwnTracksProtocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
new file mode 100644
index 000000000..323d97fa3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2017 Jan-Piet Mens (jpmens@gmail.com)
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+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.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.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public OwnTracksProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ JsonObject root = Json.createReader(
+ new StringReader(request.content().toString(StandardCharsets.US_ASCII))).readObject();
+
+ if (!root.containsKey("_type")) {
+ sendResponse(channel, HttpResponseStatus.OK);
+ return null;
+ }
+ if (!root.getString("_type").equals("location")
+ && !root.getString("_type").equals("lwt")) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ String uniqueId;
+
+ if (root.containsKey("topic")) {
+ uniqueId = root.getString("topic");
+ if (root.containsKey("tid")) {
+ position.set("tid", root.getString("tid"));
+ }
+ } else {
+ uniqueId = root.getString("tid");
+ }
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, uniqueId);
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ if (root.getString("_type").equals("lwt")) {
+ sendResponse(channel, HttpResponseStatus.OK);
+ return null;
+ }
+
+ if (root.containsKey("t") && root.getString("t").equals("p")) {
+ sendResponse(channel, HttpResponseStatus.OK);
+ return null;
+ }
+
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date(root.getJsonNumber("tst").longValue() * 1000));
+ if (root.containsKey("sent")) {
+ position.setDeviceTime(new Date(root.getJsonNumber("sent").longValue() * 1000));
+ }
+
+ position.setValid(true);
+
+ position.setLatitude(root.getJsonNumber("lat").doubleValue());
+ position.setLongitude(root.getJsonNumber("lon").doubleValue());
+
+ if (root.containsKey("vel")) {
+ position.setSpeed(UnitsConverter.knotsFromKph(root.getInt("vel")));
+ }
+ if (root.containsKey("alt")) {
+ position.setAltitude(root.getInt("alt"));
+ }
+ if (root.containsKey("cog")) {
+ position.setCourse(root.getInt("cog"));
+ }
+ if (root.containsKey("acc")) {
+ position.setAccuracy(root.getInt("acc"));
+ }
+ if (root.containsKey("t")) {
+ String trigger = root.getString("t");
+ position.set("t", trigger);
+ Integer reportType = -1;
+ if (root.containsKey("rty")) {
+ reportType = root.getInt("rty");
+ }
+ setEventOrAlarm(position, trigger, reportType);
+ }
+ if (root.containsKey("batt")) {
+ position.set(Position.KEY_BATTERY_LEVEL, root.getInt("batt"));
+ }
+ if (root.containsKey("uext")) {
+ position.set(Position.KEY_POWER, root.getJsonNumber("uext").doubleValue());
+ }
+ if (root.containsKey("ubatt")) {
+ position.set(Position.KEY_BATTERY, root.getJsonNumber("ubatt").doubleValue());
+ }
+ if (root.containsKey("vin")) {
+ position.set(Position.KEY_VIN, root.getString("vin"));
+ }
+ if (root.containsKey("name")) {
+ position.set(Position.KEY_VIN, root.getString("name"));
+ }
+ if (root.containsKey("rpm")) {
+ position.set(Position.KEY_RPM, root.getInt("rpm"));
+ }
+ if (root.containsKey("ign")) {
+ position.set(Position.KEY_IGNITION, root.getBoolean("ign"));
+ }
+ if (root.containsKey("motion")) {
+ position.set(Position.KEY_MOTION, root.getBoolean("motion"));
+ }
+ if (root.containsKey("odometer")) {
+ position.set(Position.KEY_ODOMETER, root.getJsonNumber("odometer").doubleValue() * 1000.0);
+ }
+ if (root.containsKey("hmc")) {
+ position.set(Position.KEY_HOURS, root.getJsonNumber("hmc").doubleValue() / 3600.0);
+ }
+
+ if (root.containsKey("anum")) {
+ Integer numberOfAnalogueInputs = root.getInt("anum");
+ for (Integer i = 0; i < numberOfAnalogueInputs; i++) {
+ String indexString = String.format("%02d", i);
+ if (root.containsKey("adda-" + indexString)) {
+ position.set(Position.PREFIX_ADC + (i + 1), root.getString("adda-" + indexString));
+ }
+ if (root.containsKey("temp_c-" + indexString)) {
+ position.set(Position.PREFIX_TEMP + (i + 1),
+ root.getJsonNumber("temp_c-" + indexString).doubleValue());
+ }
+ }
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ }
+
+ private void setEventOrAlarm(Position position, String trigger, Integer reportType) {
+ switch (trigger) {
+ case "9":
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case "1":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
+ break;
+ case "i":
+ position.set(Position.KEY_IGNITION, true);
+ break;
+ case "I":
+ position.set(Position.KEY_IGNITION, false);
+ break;
+ case "E":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
+ break;
+ case "e":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case "!":
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ break;
+ case "s":
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ case "h":
+ switch (reportType) {
+ case 0:
+ case 3:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 1:
+ case 4:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 2:
+ case 5:
+ default:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
new file mode 100644
index 000000000..08991ab64
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class PacificTrackProtocol extends BaseProtocol {
+
+ public PacificTrackProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..d49a73a86
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2019 - 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.protocol;
+
+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.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
+
+ public PacificTrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static int readBitExt(ByteBuf buf) {
+ int result = 0;
+ while (buf.isReadable()) {
+ int b = buf.readUnsignedByte();
+ result <<= 7;
+ result += BitUtil.to(b, 7);
+ if (BitUtil.check(b, 7)) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readByte(); // frame start
+ readBitExt(buf); // frame control
+ readBitExt(buf); // frame length
+
+ DeviceSession deviceSession = null;
+ Position position = new Position(getProtocolName());
+
+ while (buf.isReadable()) {
+
+ int segmentId = readBitExt(buf);
+ int segmentEnd = readBitExt(buf) + buf.readerIndex();
+
+ switch (segmentId) {
+ case 0x01:
+ position.set(Position.KEY_EVENT, readBitExt(buf));
+ break;
+ case 0x10:
+ position.setValid(BitUtil.check(buf.readUnsignedByte(), 4));
+ int date = buf.readUnsignedByte();
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(2010 + BitUtil.from(date, 4), BitUtil.to(date, 4), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+ position.setLatitude(buf.readUnsignedInt() / 1000000.0 - 90.0);
+ position.setLongitude(buf.readUnsignedInt() / 1000000.0 - 180.0);
+ int speedAndCourse = buf.readUnsignedMedium();
+ position.setCourse(BitUtil.from(speedAndCourse, 12));
+ position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speedAndCourse, 12) * 0.1));
+ position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ break;
+ case 0x92:
+ while (buf.readerIndex() < segmentEnd) {
+ int field = buf.readUnsignedByte();
+ int fieldPrefix = BitUtil.from(field, 5);
+ if (fieldPrefix < 0b100) {
+ switch (BitUtil.between(field, 2, 5)) {
+ case 0b000:
+ position.set("bus", BitUtil.to(field, 2));
+ case 0b001:
+ position.set("currentGear", BitUtil.to(field, 2));
+ break;
+ default:
+ break;
+ }
+ } else if (fieldPrefix < 0b101) {
+ switch (BitUtil.to(field, 5)) {
+ case 0b00000:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ break;
+ case 0b00001:
+ position.set(Position.KEY_RPM, buf.readUnsignedByte() * 32);
+ break;
+ default:
+ buf.readUnsignedByte();
+ break;
+ }
+ } else if (fieldPrefix < 0b110) {
+ buf.readUnsignedShort();
+ } else if (fieldPrefix < 0b111) {
+ switch (BitUtil.to(field, 5)) {
+ case 0b00000:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ break;
+ case 0b00001:
+ position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 180);
+ break;
+ case 0b00010:
+ position.set("idleHours", buf.readUnsignedInt() * 180);
+ break;
+ default:
+ buf.readUnsignedInt();
+ break;
+ }
+ } else {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+ }
+ break;
+ case 0x100:
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ break;
+ default:
+ buf.readerIndex(segmentEnd);
+ break;
+ }
+ }
+
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/PathAwayProtocol.java b/src/main/java/org/traccar/protocol/PathAwayProtocol.java
index a41692750..6b5d75c5e 100644
--- a/src/org/traccar/protocol/PathAwayProtocol.java
+++ b/src/main/java/org/traccar/protocol/PathAwayProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,29 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
+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 java.util.List;
-
public class PathAwayProtocol extends BaseProtocol {
public PathAwayProtocol() {
- super("pathaway");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("objectDecoder", new PathAwayProtocolDecoder(PathAwayProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new PathAwayProtocolDecoder(PathAwayProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/PathAwayProtocolDecoder.java b/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java
index 1c4531612..02a15e34a 100644
--- a/src/org/traccar/protocol/PathAwayProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PathAwayProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelFutureListener;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
-import org.jboss.netty.handler.codec.http.QueryStringDecoder;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -34,7 +36,7 @@ import java.util.regex.Pattern;
public class PathAwayProtocolDecoder extends BaseProtocolDecoder {
- public PathAwayProtocolDecoder(PathAwayProtocol protocol) {
+ public PathAwayProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -58,22 +60,21 @@ public class PathAwayProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- HttpRequest request = (HttpRequest) msg;
- QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
+ FullHttpRequest request = (FullHttpRequest) msg;
+ QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, decoder.getParameters().get("UserName").get(0));
+ channel, remoteAddress, decoder.parameters().get("UserName").get(0));
if (deviceSession == null) {
return null;
}
- Parser parser = new Parser(PATTERN, decoder.getParameters().get("LOC").get(0));
+ Parser parser = new Parser(PATTERN, decoder.parameters().get("LOC").get(0));
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
@@ -86,8 +87,8 @@ public class PathAwayProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextDouble(0));
if (channel != null) {
- HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
- channel.write(response).addListener(ChannelFutureListener.CLOSE);
+ FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress)).addListener(ChannelFutureListener.CLOSE);
}
return position;
diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocol.java b/src/main/java/org/traccar/protocol/PiligrimProtocol.java
new file mode 100644
index 000000000..d88c1ab72
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class PiligrimProtocol extends BaseProtocol {
+
+ public PiligrimProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(16384));
+ pipeline.addLast(new PiligrimProtocolDecoder(PiligrimProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
index 9d5bb9e24..47aa86da7 100644
--- a/src/org/traccar/protocol/PiligrimProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,40 +15,40 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
-import org.jboss.netty.handler.codec.http.QueryStringDecoder;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
public class PiligrimProtocolDecoder extends BaseProtocolDecoder {
- public PiligrimProtocolDecoder(PiligrimProtocol protocol) {
+ public PiligrimProtocolDecoder(Protocol protocol) {
super(protocol);
}
private void sendResponse(Channel channel, String message) {
if (channel != null) {
- HttpResponse response = new DefaultHttpResponse(
- HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
- response.setContent(ChannelBuffers.copiedBuffer(
- ByteOrder.BIG_ENDIAN, message, StandardCharsets.US_ASCII));
- channel.write(response);
+ FullHttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
+ Unpooled.copiedBuffer(message, StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -60,8 +60,8 @@ public class PiligrimProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- HttpRequest request = (HttpRequest) msg;
- String uri = request.getUri();
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String uri = request.uri();
if (uri.startsWith("/config")) {
@@ -79,15 +79,15 @@ public class PiligrimProtocolDecoder extends BaseProtocolDecoder {
sendResponse(channel, "BINGPS: OK");
- QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
+ QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, decoder.getParameters().get("imei").get(0));
+ channel, remoteAddress, decoder.parameters().get("imei").get(0));
if (deviceSession == null) {
return null;
}
List<Position> positions = new LinkedList<>();
- ChannelBuffer buf = request.getContent();
+ ByteBuf buf = request.content();
while (buf.readableBytes() > 2) {
@@ -97,8 +97,7 @@ public class PiligrimProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_GPS || type == MSG_GPS_SENSORS) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/main/java/org/traccar/protocol/PluginProtocol.java b/src/main/java/org/traccar/protocol/PluginProtocol.java
new file mode 100644
index 000000000..d5f28da9d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PluginProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 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;
+
+public class PluginProtocol extends BaseProtocol {
+
+ public PluginProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new PluginProtocolDecoder(PluginProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
new file mode 100644
index 000000000..949c994ee
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+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 PluginProtocolDecoder extends BaseProtocolDecoder {
+
+ public PluginProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("[^0-9,]*,?")
+ .number("([^,]+),") // device id
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(-?d+.d+),") // longitude
+ .number("(-?d+.d+),") // latitude
+ .number("(d+.?d*),") // speed
+ .number("(d+),") // course
+ .number("(-?d+),") // altitude
+ .number("(-?d+),") // satellites
+ .number("d+,") // type
+ .number("(d+.?d*),") // odometer
+ .number("(d+),") // status
+ .number("(d+.?d*),") // fuel
+ .expression("[^,]*,")
+ .text("0")
+ .groupBegin()
+ .number(",(-?d+.?d*)") // temperature 1
+ .number(",(-?d+.?d*)") // temperature 2
+ .number(",d+") // oil level
+ .number(",(d+)") // rpm
+ .number(",(d+)") // obd speed
+ .number(",d+") // people up
+ .number(",d+") // people down
+ .number(",d+") // obd status
+ .number(",d+") // fuel intake air temperature
+ .number(",(d+)") // throttle
+ .number(",(d+)") // battery
+ .groupEnd("?")
+ .groupBegin()
+ .text(",+,")
+ .number("(d+),") // event
+ .groupEnd("?")
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime());
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, (long) (parser.nextDouble() * 1000));
+
+ int status = parser.nextInt();
+ position.setValid(BitUtil.check(status, 0));
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 1));
+ for (int i = 0; i < 4; i++) {
+ position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(status, 20 + i));
+ }
+ position.set(Position.KEY_STATUS, status);
+
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
+
+ if (parser.hasNext(6)) {
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble());
+ position.set(Position.PREFIX_TEMP + 2, parser.nextDouble());
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.KEY_THROTTLE, parser.nextInt() * 0.1);
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.1);
+ }
+
+ if (parser.hasNext()) {
+ int event = parser.nextInt();
+ switch (event) {
+ case 11317:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 11319:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ default:
+ break;
+
+ }
+ position.set(Position.KEY_EVENT, event);
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PretraceProtocol.java b/src/main/java/org/traccar/protocol/PretraceProtocol.java
new file mode 100644
index 000000000..9d35c1c2f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PretraceProtocol.java
@@ -0,0 +1,44 @@
+/*
+ * 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.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.model.Command;
+
+public class PretraceProtocol extends BaseProtocol {
+
+ public PretraceProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_PERIODIC);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new PretraceProtocolEncoder(PretraceProtocol.this));
+ pipeline.addLast(new PretraceProtocolDecoder(PretraceProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/PretraceProtocolDecoder.java b/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java
index 77d94068f..a19384e62 100644
--- a/src/org/traccar/protocol/PretraceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PretraceProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class PretraceProtocolDecoder extends BaseProtocolDecoder {
- public PretraceProtocolDecoder(PretraceProtocol protocol) {
+ public PretraceProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -51,8 +52,8 @@ public class PretraceProtocolDecoder extends BaseProtocolDecoder {
.number("(x)") // satellites
.number("(dd)") // hdop
.number("(dd)") // gsm
- .expression("(.{8})") // state
- .any()
+ .expression("(.{8}),&") // state
+ .expression("(.+)?") // optional data
.text("^")
.number("xx") // checksum
.compile();
@@ -71,8 +72,7 @@ public class PretraceProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setValid(parser.next().equals("A"));
@@ -90,6 +90,42 @@ public class PretraceProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_HDOP, parser.nextInt(0));
position.set(Position.KEY_RSSI, parser.nextInt(0));
+ parser.next(); // state
+
+ if (parser.hasNext()) {
+ for (String value : parser.next().split(",")) {
+ switch (value.charAt(0)) {
+ case 'P':
+ if (value.charAt(1) == '1') {
+ if (value.charAt(4) == '%') {
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(value.substring(2, 4)));
+ } else {
+ position.set(Position.KEY_BATTERY, Integer.parseInt(value.substring(2), 16) * 0.01);
+ }
+ } else {
+ position.set(Position.KEY_POWER, Integer.parseInt(value.substring(2), 16) * 0.01);
+ }
+ break;
+ case 'T':
+ double temperature = Integer.parseInt(value.substring(2), 16) * 0.25;
+ if (value.charAt(1) == '1') {
+ position.set(Position.KEY_DEVICE_TEMP, temperature);
+ } else {
+ position.set(Position.PREFIX_TEMP + (value.charAt(1) - '0'), temperature);
+ }
+ break;
+ case 'F':
+ position.set("fuel" + (value.charAt(1) - '0'), Integer.parseInt(value.substring(2), 16) * 0.01);
+ break;
+ case 'R':
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, value.substring(3));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java b/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java
new file mode 100644
index 000000000..1083a252e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PretraceProtocolEncoder.java
@@ -0,0 +1,51 @@
+/*
+ * 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.BaseProtocolEncoder;
+import org.traccar.Context;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class PretraceProtocolEncoder extends BaseProtocolEncoder {
+
+ public PretraceProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private String formatCommand(String uniqueId, String data) {
+ String content = uniqueId + data;
+ return String.format("(%s^%02X)", content, Checksum.xor(content));
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ String uniqueId = Context.getIdentityManager().getById(command.getDeviceId()).getUniqueId();
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(uniqueId, command.getString(Command.KEY_DATA));
+ case Command.TYPE_POSITION_PERIODIC:
+ return formatCommand(
+ uniqueId, String.format("D221%1$d,%1$d,,", command.getInteger(Command.KEY_FREQUENCY)));
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PricolProtocol.java b/src/main/java/org/traccar/protocol/PricolProtocol.java
new file mode 100644
index 000000000..6821cd949
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PricolProtocol.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.FixedLengthFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class PricolProtocol extends BaseProtocol {
+
+ public PricolProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FixedLengthFrameDecoder(64));
+ pipeline.addLast(new PricolProtocolDecoder(PricolProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PricolProtocolDecoder(PricolProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/PricolProtocolDecoder.java b/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java
index a33e19b90..190c68258 100644
--- a/src/org/traccar/protocol/PricolProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PricolProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,7 +31,7 @@ import java.nio.charset.StandardCharsets;
public class PricolProtocolDecoder extends BaseProtocolDecoder {
- public PricolProtocolDecoder(PricolProtocol protocol) {
+ public PricolProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -37,18 +39,17 @@ public class PricolProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedByte(); // header
DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, buf.readBytes(7).toString(StandardCharsets.US_ASCII));
+ channel, remoteAddress, buf.readSlice(7).toString(StandardCharsets.US_ASCII));
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set("eventType", buf.readUnsignedByte());
@@ -87,7 +88,8 @@ public class PricolProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, buf.readUnsignedShort());
if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer("ACK", StandardCharsets.US_ASCII), remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.copiedBuffer("ACK", StandardCharsets.US_ASCII), remoteAddress));
}
return position;
diff --git a/src/main/java/org/traccar/protocol/ProgressProtocol.java b/src/main/java/org/traccar/protocol/ProgressProtocol.java
new file mode 100644
index 000000000..aac84205d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ProgressProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+public class ProgressProtocol extends BaseProtocol {
+
+ public ProgressProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 2, 2, 4, 0, true));
+ pipeline.addLast(new ProgressProtocolDecoder(ProgressProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ProgressProtocolDecoder.java b/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java
index 1820ea926..0025cd9e7 100644
--- a/src/org/traccar/protocol/ProgressProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ProgressProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,16 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedList;
@@ -35,7 +37,7 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder {
private long lastIndex;
private long newIndex;
- public ProgressProtocolDecoder(ProgressProtocol protocol) {
+ public ProgressProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -53,12 +55,12 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder {
if (lastIndex == 0) {
lastIndex = newIndex;
} else if (newIndex > lastIndex) {
- ChannelBuffer request = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 12);
- request.writeShort(MSG_LOG_SYNC);
- request.writeShort(4);
- request.writeInt((int) lastIndex);
- request.writeInt(0);
- channel.write(request);
+ ByteBuf request = Unpooled.buffer(12);
+ request.writeShortLE(MSG_LOG_SYNC);
+ request.writeShortLE(4);
+ request.writeIntLE((int) lastIndex);
+ request.writeIntLE(0);
+ channel.writeAndFlush(new NetworkMessage(request, channel.remoteAddress()));
}
}
@@ -66,19 +68,19 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
- int type = buf.readUnsignedShort();
- buf.readUnsignedShort(); // length
+ ByteBuf buf = (ByteBuf) msg;
+ int type = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // length
if (type == MSG_IDENT || type == MSG_IDENT_FULL) {
- buf.readUnsignedInt(); // id
- int length = buf.readUnsignedShort();
+ buf.readUnsignedIntLE(); // id
+ int length = buf.readUnsignedShortLE();
buf.skipBytes(length);
- length = buf.readUnsignedShort();
+ length = buf.readUnsignedShortLE();
buf.skipBytes(length);
- length = buf.readUnsignedShort();
- String imei = buf.readBytes(length).toString(StandardCharsets.US_ASCII);
+ length = buf.readUnsignedShortLE();
+ String imei = buf.readSlice(length).toString(StandardCharsets.US_ASCII);
getDeviceSession(channel, remoteAddress, imei);
} else if (type == MSG_POINT || type == MSG_ALARM || type == MSG_LOGMSG) {
@@ -92,71 +94,69 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder {
int recordCount = 1;
if (type == MSG_LOGMSG) {
- recordCount = buf.readUnsignedShort();
+ recordCount = buf.readUnsignedShortLE();
}
for (int j = 0; j < recordCount; j++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (type == MSG_LOGMSG) {
position.set(Position.KEY_ARCHIVE, true);
- int subtype = buf.readUnsignedShort();
+ int subtype = buf.readUnsignedShortLE();
if (subtype == MSG_ALARM) {
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
}
- if (buf.readUnsignedShort() > buf.readableBytes()) {
+ if (buf.readUnsignedShortLE() > buf.readableBytes()) {
lastIndex += 1;
break; // workaround for device bug
}
- lastIndex = buf.readUnsignedInt();
+ lastIndex = buf.readUnsignedIntLE();
position.set(Position.KEY_INDEX, lastIndex);
} else {
- newIndex = buf.readUnsignedInt();
+ newIndex = buf.readUnsignedIntLE();
}
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
- position.setLatitude(buf.readInt() * 180.0 / 0x7FFFFFFF);
- position.setLongitude(buf.readInt() * 180.0 / 0x7FFFFFFF);
- position.setSpeed(buf.readUnsignedInt() * 0.01);
- position.setCourse(buf.readUnsignedShort() * 0.01);
- position.setAltitude(buf.readUnsignedShort() * 0.01);
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLatitude(buf.readIntLE() * 180.0 / 0x7FFFFFFF);
+ position.setLongitude(buf.readIntLE() * 180.0 / 0x7FFFFFFF);
+ position.setSpeed(buf.readUnsignedIntLE() * 0.01);
+ position.setCourse(buf.readUnsignedShortLE() * 0.01);
+ position.setAltitude(buf.readUnsignedShortLE() * 0.01);
int satellites = buf.readUnsignedByte();
position.setValid(satellites >= 3);
position.set(Position.KEY_SATELLITES, satellites);
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
- long extraFlags = buf.readLong();
+ long extraFlags = buf.readLongLE();
if (BitUtil.check(extraFlags, 0)) {
- int count = buf.readUnsignedShort();
+ int count = buf.readUnsignedShortLE();
for (int i = 1; i <= count; i++) {
- position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShortLE());
}
}
if (BitUtil.check(extraFlags, 1)) {
- int size = buf.readUnsignedShort();
+ int size = buf.readUnsignedShortLE();
position.set("can", buf.toString(buf.readerIndex(), size, StandardCharsets.US_ASCII));
buf.skipBytes(size);
}
if (BitUtil.check(extraFlags, 2)) {
- position.set("passenger",
- ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedShort())));
+ position.set("passenger", ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedShortLE())));
}
if (type == MSG_ALARM) {
position.set(Position.KEY_ALARM, true);
byte[] response = {(byte) 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- channel.write(ChannelBuffers.wrappedBuffer(response));
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(response), remoteAddress));
}
- buf.readUnsignedInt(); // crc
+ buf.readUnsignedIntLE(); // crc
positions.add(position);
}
diff --git a/src/main/java/org/traccar/protocol/PstFrameDecoder.java b/src/main/java/org/traccar/protocol/PstFrameDecoder.java
new file mode 100644
index 000000000..dbafd1476
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstFrameDecoder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 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 PstFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ while (buf.isReadable() && buf.getByte(buf.readerIndex()) == 0x28) {
+ buf.skipBytes(1);
+ }
+
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x29);
+ if (endIndex > 0) {
+ ByteBuf result = Unpooled.buffer(endIndex - buf.readerIndex());
+ while (buf.readerIndex() < endIndex) {
+ int b = buf.readUnsignedByte();
+ if (b == 0x27) {
+ b = buf.readUnsignedByte() ^ 0x40;
+ }
+ result.writeByte(b);
+ }
+ buf.skipBytes(1);
+ return result;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java
new file mode 100644
index 000000000..0ed9affd8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class PstProtocol extends BaseProtocol {
+
+ public PstProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PstFrameDecoder());
+ pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
new file mode 100644
index 000000000..23e2afbbd
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class PstProtocolDecoder extends BaseProtocolDecoder {
+
+ public PstProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_STATUS = 0x05;
+
+ private Date readDate(ByteBuf buf) {
+ long value = buf.readUnsignedInt();
+ return new DateBuilder()
+ .setYear((int) BitUtil.between(value, 26, 32))
+ .setMonth((int) BitUtil.between(value, 22, 26))
+ .setDay((int) BitUtil.between(value, 17, 22))
+ .setHour((int) BitUtil.between(value, 12, 17))
+ .setMinute((int) BitUtil.between(value, 6, 12))
+ .setSecond((int) BitUtil.between(value, 0, 6)).getDate();
+ }
+
+ private double readCoordinate(ByteBuf buf) {
+ long value = buf.readUnsignedInt();
+ int sign = BitUtil.check(value, 31) ? -1 : 1;
+ value = BitUtil.to(value, 31);
+ return sign * (BitUtil.from(value, 16) + BitUtil.to(value, 16) * 0.00001) / 60;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ String id = String.valueOf(buf.readUnsignedInt());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // version
+ buf.readUnsignedInt(); // index
+
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_STATUS) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(readDate(buf));
+
+ buf.readUnsignedByte();
+
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+
+ int tag = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+
+ switch (tag) {
+ case 0x0D:
+ int battery = buf.readUnsignedByte();
+ if (battery <= 20) {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 5);
+ }
+ break;
+ case 0x10:
+ position.setFixTime(readDate(buf));
+ position.setLatitude(readCoordinate(buf));
+ position.setLongitude(readCoordinate(buf));
+ position.setSpeed(buf.readUnsignedByte());
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readShort());
+ buf.readUnsignedInt(); // gps condition
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+
+ return position.getFixTime() != null ? position : null;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt215Protocol.java b/src/main/java/org/traccar/protocol/Pt215Protocol.java
new file mode 100644
index 000000000..09bd9b121
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt215Protocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Pt215Protocol extends BaseProtocol {
+
+ public Pt215Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..48ce7dede
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class Pt215ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Pt215ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_HEARTBEAT = 0x08;
+ public static final int MSG_GPS_REALTIME = 0x10;
+ public static final int MSG_GPS_OFFLINE = 0x11;
+ public static final int MSG_STATUS = 0x13;
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, int type, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte('X');
+ response.writeByte('X');
+ response.writeByte(content != null ? 1 + content.readableBytes() : 1);
+ response.writeByte(type);
+ if (content != null) {
+ response.writeBytes(content);
+ content.release();
+ }
+ response.writeByte('\r');
+ response.writeByte('\n');
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ buf.readUnsignedByte(); // length
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_LOGIN) {
+
+ getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(buf.readSlice(8)).substring(1));
+ sendResponse(channel, remoteAddress, type, null);
+
+ } else if (type == MSG_GPS_OFFLINE || type == MSG_GPS_REALTIME) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ sendResponse(channel, remoteAddress, type, buf.retainedSlice(buf.readerIndex(), 6));
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+ double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+
+ 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);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt3000Protocol.java b/src/main/java/org/traccar/protocol/Pt3000Protocol.java
new file mode 100644
index 000000000..1ad0026a3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt3000Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Pt3000Protocol extends BaseProtocol {
+
+ public Pt3000Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, 'd')); // probably wrong
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Pt3000ProtocolDecoder(Pt3000Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Pt3000ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java
index c36be7976..e7f9e062a 100644
--- a/src/org/traccar/protocol/Pt3000ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt3000ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class Pt3000ProtocolDecoder extends BaseProtocolDecoder {
- public Pt3000ProtocolDecoder(Pt3000Protocol protocol) {
+ public Pt3000ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -56,8 +57,7 @@ public class Pt3000ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/Pt502FrameDecoder.java b/src/main/java/org/traccar/protocol/Pt502FrameDecoder.java
new file mode 100644
index 000000000..316cd987f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt502FrameDecoder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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 Pt502FrameDecoder extends BaseFrameDecoder {
+
+ private static final int BINARY_HEADER = 5;
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ if (buf.getUnsignedByte(buf.readerIndex()) == 0xbf
+ && buf.toString(buf.readerIndex() + BINARY_HEADER, 4, StandardCharsets.US_ASCII).equals("$PHD")) {
+
+ int length = buf.getUnsignedShortLE(buf.readerIndex() + 3);
+ if (buf.readableBytes() >= length) {
+ buf.skipBytes(BINARY_HEADER);
+ ByteBuf result = buf.readRetainedSlice(length - BINARY_HEADER - 2);
+ buf.skipBytes(2); // line break
+ return result;
+ }
+
+ } else {
+
+ if (buf.getUnsignedByte(buf.readerIndex()) == 0xbf) {
+ buf.skipBytes(BINARY_HEADER);
+ }
+
+ int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r');
+ if (index < 0) {
+ index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\n');
+ }
+
+ if (index > 0) {
+ ByteBuf result = buf.readRetainedSlice(index - buf.readerIndex());
+ while (buf.isReadable()
+ && (buf.getByte(buf.readerIndex()) == '\r' || buf.getByte(buf.readerIndex()) == '\n')) {
+ buf.skipBytes(1);
+ }
+ return result;
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt502Protocol.java b/src/main/java/org/traccar/protocol/Pt502Protocol.java
new file mode 100644
index 000000000..56444fb42
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt502Protocol.java
@@ -0,0 +1,44 @@
+/*
+ * 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class Pt502Protocol extends BaseProtocol {
+
+ public Pt502Protocol() {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Pt502FrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Pt502ProtocolEncoder(Pt502Protocol.this));
+ pipeline.addLast(new Pt502ProtocolDecoder(Pt502Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
index 1d976dcd5..0afec67ad 100644
--- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
@@ -1,148 +1,212 @@
-/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2012 Luis Parada (luis.parada@gmail.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.DateBuilder;
-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 Pt502ProtocolDecoder extends BaseProtocolDecoder {
-
- private static final int MAX_CHUNK_SIZE = 960;
-
- private byte[] photo;
-
- public Pt502ProtocolDecoder(Pt502Protocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .any().text("$")
- .expression("([^,]+),") // type
- .number("(d+),") // id
- .number("(dd)(dd)(dd).(ddd),") // time (hhmmss.sss)
- .expression("([AV]),") // validity
- .number("(d+)(dd.dddd),") // latitude
- .expression("([NS]),")
- .number("(d+)(dd.dddd),") // longitude
- .expression("([EW]),")
- .number("(d+.d+)?,") // speed
- .number("(d+.d+)?,") // course
- .number("(dd)(dd)(dd),,,") // date (ddmmyy)
- .expression("./")
- .expression("([01])+,") // input
- .expression("([01])+/") // output
- .expression("([^/]+)?/") // adc
- .number("(d+)") // odometer
- .expression("/([^/]+)?/") // rfid
- .number("(xxx)").optional(2) // state
- .any()
- .compile();
-
- private String decodeAlarm(String value) {
- switch (value) {
- case "IN1":
- return Position.ALARM_SOS;
- case "GOF":
- return Position.ALARM_GEOFENCE;
- case "TOW":
- return Position.ALARM_TOW;
- case "HDA":
- return Position.ALARM_ACCELERATION;
- case "HDB":
- return Position.ALARM_BRAKING;
- case "FDA":
- return Position.ALARM_FATIGUE_DRIVING;
- case "SKA":
- return Position.ALARM_VIBRATION;
- case "PMA":
- return Position.ALARM_MOVEMENT;
- 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;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- String type = parser.next();
-
- if (type.startsWith("PHO") && channel != null) {
- photo = new byte[Integer.parseInt(type.substring(3))];
- channel.write("#PHD0," + Math.min(photo.length, MAX_CHUNK_SIZE) + "\r\n");
- }
-
- position.set(Position.KEY_ALARM, decodeAlarm(type));
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
-
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
-
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
-
- position.set(Position.KEY_INPUT, parser.next());
- position.set(Position.KEY_OUTPUT, parser.next());
-
- if (parser.hasNext()) {
- String[] values = parser.next().split(",");
- for (int i = 0; i < values.length; i++) {
- position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(values[i], 16));
- }
- }
-
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
- position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
-
- if (parser.hasNext()) {
- int value = parser.nextHexInt(0);
- position.set(Position.KEY_BATTERY, value >> 8);
- position.set(Position.KEY_RSSI, (value >> 4) & 0xf);
- position.set(Position.KEY_SATELLITES, value & 0xf);
- }
-
- return position;
- }
-
-}
+/*
+ * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 Luis Parada (luis.parada@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import 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.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final int MAX_CHUNK_SIZE = 960;
+
+ private ByteBuf photo;
+
+ public Pt502ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .any().text("$")
+ .expression("([^,]+),") // type
+ .number("(d+),") // id
+ .number("(dd)(dd)(dd).(ddd),") // time (hhmmss.sss)
+ .expression("([AV]),") // validity
+ .number("(d+)(dd.dddd),") // latitude
+ .expression("([NS]),")
+ .number("(d+)(dd.dddd),") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+)?,") // speed
+ .number("(d+.d+)?,") // course
+ .number("(dd)(dd)(dd),,,") // date (ddmmyy)
+ .expression("./")
+ .expression("([01])+,") // input
+ .expression("([01])+/") // output
+ .expression("([^/]+)?/") // adc
+ .number("(d+)") // odometer
+ .expression("/([^/]+)?/") // rfid
+ .number("(xxx)").optional(2) // state
+ .any()
+ .compile();
+
+ private String decodeAlarm(String value) {
+ switch (value) {
+ case "IN1":
+ return Position.ALARM_SOS;
+ case "GOF":
+ return Position.ALARM_GEOFENCE;
+ case "TOW":
+ return Position.ALARM_TOW;
+ case "HDA":
+ return Position.ALARM_ACCELERATION;
+ case "HDB":
+ return Position.ALARM_BRAKING;
+ case "FDA":
+ return Position.ALARM_FATIGUE_DRIVING;
+ case "SKA":
+ return Position.ALARM_VIBRATION;
+ case "PMA":
+ return Position.ALARM_MOVEMENT;
+ case "CPA":
+ return Position.ALARM_POWER_CUT;
+ default:
+ return null;
+ }
+ }
+
+ private Position decodePosition(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.set(Position.KEY_ALARM, decodeAlarm(parser.next()));
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ position.setTime(dateBuilder.getDate());
+
+ position.set(Position.KEY_INPUT, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.next());
+
+ if (parser.hasNext()) {
+ String[] values = parser.next().split(",");
+ for (int i = 0; i < values.length; i++) {
+ position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(values[i], 16));
+ }
+ }
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+
+ if (parser.hasNext()) {
+ int value = parser.nextHexInt(0);
+ position.set(Position.KEY_BATTERY, value >> 8);
+ position.set(Position.KEY_RSSI, (value >> 4) & 0xf);
+ position.set(Position.KEY_SATELLITES, value & 0xf);
+ }
+
+ return position;
+ }
+
+ private void requestPhotoFragment(Channel channel) {
+ if (channel != null) {
+ int offset = photo.writerIndex();
+ int size = Math.min(photo.writableBytes(), MAX_CHUNK_SIZE);
+ channel.writeAndFlush(new NetworkMessage("#PHD" + offset + "," + size + "\r\n", channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int typeEndIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
+ String type = buf.toString(buf.readerIndex(), typeEndIndex - buf.readerIndex(), StandardCharsets.US_ASCII);
+
+ if (type.startsWith("$PHD")) {
+
+ int dataIndex = buf.indexOf(typeEndIndex + 1, buf.writerIndex(), (byte) ',') + 1;
+ buf.readerIndex(dataIndex);
+
+ if (photo != null) {
+
+ photo.writeBytes(buf.readSlice(buf.readableBytes()));
+
+ if (photo.writableBytes() > 0) {
+
+ requestPhotoFragment(channel);
+
+ } 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"));
+ photo.release();
+ photo = null;
+
+ return position;
+
+ }
+
+ }
+
+ } else {
+
+ if (type.startsWith("$PHO")) {
+ int size = Integer.parseInt(type.split("-")[0].substring(4));
+ if (size > 0) {
+ photo = Unpooled.buffer(size);
+ requestPhotoFragment(channel);
+ }
+ }
+
+ return decodePosition(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Pt502ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
index bd56e306a..364ecae86 100644
--- a/src/org/traccar/protocol/Pt502ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -18,11 +18,15 @@ package org.traccar.protocol;
import java.util.TimeZone;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class Pt502ProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
+ public Pt502ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
public String formatValue(String key, Object value) {
if (key.equals(Command.KEY_TIMEZONE)) {
@@ -42,21 +46,18 @@ public class Pt502ProtocolEncoder extends StringProtocolEncoder implements Strin
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}\r\n", Command.KEY_DATA);
+ return formatCommand(command, "%s\r\n", Command.KEY_DATA);
case Command.TYPE_OUTPUT_CONTROL:
- return formatCommand(command, "#OPC{%s},{%s}\r\n", Command.KEY_INDEX, Command.KEY_DATA);
+ return formatCommand(command, "#OPC%s,%s\r\n", Command.KEY_INDEX, Command.KEY_DATA);
case Command.TYPE_SET_TIMEZONE:
- return formatCommand(command, "#TMZ{%s}\r\n", Command.KEY_TIMEZONE);
+ return formatCommand(command, "#TMZ%s\r\n", Command.KEY_TIMEZONE);
case Command.TYPE_ALARM_SPEED:
- return formatCommand(command, "#SPD{%s}\r\n", Command.KEY_DATA);
+ return formatCommand(command, "#SPD%s\r\n", Command.KEY_DATA);
case Command.TYPE_REQUEST_PHOTO:
return formatCommand(command, "#PHO\r\n");
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/Pt60Protocol.java b/src/main/java/org/traccar/protocol/Pt60Protocol.java
new file mode 100644
index 000000000..c502426c5
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt60Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class Pt60Protocol extends BaseProtocol {
+
+ public Pt60Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "@R#@", "@E#@"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Pt60ProtocolDecoder(Pt60Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java
new file mode 100644
index 000000000..6a3fe2734
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt60ProtocolDecoder.java
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.regex.Pattern;
+
+public class Pt60ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Pt60ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_G_TRACK = 6;
+ public static final int MSG_G_STEP_COUNT = 13;
+ public static final int MSG_G_HEART_RATE = 14;
+
+ public static final int MSG_B_POSITION = 1;
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("@(.)#@[,|]") // header
+ .number("V?dd[,|]") // protocol version
+ .number("(d+)[,|]") // type
+ .number("(d+)[,|]") // imei
+ .number("d+[,|]") // imsi
+ .groupBegin()
+ .expression("[^,|]+[,|]").optional() // firmware version
+ .number("[01][,|]") // state
+ .number("d+[,|]") // battery
+ .groupEnd("?")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)[,|]") // time (hhmmss)
+ .expression("(.*)") // data
+ .expression("[,|]")
+ .compile();
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String format, int type, String imei) {
+ if (channel != null) {
+ String message;
+ String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
+ if (format.equals("G")) {
+ message = String.format("@G#@,V01,38,%s,@R#@", time);
+ } else {
+ message = String.format("@B#@|01|%03d|%s|0|%s|@E#@", type + 1, imei, time);
+ }
+ channel.writeAndFlush(new NetworkMessage(message, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String format = parser.next();
+ int type = parser.nextInt();
+ String imei = parser.next();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ sendResponse(channel, remoteAddress, format, type, imei);
+
+ if (format.equals("G")) {
+
+ if (type != MSG_G_TRACK && type != MSG_G_STEP_COUNT && type != MSG_G_HEART_RATE) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setDeviceTime(parser.nextDateTime());
+
+ String[] values = parser.next().split(",");
+
+ if (type == MSG_G_TRACK) {
+
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+
+ String[] coordinates = values[0].split(";");
+ position.setLatitude(Double.parseDouble(coordinates[0]));
+ position.setLongitude(Double.parseDouble(coordinates[1]));
+
+ } else {
+
+ getLastLocation(position, position.getDeviceTime());
+
+ switch (type) {
+ case MSG_G_STEP_COUNT:
+ position.set(Position.KEY_STEPS, Integer.parseInt(values[0]));
+ break;
+ case MSG_G_HEART_RATE:
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[0]));
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[1]));
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ return position;
+
+ } else {
+
+ if (type != MSG_B_POSITION) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setDeviceTime(parser.nextDateTime());
+
+ String[] values = parser.next().split("\\|");
+
+ if (Integer.parseInt(values[values.length - 1]) == 2) {
+
+ getLastLocation(position, position.getDeviceTime());
+
+ Network network = new Network();
+
+ for (int i = 0; i < values.length - 1; i++) {
+ String[] cellValues = values[i].split(",");
+ CellTower tower = new CellTower();
+ tower.setCellId(Long.parseLong(cellValues[0]));
+ tower.setLocationAreaCode(Integer.parseInt(cellValues[1]));
+ tower.setMobileNetworkCode(Integer.parseInt(cellValues[2]));
+ tower.setMobileCountryCode(Integer.parseInt(cellValues[3]));
+ tower.setSignalStrength(Integer.parseInt(cellValues[4]));
+ network.addCellTower(tower);
+ }
+
+ position.setNetwork(network);
+
+
+ } else {
+
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+
+ position.setLatitude(Double.parseDouble(values[0]));
+ position.setLongitude(Double.parseDouble(values[1]));
+
+ }
+
+ return position;
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
new file mode 100644
index 000000000..c9db10610
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 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;
+
+public class RaceDynamicsProtocol extends BaseProtocol {
+
+ public RaceDynamicsProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1500));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new RaceDynamicsProtocolDecoder(RaceDynamicsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
new file mode 100644
index 000000000..f441bf8ed
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class RaceDynamicsProtocolDecoder extends BaseProtocolDecoder {
+
+ public RaceDynamicsProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 12;
+ public static final int MSG_LOCATION = 15;
+
+ private static final Pattern PATTERN_LOGIN = new PatternBuilder()
+ .text("$GPRMC,")
+ .number("d+,") // type
+ .number("d{6},") // date
+ .number("d{6},") // time
+ .number("(d{15}),")
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+),") // speed
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(-?d+),") // altitude
+ .number("(d+),") // satellites
+ .number("([01]),") // ignition
+ .number("(d+),") // index
+ .text("%,")
+ .number("([^,]+),") // ibutton
+ .number("d+,") // acceleration
+ .number("d+,") // deceleration
+ .number("[01],") // cruise control
+ .number("[01],") // seat belt
+ .number("[01],") // wrong ibutton
+ .number("(d+),") // power
+ .number("[01],") // power status
+ .number("(d+),") // battery
+ .number("([01]),") // panic
+ .number("d+,")
+ .number("d+,")
+ .number("(d),") // overspeed
+ .number("d+,") // speed limit
+ .number("d+,") // tachometer
+ .number("d+,d+,d+,") // aux
+ .number("d+,") // geofence id
+ .number("d+,") // road speed type
+ .number("d+,") // ibutton count
+ .number("(d),") // overdriver alert
+ .any()
+ .compile();
+
+ private String imei;
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int type) {
+ if (channel != null) {
+ String response = String.format(
+ "$GPRMC,%1$d,%2$td%2$tm%2$ty,%2$tH%2$tM%2$tS,%3$s,\r\n", type, new Date(), imei);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ int type = Integer.parseInt(sentence.substring(7, 9));
+
+ if (type == MSG_LOGIN) {
+
+ Parser parser = new Parser(PATTERN_LOGIN, sentence);
+ if (parser.matches()) {
+ imei = parser.next();
+ getDeviceSession(channel, remoteAddress, imei);
+ sendResponse(channel, remoteAddress, type);
+ }
+
+ } else if (type == MSG_LOCATION) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ for (String data : sentence.substring(17, sentence.length() - 3).split(",#,#,")) {
+ Parser parser = new Parser(PATTERN_LOCATION, data);
+ if (parser.matches()) {
+
+ 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());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.setAltitude(parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ position.set(Position.KEY_INDEX, parser.nextInt());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
+ position.set(Position.KEY_ALARM, parser.nextInt() > 0 ? Position.ALARM_SOS : null);
+ position.set(Position.KEY_ALARM, parser.nextInt() > 0 ? Position.ALARM_OVERSPEED : null);
+
+ int overDriver = parser.nextInt();
+ if (overDriver > 0) {
+ position.set("overDriver", overDriver);
+ }
+
+ positions.add(position);
+
+ }
+ }
+
+ sendResponse(channel, remoteAddress, type);
+
+ return positions;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RadarProtocol.java b/src/main/java/org/traccar/protocol/RadarProtocol.java
new file mode 100644
index 000000000..9783778f0
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RadarProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class RadarProtocol extends BaseProtocol {
+
+ public RadarProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..b56a081c7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+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.BitSet;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class RadarProtocolDecoder extends BaseProtocolDecoder {
+
+ public RadarProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_TRACKING = 0x4C;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // product id
+ buf.readUnsignedByte(); // product version
+
+ String serialNumber = String.valueOf(buf.readUnsignedInt());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, serialNumber);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // index
+ buf.readUnsignedInt(); // timestamp
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // length
+
+ if (type == MSG_TRACKING) {
+
+ buf.readUnsignedShort(); // memory count
+ buf.readUnsignedShort(); // memory index
+ int count = buf.readUnsignedShort();
+ buf.readUnsignedShort(); // first index
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int index = 0; index < count; index++) {
+
+ Position position = new Position(getProtocolName());
+
+ position.set(Position.KEY_EVENT, buf.readUnsignedShort());
+
+ int maskLength = buf.readUnsignedByte();
+ BitSet mask = BitSet.valueOf(buf.nioBuffer(buf.readerIndex(), maskLength));
+ buf.skipBytes(maskLength);
+
+ buf.readUnsignedShort(); // length
+
+ if (mask.get(0)) {
+ position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
+ }
+ if (mask.get(1)) {
+ position.setFixTime(new Date(buf.readUnsignedInt() * 1000));
+ }
+ if (mask.get(2)) {
+ position.setLatitude(buf.readInt() / 360000.0);
+ }
+ if (mask.get(3)) {
+ position.setLongitude(buf.readInt() / 360000.0);
+ }
+ if (mask.get(4)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ }
+ if (mask.get(5)) {
+ position.setCourse(buf.readUnsignedShort() * 0.1);
+ }
+ if (mask.get(6)) {
+ position.setAltitude(buf.readShort());
+ }
+ if (mask.get(7)) {
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 0));
+ position.set(Position.KEY_SATELLITES, BitUtil.from(flags, 4));
+ }
+ if (mask.get(8)) {
+ long flags = buf.readUnsignedInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(flags, 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(flags, 1));
+ position.set(Position.KEY_MOTION, BitUtil.check(flags, 2));
+ for (int i = 0; i < 3; i++) {
+ position.set(Position.PREFIX_IN + i, BitUtil.check(flags, 4 + i));
+ }
+ }
+ if (mask.get(9)) {
+ int flags = buf.readUnsignedShort();
+ position.set(Position.KEY_BLOCKED, BitUtil.check(flags, 0));
+ position.set(Position.PREFIX_IN + 0, BitUtil.check(flags, 4));
+ }
+ for (int i = 10; i <= 14; i++) {
+ if (mask.get(i)) {
+ buf.readUnsignedShort(); // reserved
+ }
+ }
+ if (mask.get(15)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ }
+ if (mask.get(16)) {
+ buf.readUnsignedInt(); // reserved
+ }
+ for (int i = 17; i <= 27; i++) {
+ if (mask.get(i)) {
+ buf.readUnsignedByte(); // reserved
+ }
+ }
+ for (int i = 28; i <= 37; i += 2) {
+ if (mask.get(i)) {
+ buf.skipBytes(12); // reserved
+ }
+ if (mask.get(i + 1)) {
+ buf.readUnsignedByte(); // reserved
+ }
+ }
+ if (mask.get(38)) {
+ buf.skipBytes(6); // driver id
+ }
+ if (mask.get(39)) {
+ buf.readUnsignedShort(); // hardware problems
+ }
+ if (mask.get(40)) {
+ buf.readShort(); // acceleration x
+ }
+ if (mask.get(41)) {
+ buf.readShort(); // acceleration y
+ }
+ if (mask.get(42)) {
+ buf.readShort(); // acceleration z
+ }
+ if (mask.get(43)) {
+ buf.skipBytes(10); // operator
+ }
+ if (mask.get(44)) {
+ buf.readUnsignedShort(); // power
+ }
+ for (int i = 45; i <= 49; i++) {
+ if (mask.get(i)) {
+ buf.readUnsignedByte(); // reserved
+ }
+ }
+ if (mask.get(50)) {
+ buf.readShort(); // tilt
+ }
+ if (mask.get(51)) {
+ buf.readUnsignedInt(); // partial hours
+ }
+ if (mask.get(52)) {
+ buf.readUnsignedInt(); // accumulated hours
+ }
+
+ if (position.getDeviceTime() != null && position.getFixTime() != null) {
+ positions.add(position);
+ }
+
+ }
+
+ // ACK 0x9C
+
+ return positions.isEmpty() ? null : positions;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RaveonProtocol.java b/src/main/java/org/traccar/protocol/RaveonProtocol.java
new file mode 100644
index 000000000..44faadb3b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RaveonProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class RaveonProtocol extends BaseProtocol {
+
+ public RaveonProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new RaveonProtocolDecoder(RaveonProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/RaveonProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java
index cbe6026a2..50acd20a1 100644
--- a/src/org/traccar/protocol/RaveonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RaveonProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class RaveonProtocolDecoder extends BaseProtocolDecoder {
- public RaveonProtocolDecoder(RaveonProtocol protocol) {
+ public RaveonProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -68,8 +69,7 @@ public class RaveonProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
diff --git a/src/main/java/org/traccar/protocol/RecodaProtocol.java b/src/main/java/org/traccar/protocol/RecodaProtocol.java
new file mode 100644
index 000000000..0bc9870bc
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RecodaProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+public class RecodaProtocol extends BaseProtocol {
+
+ public RecodaProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 4, 4, -8, 0, true));
+ pipeline.addLast(new RecodaProtocolDecoder(RecodaProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/RecodaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java
index 8db582d35..04098225f 100644
--- a/src/org/traccar/protocol/RecodaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RecodaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,7 +30,7 @@ import java.util.Date;
public class RecodaProtocolDecoder extends BaseProtocolDecoder {
- public RecodaProtocolDecoder(RecodaProtocol protocol) {
+ public RecodaProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -43,19 +44,19 @@ public class RecodaProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- int type = buf.readInt();
- buf.readUnsignedInt(); // length
+ int type = buf.readIntLE();
+ buf.readUnsignedIntLE(); // length
if (type != MSG_HEARTBEAT) {
- buf.readUnsignedShort(); // version
- buf.readUnsignedShort(); // index
+ buf.readUnsignedShortLE(); // version
+ buf.readUnsignedShortLE(); // index
}
if (type == MSG_SIGNAL_LINK_REGISTRATION) {
- getDeviceSession(channel, remoteAddress, buf.readBytes(12).toString(StandardCharsets.US_ASCII));
+ getDeviceSession(channel, remoteAddress, buf.readSlice(12).toString(StandardCharsets.US_ASCII));
} else if (type == MSG_GPS_DATA) {
@@ -64,25 +65,24 @@ public class RecodaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(new Date(buf.readLong()));
+ position.setTime(new Date(buf.readLongLE()));
int flags = buf.readUnsignedByte();
if (BitUtil.check(flags, 0)) {
- buf.readUnsignedShort(); // declination
+ buf.readUnsignedShortLE(); // declination
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
position.setLongitude(buf.readUnsignedByte() + buf.readUnsignedByte() / 60.0);
position.setLatitude(buf.readUnsignedByte() + buf.readUnsignedByte() / 60.0);
- position.setLongitude(position.getLongitude() + buf.readUnsignedInt() / 3600.0);
- position.setLatitude(position.getLatitude() + buf.readUnsignedInt() / 3600.0);
+ position.setLongitude(position.getLongitude() + buf.readUnsignedIntLE() / 3600.0);
+ position.setLatitude(position.getLatitude() + buf.readUnsignedIntLE() / 3600.0);
int status = buf.readUnsignedByte();
diff --git a/src/main/java/org/traccar/protocol/RetranslatorFrameDecoder.java b/src/main/java/org/traccar/protocol/RetranslatorFrameDecoder.java
new file mode 100644
index 000000000..4edd09418
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RetranslatorFrameDecoder.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 RetranslatorFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int length = 4 + buf.getIntLE(buf.readerIndex());
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
new file mode 100644
index 000000000..fae81f7d2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class RetranslatorProtocol extends BaseProtocol {
+
+ public RetranslatorProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..5bf6cef50
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RetranslatorProtocolDecoder.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2018 - 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class RetranslatorProtocolDecoder extends BaseProtocolDecoder {
+
+ public RetranslatorProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(new byte[]{0x11}), remoteAddress));
+ }
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedInt(); // length
+
+ int idLength = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x00) - buf.readerIndex();
+ String id = buf.readCharSequence(idLength, StandardCharsets.US_ASCII).toString();
+ buf.readByte();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(new Date(buf.readUnsignedInt() * 1000));
+
+ buf.readUnsignedInt(); // bit flags
+
+ while (buf.isReadable()) {
+
+ buf.readUnsignedShort(); // block type
+ int blockEnd = buf.readInt() + buf.readerIndex();
+ buf.readUnsignedByte(); // security attribute
+ int dataType = buf.readUnsignedByte();
+
+ int nameLength = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x00) - buf.readerIndex();
+ String name = buf.readCharSequence(nameLength, StandardCharsets.US_ASCII).toString();
+ buf.readByte();
+
+ if (name.equals("posinfo")) {
+ position.setValid(true);
+ position.setLongitude(buf.readDoubleLE());
+ position.setLatitude(buf.readDoubleLE());
+ position.setAltitude(buf.readDoubleLE());
+ position.setSpeed(buf.readShort());
+ position.setCourse(buf.readShort());
+ position.set(Position.KEY_SATELLITES, buf.readByte());
+ } else {
+ switch (dataType) {
+ case 1:
+ int len = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x00) - buf.readerIndex();
+ position.set(name, buf.readCharSequence(len, StandardCharsets.US_ASCII).toString());
+ buf.readByte();
+ break;
+ case 3:
+ position.set(name, buf.readInt());
+ break;
+ case 4:
+ position.set(name, buf.readDoubleLE());
+ break;
+ case 5:
+ position.set(name, buf.readLong());
+ break;
+ default:
+ break;
+ }
+ }
+
+ buf.readerIndex(blockEnd);
+
+ }
+
+ if (position.getLatitude() == 0 && position.getLongitude() == 0) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RitiProtocol.java b/src/main/java/org/traccar/protocol/RitiProtocol.java
new file mode 100644
index 000000000..de1026672
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RitiProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+public class RitiProtocol extends BaseProtocol {
+
+ public RitiProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 105, 2, 3, 0, true));
+ pipeline.addLast(new RitiProtocolDecoder(RitiProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/RitiProtocolDecoder.java b/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java
index 5c298e8c5..46267ca90 100644
--- a/src/org/traccar/protocol/RitiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RitiProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -30,7 +31,7 @@ import java.util.regex.Pattern;
public class RitiProtocolDecoder extends BaseProtocolDecoder {
- public RitiProtocolDecoder(RitiProtocol protocol) {
+ public RitiProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -52,12 +53,11 @@ public class RitiProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(buf.readUnsignedShort()));
if (deviceSession == null) {
@@ -67,14 +67,14 @@ public class RitiProtocolDecoder extends BaseProtocolDecoder {
position.set("mode", buf.readUnsignedByte());
position.set(Position.KEY_COMMAND, buf.readUnsignedByte());
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001);
buf.skipBytes(5); // status
- buf.readUnsignedShort(); // idleCount
- buf.readUnsignedShort(); // idleTime in seconds
+ buf.readUnsignedShortLE(); // idleCount
+ buf.readUnsignedShortLE(); // idleTime in seconds
- position.set(Position.KEY_DISTANCE, buf.readUnsignedInt());
- position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
+ position.set(Position.KEY_DISTANCE, buf.readUnsignedIntLE());
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedIntLE());
// Parse GPRMC
int end = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*');
diff --git a/src/main/java/org/traccar/protocol/RoboTrackFrameDecoder.java b/src/main/java/org/traccar/protocol/RoboTrackFrameDecoder.java
new file mode 100644
index 000000000..85ed6c76f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RoboTrackFrameDecoder.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 RoboTrackFrameDecoder extends BaseFrameDecoder {
+
+ private int messageLength(ByteBuf buf) {
+ switch (buf.getUnsignedByte(buf.readerIndex())) {
+ case RoboTrackProtocolDecoder.MSG_ID:
+ return 69;
+ case RoboTrackProtocolDecoder.MSG_ACK:
+ return 3;
+ case RoboTrackProtocolDecoder.MSG_GPS:
+ case RoboTrackProtocolDecoder.MSG_GSM:
+ case RoboTrackProtocolDecoder.MSG_IMAGE_START:
+ return 24;
+ case RoboTrackProtocolDecoder.MSG_IMAGE_DATA:
+ return 8 + buf.getUnsignedShortLE(buf.readerIndex() + 1);
+ case RoboTrackProtocolDecoder.MSG_IMAGE_END:
+ return 6;
+ default:
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int length = messageLength(buf);
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
new file mode 100644
index 000000000..c2c531293
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class RoboTrackProtocol extends BaseProtocol {
+
+ public RoboTrackProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..b613f31d7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RoboTrackProtocolDecoder.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class RoboTrackProtocolDecoder extends BaseProtocolDecoder {
+
+ public RoboTrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_ID = 0x00;
+ public static final int MSG_ACK = 0x80;
+ public static final int MSG_GPS = 0x03;
+ public static final int MSG_GSM = 0x04;
+ public static final int MSG_IMAGE_START = 0x06;
+ public static final int MSG_IMAGE_DATA = 0x07;
+ public static final int MSG_IMAGE_END = 0x08;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_ID) {
+
+ buf.skipBytes(16); // name
+
+ String imei = buf.readSlice(15).toString(StandardCharsets.US_ASCII);
+
+ if (getDeviceSession(channel, remoteAddress, imei) != null && channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(MSG_ACK);
+ response.writeByte(0x01); // success
+ response.writeByte(0x66); // checksum
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ } else if (type == MSG_GPS || type == MSG_GSM) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(new Date(buf.readUnsignedIntLE() * 1000));
+
+ if (type == MSG_GPS) {
+
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ position.setLatitude(buf.readIntLE() * 0.000001);
+ position.setLongitude(buf.readIntLE() * 0.000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readByte()));
+
+ } else {
+
+ getLastLocation(position, position.getDeviceTime());
+
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
+ buf.readUnsignedShortLE(), buf.readUnsignedShortLE())));
+
+ buf.readUnsignedByte(); // reserved
+
+ }
+
+ int value = buf.readUnsignedByte();
+
+ position.set(Position.KEY_SATELLITES, BitUtil.to(value, 4));
+ position.set(Position.KEY_RSSI, BitUtil.between(value, 4, 7));
+ position.set(Position.KEY_MOTION, BitUtil.check(value, 7));
+
+ value = buf.readUnsignedByte();
+
+ position.set(Position.KEY_CHARGE, BitUtil.check(value, 0));
+
+ for (int i = 1; i <= 4; i++) {
+ position.set(Position.PREFIX_IN + i, BitUtil.check(value, i));
+ }
+
+ position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(value, 5) * 100 / 7);
+ position.set(Position.KEY_DEVICE_TEMP, buf.readByte());
+
+ for (int i = 1; i <= 3; i++) {
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShortLE());
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RstProtocol.java b/src/main/java/org/traccar/protocol/RstProtocol.java
new file mode 100644
index 000000000..10d11d493
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RstProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 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;
+
+public class RstProtocol extends BaseProtocol {
+
+ public RstProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..05601ed51
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2019 - 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.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+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 RstProtocolDecoder extends BaseProtocolDecoder {
+
+ public RstProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("RST;")
+ .expression("([AL]);") // archive
+ .expression("([^,]+);") // model
+ .expression("(.{5});") // firmware
+ .number("(d{9});") // serial number
+ .number("(d+);") // index
+ .number("(d+);") // type
+ .number("(dd)-(dd)-(dddd) ") // event date
+ .number("(dd):(dd):(dd);") // event time
+ .number("(dd)-(dd)-(dddd) ") // fix date
+ .number("(dd):(dd):(dd);") // fix time
+ .number("(-?d+.d+);") // latitude
+ .number("(-?d+.d+);") // longitude
+ .number("(d+);") // speed
+ .number("(d+);") // course
+ .number("(-?d+);") // altitude
+ .number("([01]);") // valid
+ .number("(d+);") // satellites
+ .number("(d+);") // hdop
+ .number("(xx);") // inputs 1
+ .number("(xx);") // inputs 2
+ .number("(xx);") // inputs 3
+ .number("(xx);") // outputs 1
+ .number("(xx);") // outputs 2
+ .number("(d+.d+);") // power
+ .number("(d+.d+);") // battery
+ .number("(d+);") // odometer
+ .number("(d+);") // rssi
+ .number("(xx);") // temperature
+ .number("x{4};") // sensors
+ .number("(xx);") // status 1
+ .number("(xx);") // status 2
+ .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;
+ }
+
+ String archive = parser.next();
+ String model = parser.next();
+ String firmware = parser.next();
+ String serial = parser.next();
+ int index = parser.nextInt();
+ parser.nextInt(); // type
+
+ if (channel != null && archive.equals("A")) {
+ String response = "RST;A;" + model + ";" + firmware + ";" + serial + ";" + index + ";6;FIM;";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, serial);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setFixTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextInt());
+ position.set(Position.PREFIX_IN + 1, parser.nextHexInt());
+ position.set(Position.PREFIX_IN + 2, parser.nextHexInt());
+ position.set(Position.PREFIX_IN + 3, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT + 1, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT + 2, parser.nextHexInt());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, (int) parser.nextHexInt().byteValue());
+
+ int status = (parser.nextHexInt() << 8) + parser.nextHexInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 7));
+ position.set(Position.KEY_STATUS, status);
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
index fc3b17dd9..b8f72336b 100644
--- a/src/org/traccar/protocol/RuptelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,37 +15,30 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class RuptelaProtocol extends BaseProtocol {
public RuptelaProtocol() {
- super("ruptela");
setSupportedDataCommands(
Command.TYPE_CUSTOM,
+ Command.TYPE_REQUEST_PHOTO,
Command.TYPE_CONFIGURATION,
Command.TYPE_GET_VERSION,
Command.TYPE_FIRMWARE_UPDATE,
Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_SET_CONNECTION,
Command.TYPE_SET_ODOMETER);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, 2, 0));
- pipeline.addLast("objectEncoder", new RuptelaProtocolEncoder());
- pipeline.addLast("objectDecoder", new RuptelaProtocolDecoder(RuptelaProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 8752d30c0..227a9ac91 100644
--- a/src/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,15 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
-import javax.xml.bind.DatatypeConverter;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
@@ -32,7 +35,9 @@ import java.util.List;
public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
- public RuptelaProtocolDecoder(RuptelaProtocol protocol) {
+ private ByteBuf photo;
+
+ public RuptelaProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -46,11 +51,11 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_SMS_VIA_GPRS = 8;
public static final int MSG_DTCS = 9;
public static final int MSG_SET_IO = 17;
+ public static final int MSG_FILES = 37;
public static final int MSG_EXTENDED_RECORDS = 68;
- private Position decodeCommandResponse(DeviceSession deviceSession, int type, ChannelBuffer buf) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ private Position decodeCommandResponse(DeviceSession deviceSession, int type, ByteBuf buf) {
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
@@ -74,7 +79,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private long readValue(ChannelBuffer buf, int length, boolean signed) {
+ private long readValue(ByteBuf buf, int length, boolean signed) {
switch (length) {
case 1:
return signed ? buf.readByte() : buf.readUnsignedByte();
@@ -87,7 +92,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) {
+ private void decodeParameter(Position position, int id, ByteBuf buf, int length) {
switch (id) {
case 2:
case 3:
@@ -115,7 +120,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedShort(); // data length
@@ -135,8 +140,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
int count = buf.readUnsignedByte();
for (int i = 0; i < count; i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date(buf.readUnsignedInt() * 1000));
@@ -194,11 +198,20 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
decodeParameter(position, id, buf, 8);
}
+ Long driverIdPart1 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 126);
+ Long driverIdPart2 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 127);
+ if (driverIdPart1 != null && driverIdPart2 != null) {
+ ByteBuf driverId = Unpooled.copyLong(driverIdPart1, driverIdPart2);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, driverId.toString(StandardCharsets.US_ASCII));
+ driverId.release();
+ }
+
positions.add(position);
}
if (channel != null) {
- channel.write(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary("0002640113bc")));
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.wrappedBuffer(DataConverter.parseHex("0002640113bc")), remoteAddress));
}
return positions;
@@ -210,8 +223,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
int count = buf.readUnsignedByte();
for (int i = 0; i < count; i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
buf.readUnsignedByte(); // reserved
@@ -226,17 +238,55 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ARCHIVE, true);
}
- position.set(Position.KEY_DTCS, buf.readBytes(5).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_DTCS, buf.readSlice(5).toString(StandardCharsets.US_ASCII));
positions.add(position);
}
if (channel != null) {
- channel.write(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary("00026d01c4a4")));
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.wrappedBuffer(DataConverter.parseHex("00026d01c4a4")), remoteAddress));
}
return positions;
+ } else if (type == MSG_FILES) {
+
+ int subtype = buf.readUnsignedByte();
+ int source = buf.readUnsignedByte();
+
+ if (subtype == 2) {
+ ByteBuf filename = buf.readSlice(8);
+ int total = buf.readUnsignedShort();
+ int current = buf.readUnsignedShort();
+ if (photo == null) {
+ photo = Unpooled.buffer();
+ }
+ photo.writeBytes(buf.readSlice(buf.readableBytes() - 2));
+ if (current < total - 1) {
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(subtype);
+ content.writeByte(source);
+ content.writeBytes(filename);
+ content.writeShort(current + 1);
+ ByteBuf response = RuptelaProtocolEncoder.encodeContent(type, content);
+ content.release();
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ } else {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ photo.release();
+ photo = null;
+ return position;
+ }
+ }
+
+ return null;
+
} else {
return decodeCommandResponse(deviceSession, type, buf);
diff --git a/src/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
index 564d80869..fb0dcf690 100644
--- a/src/org/traccar/protocol/RuptelaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,25 +15,30 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
+import org.traccar.Protocol;
import java.nio.charset.StandardCharsets;
public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeContent(int type, ChannelBuffer content) {
+ public RuptelaProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static ByteBuf encodeContent(int type, ByteBuf content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
buf.writeShort(1 + content.readableBytes());
buf.writeByte(100 + type);
buf.writeBytes(content);
- buf.writeShort(Checksum.crc16(Checksum.CRC16_KERMIT, buf.toByteBuffer(2, buf.writerIndex() - 2)));
+ buf.writeShort(Checksum.crc16(Checksum.CRC16_KERMIT, buf.nioBuffer(2, buf.writerIndex() - 2)));
return buf;
}
@@ -41,12 +46,24 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- ChannelBuffer content = ChannelBuffers.dynamicBuffer();
+ ByteBuf content = Unpooled.buffer();
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- content.writeBytes(command.getString(Command.KEY_DATA).getBytes(StandardCharsets.US_ASCII));
- return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
+ String data = command.getString(Command.KEY_DATA);
+ if (data.matches("(\\p{XDigit}{2})+")) {
+ content.writeBytes(DataConverter.parseHex(data));
+ return content;
+ } else {
+ content.writeBytes(data.getBytes(StandardCharsets.US_ASCII));
+ return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
+ }
+ case Command.TYPE_REQUEST_PHOTO:
+ content.writeByte(1); // sub-command
+ content.writeByte(0); // source
+ content.writeInt(0); // start timestamp
+ content.writeInt(Integer.MAX_VALUE); // end timestamp
+ return encodeContent(RuptelaProtocolDecoder.MSG_FILES, content);
case Command.TYPE_CONFIGURATION:
content.writeBytes((command.getString(Command.KEY_DATA) + "\r\n").getBytes(StandardCharsets.US_ASCII));
return encodeContent(RuptelaProtocolDecoder.MSG_DEVICE_CONFIGURATION, content);
@@ -67,11 +84,8 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
content.writeInt(Integer.parseInt(command.getString(Command.KEY_DATA)));
return encodeContent(RuptelaProtocolDecoder.MSG_SET_ODOMETER, content);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/S168Protocol.java b/src/main/java/org/traccar/protocol/S168Protocol.java
new file mode 100644
index 000000000..e78664c40
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/S168Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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 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;
+
+public class S168Protocol extends BaseProtocol {
+
+ public S168Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new S168ProtocolDecoder(S168Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
new file mode 100644
index 000000000..71aff1a65
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+public class S168ProtocolDecoder extends BaseProtocolDecoder {
+
+ public S168ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ String[] values = sentence.split("#");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[1]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ String content = values[4];
+ String[] fragments = content.split(";");
+ for (String fragment : fragments) {
+
+ int dataIndex = fragment.indexOf(':');
+ String type = fragment.substring(0, dataIndex);
+ values = fragment.substring(dataIndex + 1).split(",");
+ int index = 0;
+
+ switch (type) {
+ case "GDATA":
+ position.setValid(values[index++].equals("A"));
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Integer.parseInt(values[index++]));
+ position.setAltitude(Integer.parseInt(values[index++]));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return position.getAttributes().containsKey(Position.KEY_SATELLITES) ? position : null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SabertekFrameDecoder.java b/src/main/java/org/traccar/protocol/SabertekFrameDecoder.java
new file mode 100644
index 000000000..ad5000bf8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SabertekFrameDecoder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 SabertekFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int beginIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x02);
+ if (beginIndex >= 0) {
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x03);
+ if (endIndex >= 0) {
+ buf.readerIndex(beginIndex + 1);
+ ByteBuf frame = buf.readRetainedSlice(endIndex - beginIndex - 1);
+ buf.readerIndex(endIndex + 1);
+ buf.skipBytes(2); // end line
+ return frame;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SabertekProtocol.java b/src/main/java/org/traccar/protocol/SabertekProtocol.java
new file mode 100644
index 000000000..0ec847b60
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SabertekProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class SabertekProtocol extends BaseProtocol {
+
+ public SabertekProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..3033aa2cc
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SabertekProtocolDecoder.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+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.Date;
+import java.util.regex.Pattern;
+
+public class SabertekProtocolDecoder extends BaseProtocolDecoder {
+
+ public SabertekProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text(",")
+ .number("(d+),") // id
+ .number("d,") // type
+ .groupBegin()
+ .number("d+,") // imei
+ .number("d+,") // scid
+ .expression("[^,]*,") // phone
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .groupEnd("?")
+ .number("(d+),") // battery
+ .number("(d+),") // rssi
+ .number("(d+),") // state
+ .number("(d+),") // events
+ .number("(d),") // valid
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(d+),") // altitude
+ .number("(d+),") // satellites
+ .number("(d+),") // odometer
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.wrappedBuffer(new byte[]{(byte) (deviceSession != null ? 0x06 : 0x15)}), remoteAddress));
+ }
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (parser.hasNext(6)) {
+ position.setTime(parser.nextDateTime());
+ } else {
+ position.setTime(new Date());
+ }
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ int state = parser.nextInt();
+
+ position.set(Position.KEY_IGNITION, BitUtil.check(state, 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(state, 1));
+
+ if (BitUtil.check(state, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
+ }
+ if (BitUtil.check(state, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
+ }
+
+ int events = parser.nextInt();
+
+ if (BitUtil.check(events, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ }
+ if (BitUtil.check(events, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+ if (BitUtil.check(events, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ }
+ if (BitUtil.check(events, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ }
+
+ position.setValid(parser.nextInt() == 1);
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextInt() * 1000L);
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SanavProtocol.java b/src/main/java/org/traccar/protocol/SanavProtocol.java
new file mode 100644
index 000000000..6799c57e6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SanavProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class SanavProtocol extends BaseProtocol {
+
+ public SanavProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new SanavProtocolDecoder(SanavProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new SanavProtocolDecoder(SanavProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/SanavProtocolDecoder.java b/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java
index 151c55795..7e1c158e6 100644
--- a/src/org/traccar/protocol/SanavProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SanavProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
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;
@@ -28,12 +30,11 @@ import java.util.regex.Pattern;
public class SanavProtocolDecoder extends BaseProtocolDecoder {
- public SanavProtocolDecoder(SanavProtocol protocol) {
+ public SanavProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN = new PatternBuilder()
- .any()
.expression("imei[:=]")
.number("(d+)") // imei
.expression("&?rmc[:=]")
@@ -47,6 +48,13 @@ public class SanavProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+),") // speed
.number("(d+.d+)?,") // course
.number("(dd)(dd)(dd),") // date (ddmmyy)
+ .groupBegin()
+ .expression("[^*]*")
+ .text("*")
+ .number("xx,")
+ .expression("[^,]+,") // status
+ .number("(d+),") // io
+ .groupEnd("?")
.any()
.compile();
@@ -59,27 +67,40 @@ public class SanavProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setValid(parser.next().equals("A"));
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
+ position.setSpeed(parser.nextDouble());
position.setCourse(parser.nextDouble(0));
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
+ if (parser.hasNext()) {
+ int io = parser.nextHexInt();
+ for (int i = 0; i < 5; i++) {
+ position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(io, i));
+ }
+ position.set(Position.KEY_IGNITION, BitUtil.check(io, 5));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 6));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 7));
+ position.set(Position.KEY_CHARGE, BitUtil.check(io, 8));
+ if (!BitUtil.check(io, 9)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ }
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/SanulProtocol.java b/src/main/java/org/traccar/protocol/SanulProtocol.java
new file mode 100644
index 000000000..3104e9366
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SanulProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class SanulProtocol extends BaseProtocol {
+
+ public SanulProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..036d1ee51
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SanulProtocolDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class SanulProtocolDecoder extends BaseProtocolDecoder {
+
+ public static final int MSG_LOGIN = 0;
+ public static final int MSG_LOCATION = 1;
+ public static final int MSG_RESPONSE = 5;
+
+ public SanulProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, int type) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xaa); // header
+ response.writeShortLE(0x85da); // reserved
+ response.writeShortLE(15); // length
+ response.writeByte(1); // edition
+ response.writeShortLE(MSG_RESPONSE);
+ response.writeShortLE(type);
+ response.writeIntLE(0); // command id
+ response.writeByte(0); // status
+ response.writeByte(0); // result length
+ response.writeIntLE(0x20000); // result data ?
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedShortLE(); // reserved
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedByte(); // edition
+
+ int type = buf.readUnsignedShortLE();
+
+ buf.readUnsignedIntLE(); // command id
+
+ sendResponse(channel, type);
+
+ if (type == MSG_LOGIN) {
+
+ getDeviceSession(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim());
+
+ } else if (type == MSG_LOCATION) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SatsolProtocol.java b/src/main/java/org/traccar/protocol/SatsolProtocol.java
new file mode 100644
index 000000000..b69fdd1fe
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SatsolProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class SatsolProtocol extends BaseProtocol {
+
+ public SatsolProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..c457d5620
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SatsolProtocolDecoder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 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 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.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 java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class SatsolProtocolDecoder extends BaseProtocolDecoder {
+
+ public SatsolProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedShortLE(); // preamble
+ long id = buf.readUnsignedIntLE();
+ buf.readUnsignedShortLE(); // length
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.isReadable()) {
+
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedShortLE(); // type
+ int length = buf.readUnsignedShortLE();
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLatitude(buf.readUnsignedIntLE() * 0.000001);
+ position.setLongitude(buf.readUnsignedIntLE() * 0.000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE() * 0.01));
+ position.setAltitude(buf.readShortLE());
+ position.setCourse(buf.readUnsignedShortLE());
+ position.setValid(buf.readUnsignedByte() > 0);
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+
+ if (BitUtil.check(buf.readUnsignedByte(), 0)) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ positions.add(position);
+
+ buf.skipBytes(length);
+
+ }
+
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShortLE(0);
+ response.writeShortLE(0x4CBF); // preamble
+ response.writeIntLE((int) id);
+ response.writeShortLE(0);
+ response.setShortLE(0, Checksum.crc16(
+ Checksum.CRC16_CCITT_FALSE, response.nioBuffer(2, response.readableBytes() - 2)));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocol.java b/src/main/java/org/traccar/protocol/SigfoxProtocol.java
new file mode 100644
index 000000000..e2f2cbe1f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class SigfoxProtocol extends BaseProtocol {
+
+ public SigfoxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new SigfoxProtocolDecoder(SigfoxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
new file mode 100644
index 000000000..304f61836
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
@@ -0,0 +1,219 @@
+/*
+ * 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.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+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 org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DataConverter;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import javax.json.Json;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public SigfoxProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private int getJsonInt(JsonObject json, String key) {
+ JsonValue value = json.get(key);
+ if (value != null) {
+ if (value.getValueType() == JsonValue.ValueType.NUMBER) {
+ return ((JsonNumber) value).intValue();
+ } else if (value.getValueType() == JsonValue.ValueType.STRING) {
+ return Integer.parseInt(((JsonString) value).getString());
+ }
+ }
+ return 0;
+ }
+
+ private double getJsonDouble(JsonObject json, String key) {
+ JsonValue value = json.get(key);
+ if (value != null) {
+ if (value.getValueType() == JsonValue.ValueType.NUMBER) {
+ return ((JsonNumber) value).doubleValue();
+ } else if (value.getValueType() == JsonValue.ValueType.STRING) {
+ return Double.parseDouble(((JsonString) value).getString());
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ if (!content.startsWith("{")) {
+ content = URLDecoder.decode(content.split("=")[0], "UTF-8");
+ }
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("device"));
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (json.containsKey("time")) {
+ position.setTime(new Date(getJsonInt(json, "time") * 1000L));
+ } else {
+ position.setTime(new Date());
+ }
+
+ if (json.containsKey("location")
+ || json.containsKey("lat") && json.containsKey("lng") && !json.containsKey("data")) {
+
+ JsonObject location;
+ if (json.containsKey("location")) {
+ location = json.getJsonObject("location");
+ } else {
+ location = json;
+ }
+
+ position.setValid(true);
+ position.setLatitude(getJsonDouble(location, "lat"));
+ position.setLongitude(getJsonDouble(location, "lng"));
+
+ } else {
+
+ String data = json.getString(json.containsKey("data") ? "data" : "payload");
+ ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data));
+ try {
+ int event = buf.readUnsignedByte();
+ 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.set(Position.KEY_BATTERY, (int) buf.readUnsignedByte());
+
+ } else if (event >> 4 == 0) {
+
+ position.setValid(true);
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.025);
+
+ } else {
+
+ position.set(Position.KEY_EVENT, event);
+ if (event == 0x22 || event == 0x62) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ while (buf.isReadable()) {
+ int type = buf.readUnsignedByte();
+ switch (type) {
+ case 0x01:
+ position.setValid(true);
+ position.setLatitude(buf.readMedium());
+ position.setLongitude(buf.readMedium());
+ break;
+ case 0x02:
+ position.setValid(true);
+ position.setLatitude(buf.readFloat());
+ position.setLongitude(buf.readFloat());
+ break;
+ case 0x03:
+ position.set(Position.PREFIX_TEMP + 1, buf.readByte() * 0.5);
+ break;
+ case 0x04:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
+ break;
+ case 0x05:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ break;
+ case 0x06:
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ position.setNetwork(new Network(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte())));
+ break;
+ case 0x07:
+ buf.skipBytes(10); // wifi extended
+ break;
+ case 0x08:
+ buf.skipBytes(6); // accelerometer
+ break;
+ case 0x09:
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ break;
+ default:
+ buf.readUnsignedByte(); // fence number
+ break;
+ }
+ }
+
+ }
+ } finally {
+ buf.release();
+ }
+ }
+
+ if (position.getLatitude() == 0 && position.getLongitude() == 0) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ if (json.containsKey("rssi")) {
+ position.set(Position.KEY_RSSI, getJsonDouble(json, "rssi"));
+ }
+ if (json.containsKey("seqNumber")) {
+ position.set(Position.KEY_INDEX, getJsonInt(json, "seqNumber"));
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SiwiProtocol.java b/src/main/java/org/traccar/protocol/SiwiProtocol.java
new file mode 100644
index 000000000..8963721c8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SiwiProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class SiwiProtocol extends BaseProtocol {
+
+ public SiwiProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new SiwiProtocolDecoder(SiwiProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/SiwiProtocolDecoder.java b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
index 198df24d5..6b97f5fe0 100644
--- a/src/org/traccar/protocol/SiwiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SiwiProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class SiwiProtocolDecoder extends BaseProtocolDecoder {
- public SiwiProtocolDecoder(SiwiProtocol protocol) {
+ public SiwiProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -70,8 +71,7 @@ public class SiwiProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_EVENT, parser.next());
diff --git a/src/org/traccar/protocol/SkypatrolProtocol.java b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
index fba432522..7c6203d86 100644
--- a/src/org/traccar/protocol/SkypatrolProtocol.java
+++ b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java
@@ -15,25 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class SkypatrolProtocol extends BaseProtocol {
public SkypatrolProtocol() {
- super("skypatrol");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new SkypatrolProtocolDecoder(SkypatrolProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new SkypatrolProtocolDecoder(SkypatrolProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/SkypatrolProtocolDecoder.java b/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java
index f4dded972..3c7ca6dc5 100644
--- a/src/org/traccar/protocol/SkypatrolProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SkypatrolProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,14 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+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.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Log;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -30,9 +32,11 @@ import java.nio.charset.StandardCharsets;
public class SkypatrolProtocolDecoder extends BaseProtocolDecoder {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SkypatrolProtocolDecoder.class);
+
private final long defaultMask;
- public SkypatrolProtocolDecoder(SkypatrolProtocol protocol) {
+ public SkypatrolProtocolDecoder(Protocol protocol) {
super(protocol);
defaultMask = Context.getConfig().getInteger(getProtocolName() + ".mask");
}
@@ -54,7 +58,7 @@ public class SkypatrolProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
int apiNumber = buf.readUnsignedShort();
int commandType = buf.readUnsignedByte();
@@ -67,8 +71,7 @@ public class SkypatrolProtocolDecoder extends BaseProtocolDecoder {
// Binary position report
if (apiNumber == 5 && commandType == 2 && messageType == 1 && BitUtil.check(mask, 0)) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
if (BitUtil.check(mask, 1)) {
position.set(Position.KEY_STATUS, buf.readUnsignedInt());
@@ -82,7 +85,7 @@ public class SkypatrolProtocolDecoder extends BaseProtocolDecoder {
id = buf.toString(buf.readerIndex(), 22, StandardCharsets.US_ASCII).trim();
buf.skipBytes(22);
} else {
- Log.warning("No device id field");
+ LOGGER.warn("No device id field");
return null;
}
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
new file mode 100644
index 000000000..bcf43f68b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class SmartSoleProtocol extends BaseProtocol {
+
+ public SmartSoleProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new SmartSoleProtocolDecoder(SmartSoleProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java b/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java
new file mode 100644
index 000000000..04920c969
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SmartSoleProtocolDecoder.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.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 SmartSoleProtocolDecoder extends BaseProtocolDecoder {
+
+ public SmartSoleProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("#GTXRP=")
+ .number("(d+),") // imei
+ .number("d+,") // report type
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(-?d+.d+),") // longitude
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+),") // altitude
+ .number("(d+),") // speed
+ .number("([01]),") // valid
+ .number("(d+),") // satellites
+ .number("(d+.d+),") // hdop
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+.d+),") // battery
+ .number("(d+)") // status
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setFixTime(parser.nextDateTime());
+
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setAltitude(parser.nextInt());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setValid(parser.nextInt() == 1);
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+
+ position.setDeviceTime(parser.nextDateTime());
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_STATUS, parser.nextInt());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/SmokeyProtocol.java b/src/main/java/org/traccar/protocol/SmokeyProtocol.java
index 4f9a8dd74..482c8347c 100644
--- a/src/org/traccar/protocol/SmokeyProtocol.java
+++ b/src/main/java/org/traccar/protocol/SmokeyProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,25 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class SmokeyProtocol extends BaseProtocol {
public SmokeyProtocol() {
- super("smokey");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new SmokeyProtocolDecoder(SmokeyProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new SmokeyProtocolDecoder(SmokeyProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/SmokeyProtocolDecoder.java b/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java
index 2dcfeb86f..9da52e97a 100644
--- a/src/org/traccar/protocol/SmokeyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SmokeyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,14 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.Seconds;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
@@ -31,10 +31,12 @@ import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
public class SmokeyProtocolDecoder extends BaseProtocolDecoder {
- public SmokeyProtocolDecoder(SmokeyProtocol protocol) {
+ public SmokeyProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -42,26 +44,26 @@ public class SmokeyProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_DATE_RECORD_ACK = 1;
private static void sendResponse(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer id, int index, int report) {
+ Channel channel, SocketAddress remoteAddress, ByteBuf id, int index, int report) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeBytes("SM".getBytes(StandardCharsets.US_ASCII));
response.writeByte(3); // protocol version
response.writeByte(MSG_DATE_RECORD_ACK);
response.writeBytes(id);
- response.writeInt(Seconds.secondsBetween(
- new DateTime(2000, 1, 1, 0, 0, DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)).getSeconds());
+ response.writeInt(
+ (int) ChronoUnit.SECONDS.between(Instant.parse("2000-01-01T00:00:00.00Z"), Instant.now()));
response.writeByte(index);
response.writeByte(report - 0x200);
short checksum = (short) 0xF5A0;
for (int i = 0; i < response.readableBytes(); i += 2) {
- checksum ^= ChannelBuffers.swapShort(response.getShort(i));
+ checksum ^= response.getShortLE(i);
}
response.writeShort(checksum);
- channel.write(response, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
@@ -69,23 +71,22 @@ public class SmokeyProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
buf.readUnsignedByte(); // protocol version
int type = buf.readUnsignedByte();
- ChannelBuffer id = buf.readBytes(8);
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(id));
+ ByteBuf id = buf.readSlice(8);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(id));
if (deviceSession == null) {
return null;
}
if (type == MSG_DATE_RECORD) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_VERSION_FW, buf.readUnsignedShort());
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
new file mode 100644
index 000000000..53a948cdc
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class SolarPoweredProtocol extends BaseProtocol {
+
+ public SolarPoweredProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..eae37386a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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 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.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class SolarPoweredProtocolDecoder extends BaseProtocolDecoder {
+
+ public SolarPoweredProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_ACTIVE_REPORTING = 0x11;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // start marker
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // attributes
+
+ if (type == MSG_ACTIVE_REPORTING) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ while (buf.readableBytes() > 2) {
+ int tag = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+ switch (tag) {
+ case 0x81:
+ int status = buf.readUnsignedByte();
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+ position.setLatitude(buf.readUnsignedInt() * 0.000001);
+ if (BitUtil.check(status, 3)) {
+ position.setLatitude(-position.getLatitude());
+ }
+ position.setLongitude(buf.readUnsignedInt() * 0.000001);
+ if (BitUtil.check(status, 2)) {
+ position.setLongitude(-position.getLongitude());
+ }
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.02);
+ position.setCourse(buf.readUnsignedByte());
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SpotProtocol.java b/src/main/java/org/traccar/protocol/SpotProtocol.java
new file mode 100644
index 000000000..bbf0e8d8a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SpotProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class SpotProtocol extends BaseProtocol {
+
+ public SpotProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new SpotProtocolDecoder(SpotProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
new file mode 100644
index 000000000..da36c2048
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
@@ -0,0 +1,102 @@
+/*
+ * 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.protocol;
+
+import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
+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.Protocol;
+import org.traccar.helper.DateUtil;
+import org.traccar.model.Position;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import java.net.SocketAddress;
+import java.util.LinkedList;
+import java.util.List;
+
+public class SpotProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ private DocumentBuilder documentBuilder;
+ private XPath xPath;
+ private XPathExpression messageExpression;
+
+ public SpotProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ try {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ builderFactory.setXIncludeAware(false);
+ builderFactory.setExpandEntityReferences(false);
+ documentBuilder = builderFactory.newDocumentBuilder();
+ xPath = XPathFactory.newInstance().newXPath();
+ messageExpression = xPath.compile("//messageList/message");
+ } catch (ParserConfigurationException | XPathExpressionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+
+ Document document = documentBuilder.parse(new ByteBufferBackedInputStream(request.content().nioBuffer()));
+ NodeList nodes = (NodeList) messageExpression.evaluate(document, XPathConstants.NODESET);
+
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, xPath.evaluate("esnName", node));
+ if (deviceSession != null) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(DateUtil.parseDate(xPath.evaluate("timestamp", node)));
+ position.setLatitude(Double.parseDouble(xPath.evaluate("latitude", node)));
+ position.setLongitude(Double.parseDouble(xPath.evaluate("longitude", node)));
+
+ position.set(Position.KEY_EVENT, xPath.evaluate("messageType", node));
+
+ positions.add(position);
+
+ }
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocol.java b/src/main/java/org/traccar/protocol/StarLinkProtocol.java
new file mode 100644
index 000000000..5630722ee
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class StarLinkProtocol extends BaseProtocol {
+
+ public StarLinkProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StarLinkProtocolDecoder(StarLinkProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index 79f013fac..bad6f03a9 100644
--- a/src/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.CellTower;
@@ -33,21 +34,7 @@ import java.util.regex.Pattern;
public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
- private String[] dataTags;
- private DateFormat dateFormat;
-
- public StarLinkProtocolDecoder(StarLinkProtocol protocol) {
- super(protocol);
-
- String format = Context.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#");
- dataTags = format.split(",");
-
- dateFormat = new SimpleDateFormat(
- Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss"));
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
+ public static final int MSG_EVENT_REPORT = 6;
private static final Pattern PATTERN = new PatternBuilder()
.expression(".") // protocol head
@@ -60,7 +47,27 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
.number("xx") // checksum
.compile();
- public static final int MSG_EVENT_REPORT = 6;
+ private String[] dataTags;
+ private DateFormat dateFormat;
+
+ public StarLinkProtocolDecoder(Protocol protocol) {
+ super(protocol);
+
+ setFormat(Context.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"));
+ }
+
+ public void setFormat(String format) {
+ dataTags = format.split(",");
+ }
+
+ public void setDateFormat(String dateFormat) {
+ this.dateFormat = new SimpleDateFormat(dateFormat);
+ this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
private double parseCoordinate(String value) {
int minutesIndex = value.indexOf('.') - 2;
@@ -111,8 +118,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setValid(true);
@@ -151,31 +157,19 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(Integer.parseInt(data[i]));
break;
case "#ODO#":
- position.set(Position.KEY_ODOMETER, Long.parseLong(data[i]) * 1000);
+ position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(data[i]) * 1000));
break;
case "#IN1#":
- position.set(Position.PREFIX_IN + 1, Integer.parseInt(data[i]));
- break;
case "#IN2#":
- position.set(Position.PREFIX_IN + 2, Integer.parseInt(data[i]));
- break;
case "#IN3#":
- position.set(Position.PREFIX_IN + 3, Integer.parseInt(data[i]));
- break;
case "#IN4#":
- position.set(Position.PREFIX_IN + 4, Integer.parseInt(data[i]));
+ position.set(Position.PREFIX_IN + dataTags[i].charAt(3), Integer.parseInt(data[i]));
break;
case "#OUT1#":
- position.set(Position.PREFIX_OUT + 1, Integer.parseInt(data[i]));
- break;
case "#OUT2#":
- position.set(Position.PREFIX_OUT + 2, Integer.parseInt(data[i]));
- break;
case "#OUT3#":
- position.set(Position.PREFIX_OUT + 3, Integer.parseInt(data[i]));
- break;
case "#OUT4#":
- position.set(Position.PREFIX_OUT + 4, Integer.parseInt(data[i]));
+ position.set(Position.PREFIX_OUT + dataTags[i].charAt(3), Integer.parseInt(data[i]));
break;
case "#LAC#":
if (!data[i].isEmpty()) {
diff --git a/src/main/java/org/traccar/protocol/StarcomProtocol.java b/src/main/java/org/traccar/protocol/StarcomProtocol.java
new file mode 100644
index 000000000..63a6143a6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StarcomProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 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;
+
+public class StarcomProtocol extends BaseProtocol {
+
+ public StarcomProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..5ffddb318
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+
+public class StarcomProtocolDecoder extends BaseProtocolDecoder {
+
+ public StarcomProtocolDecoder(StarcomProtocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ sentence = sentence.substring(sentence.indexOf('|') + 1, sentence.lastIndexOf('|'));
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ for (String entry : sentence.split(",")) {
+ int delimiter = entry.indexOf('=');
+ String key = entry.substring(0, delimiter);
+ String value = entry.substring(delimiter + 1);
+ switch (key) {
+ case "unit":
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+ break;
+ case "gps_valid":
+ position.setValid(Integer.parseInt(value) != 0);
+ break;
+ case "datetime_actual":
+ position.setTime(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse(value));
+ break;
+ case "latitude":
+ position.setLatitude(Double.parseDouble(value));
+ break;
+ case "longitude":
+ position.setLongitude(Double.parseDouble(value));
+ break;
+ case "altitude":
+ position.setAltitude(Double.parseDouble(value));
+ break;
+ case "velocity":
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(value)));
+ break;
+ case "heading":
+ position.setCourse(Integer.parseInt(value));
+ break;
+ case "eventid":
+ position.set(Position.KEY_EVENT, Integer.parseInt(value));
+ break;
+ case "mileage":
+ position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(value) * 1000));
+ break;
+ case "satellites":
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
+ break;
+ case "ignition":
+ position.set(Position.KEY_IGNITION, Integer.parseInt(value) != 0);
+ break;
+ case "door":
+ position.set(Position.KEY_DOOR, Integer.parseInt(value) != 0);
+ break;
+ case "arm":
+ position.set(Position.KEY_ARMED, Integer.parseInt(value) != 0);
+ break;
+ case "fuel":
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(value));
+ break;
+ case "rpm":
+ position.set(Position.KEY_RPM, Integer.parseInt(value));
+ break;
+ case "main_voltage":
+ position.set(Position.KEY_POWER, Double.parseDouble(value));
+ break;
+ case "backup_voltage":
+ position.set(Position.KEY_BATTERY, Double.parseDouble(value));
+ break;
+ case "analog1":
+ case "analog2":
+ case "analog3":
+ position.set(Position.PREFIX_ADC + (key.charAt(key.length() - 1) - '0'), Double.parseDouble(value));
+ break;
+ case "extra1":
+ case "extra2":
+ case "extra3":
+ position.set(key, value);
+ default:
+ break;
+ }
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Stl060FrameDecoder.java b/src/main/java/org/traccar/protocol/Stl060FrameDecoder.java
index 455a869ee..f72474e2b 100644
--- a/src/org/traccar/protocol/Stl060FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Stl060FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,8 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
import org.traccar.CharacterDelimiterFrameDecoder;
public class Stl060FrameDecoder extends CharacterDelimiterFrameDecoder {
@@ -27,10 +26,9 @@ public class Stl060FrameDecoder extends CharacterDelimiterFrameDecoder {
}
@Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ protected Object decode(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
- ChannelBuffer result = (ChannelBuffer) super.decode(ctx, channel, buf);
+ ByteBuf result = (ByteBuf) super.decode(ctx, buf);
if (result != null) {
@@ -39,7 +37,7 @@ public class Stl060FrameDecoder extends CharacterDelimiterFrameDecoder {
return result;
} else {
result.skipBytes(index);
- return result.readBytes(result.readableBytes());
+ return result.readRetainedSlice(result.readableBytes());
}
}
diff --git a/src/main/java/org/traccar/protocol/Stl060Protocol.java b/src/main/java/org/traccar/protocol/Stl060Protocol.java
new file mode 100644
index 000000000..2711e936b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Stl060Protocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Stl060Protocol extends BaseProtocol {
+
+ public Stl060Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Stl060FrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Stl060ProtocolDecoder(Stl060Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java
index 26817a5c8..7b0055aa1 100644
--- a/src/org/traccar/protocol/Stl060ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Stl060ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class Stl060ProtocolDecoder extends BaseProtocolDecoder {
- public Stl060ProtocolDecoder(Stl060Protocol protocol) {
+ public Stl060ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -75,8 +76,7 @@ public class Stl060ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
new file mode 100644
index 000000000..8748ecc61
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class SuntechFrameDecoder extends BaseFrameDecoder {
+
+ private ByteBuf readFrame(ByteBuf buf, int delimiterIndex) {
+ ByteBuf frame = buf.readRetainedSlice(delimiterIndex - buf.readerIndex());
+ buf.skipBytes(1); // delimiter
+ return frame;
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.getByte(buf.readerIndex() + 1) == 0) {
+
+ int length = 1 + 2 + buf.getShort(buf.readerIndex() + 1);
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ } else {
+
+ int delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r');
+ while (delimiterIndex > 0) {
+ if (delimiterIndex + 1 < buf.writerIndex() && buf.getByte(delimiterIndex + 1) == '\n') {
+ delimiterIndex = buf.indexOf(delimiterIndex + 1, buf.writerIndex(), (byte) '\r');
+ } else {
+ return readFrame(buf, delimiterIndex);
+ }
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java
index 03a979d04..199885537 100644
--- a/src/org/traccar/protocol/SuntechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,21 +15,15 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+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.model.Command;
-import java.util.List;
-
public class SuntechProtocol extends BaseProtocol {
public SuntechProtocol() {
- super("suntech");
setSupportedDataCommands(
Command.TYPE_OUTPUT_CONTROL,
Command.TYPE_REBOOT_DEVICE,
@@ -38,18 +32,13 @@ public class SuntechProtocol extends BaseProtocol {
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ALARM_ARM,
Command.TYPE_ALARM_DISARM);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new SuntechProtocolEncoder());
- pipeline.addLast("objectDecoder", new SuntechProtocolDecoder(SuntechProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new SuntechFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new SuntechProtocolEncoder(SuntechProtocol.this));
+ pipeline.addLast(new SuntechProtocolDecoder(SuntechProtocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
new file mode 100644
index 000000000..915f764e1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright 2013 - 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.protocol;
+
+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.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+public class SuntechProtocolDecoder extends BaseProtocolDecoder {
+
+ private String prefix;
+
+ private int protocolType;
+ private boolean hbm;
+ private boolean includeAdc;
+ private boolean includeRpm;
+ private boolean includeTemp;
+
+ public SuntechProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setProtocolType(int protocolType) {
+ this.protocolType = protocolType;
+ }
+
+ public int getProtocolType(long deviceId) {
+ return Context.getIdentityManager().lookupAttributeInteger(
+ deviceId, getProtocolName() + ".protocolType", protocolType, false, true);
+ }
+
+ public void setHbm(boolean hbm) {
+ this.hbm = hbm;
+ }
+
+ public boolean isHbm(long deviceId) {
+ return Context.getIdentityManager().lookupAttributeBoolean(
+ deviceId, getProtocolName() + ".hbm", hbm, false, true);
+ }
+
+ public void setIncludeAdc(boolean includeAdc) {
+ this.includeAdc = includeAdc;
+ }
+
+ public boolean isIncludeAdc(long deviceId) {
+ return Context.getIdentityManager().lookupAttributeBoolean(
+ deviceId, getProtocolName() + ".includeAdc", includeAdc, false, true);
+ }
+
+ public void setIncludeRpm(boolean includeRpm) {
+ this.includeRpm = includeRpm;
+ }
+
+ public boolean isIncludeRpm(long deviceId) {
+ return Context.getIdentityManager().lookupAttributeBoolean(
+ deviceId, getProtocolName() + ".includeRpm", includeRpm, false, true);
+ }
+
+ public void setIncludeTemp(boolean includeTemp) {
+ this.includeTemp = includeTemp;
+ }
+
+ public boolean isIncludeTemp(long deviceId) {
+ return Context.getIdentityManager().lookupAttributeBoolean(
+ deviceId, getProtocolName() + ".includeTemp", includeTemp, false, true);
+ }
+
+ private Position decode9(
+ Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
+ int index = 1;
+
+ String type = values[index++];
+
+ if (!type.equals("Location") && !type.equals("Emergency") && !type.equals("Alert")) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (type.equals("Emergency") || type.equals("Alert")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ if (!type.equals("Alert") || getProtocolType(deviceSession.getDeviceId()) == 0) {
+ position.set(Position.KEY_VERSION_FW, values[index++]);
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++] + values[index++]));
+
+ if (getProtocolType(deviceSession.getDeviceId()) == 1) {
+ index += 1; // cell
+ }
+
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Double.parseDouble(values[index++]));
+
+ position.setValid(values[index++].equals("1"));
+
+ if (getProtocolType(deviceSession.getDeviceId()) == 1) {
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
+ }
+
+ return position;
+ }
+
+ private String decodeEmergency(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
+ case 2:
+ return Position.ALARM_PARKING;
+ case 3:
+ return Position.ALARM_POWER_CUT;
+ case 5:
+ case 6:
+ return Position.ALARM_DOOR;
+ case 7:
+ return Position.ALARM_MOVEMENT;
+ case 8:
+ return Position.ALARM_SHOCK;
+ default:
+ return null;
+ }
+ }
+
+ private String decodeAlert(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_OVERSPEED;
+ case 5:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 6:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 14:
+ return Position.ALARM_LOW_BATTERY;
+ case 15:
+ return Position.ALARM_SHOCK;
+ case 16:
+ return Position.ALARM_ACCIDENT;
+ case 46:
+ return Position.ALARM_ACCELERATION;
+ case 47:
+ return Position.ALARM_BRAKING;
+ case 48:
+ return Position.ALARM_ACCIDENT;
+ case 50:
+ return Position.ALARM_JAMMING;
+ default:
+ return null;
+ }
+ }
+ private Position decode4(
+ Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
+ int index = 0;
+
+ String type = values[index++].substring(5);
+
+ if (!type.equals("STT") && !type.equals("ALT")) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+
+ position.set(Position.KEY_VERSION_FW, values[index++]);
+ index += 1; // model
+
+ Network network = new Network();
+
+ for (int i = 0; i < 7; i++) {
+ int cid = Integer.parseInt(values[index++]);
+ int mcc = Integer.parseInt(values[index++]);
+ int mnc = Integer.parseInt(values[index++]);
+ int lac, rssi;
+ if (i == 0) {
+ rssi = Integer.parseInt(values[index++]);
+ lac = Integer.parseInt(values[index++]);
+ } else {
+ lac = Integer.parseInt(values[index++]);
+ rssi = Integer.parseInt(values[index++]);
+ }
+ index += 1; // timing advance
+ if (cid > 0) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi));
+ }
+ }
+
+ position.setNetwork(network);
+
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++]));
+ position.set(Position.KEY_ARCHIVE, values[index++].equals("0") ? true : null);
+ position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ position.set(Position.KEY_STATUS, Integer.parseInt(values[index++]));
+
+ if (values[index].length() == 3) {
+ index += 1; // collaborative network
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++] + values[index++]));
+
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Double.parseDouble(values[index++]));
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+
+ position.setValid(values[index++].equals("1"));
+
+ return position;
+ }
+
+ private Position decode2356(
+ Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException {
+ int index = 0;
+
+ String type = values[index++].substring(5);
+
+ if (!type.equals("STT") && !type.equals("EMG") && !type.equals("EVT")
+ && !type.equals("ALT") && !type.equals("UEX")) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+
+ if (protocol.startsWith("ST3") || protocol.equals("ST500") || protocol.equals("ST600")) {
+ index += 1; // model
+ }
+
+ position.set(Position.KEY_VERSION_FW, values[index++]);
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++] + values[index++]));
+
+ if (!protocol.equals("ST500")) {
+ long cid = Long.parseLong(values[index++], 16);
+ if (protocol.equals("ST600")) {
+ position.setNetwork(new Network(CellTower.from(
+ Integer.parseInt(values[index++]), Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++], 16), cid, Integer.parseInt(values[index++]))));
+ }
+ }
+
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Double.parseDouble(values[index++]));
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+
+ position.setValid(values[index++].equals("1"));
+
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
+ position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
+
+ String io = values[index++];
+ if (io.length() >= 6) {
+ position.set(Position.KEY_IGNITION, io.charAt(0) == '1');
+ position.set(Position.PREFIX_IN + 1, io.charAt(1) == '1');
+ position.set(Position.PREFIX_IN + 2, io.charAt(2) == '1');
+ position.set(Position.PREFIX_IN + 3, io.charAt(3) == '1');
+ position.set(Position.PREFIX_OUT + 1, io.charAt(4) == '1');
+ position.set(Position.PREFIX_OUT + 2, io.charAt(5) == '1');
+ }
+
+ switch (type) {
+ case "STT":
+ position.set(Position.KEY_STATUS, Integer.parseInt(values[index++]));
+ position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ break;
+ case "EMG":
+ position.set(Position.KEY_ALARM, decodeEmergency(Integer.parseInt(values[index++])));
+ break;
+ case "EVT":
+ position.set(Position.KEY_EVENT, Integer.parseInt(values[index++]));
+ break;
+ case "ALT":
+ position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++])));
+ break;
+ case "UEX":
+ int remaining = Integer.parseInt(values[index++]);
+ while (remaining > 0) {
+ String attribute = values[index++];
+ if (attribute.startsWith("CabAVL")) {
+ String[] data = attribute.split(",");
+ double fuel1 = Double.parseDouble(data[2]);
+ if (fuel1 > 0) {
+ position.set("fuel1", fuel1);
+ }
+ double fuel2 = Double.parseDouble(data[3]);
+ if (fuel2 > 0) {
+ position.set("fuel2", fuel2);
+ }
+ } else {
+ String[] pair = attribute.split("=");
+ if (pair.length >= 2) {
+ String value = pair[1].trim();
+ if (value.contains(".")) {
+ value = value.substring(0, value.indexOf('.'));
+ }
+ switch (pair[0].charAt(0)) {
+ case 't':
+ position.set(Position.PREFIX_TEMP + pair[0].charAt(2), Integer.parseInt(value, 16));
+ break;
+ case 'N':
+ position.set("fuel" + pair[0].charAt(2), Integer.parseInt(value, 16));
+ break;
+ case 'Q':
+ position.set("drivingQuality", Integer.parseInt(value, 16));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ remaining -= attribute.length() + 1;
+ }
+ index += 1; // checksum
+ break;
+ default:
+ break;
+ }
+
+ if (isHbm(deviceSession.getDeviceId())) {
+
+ if (index < values.length) {
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromMinutes(Integer.parseInt(values[index++])));
+ }
+
+ if (index < values.length) {
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++]));
+ }
+
+ if (index < values.length && values[index++].equals("0")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (isIncludeAdc(deviceSession.getDeviceId())) {
+ for (int i = 1; i <= 3; i++) {
+ if (!values[index++].isEmpty()) {
+ position.set(Position.PREFIX_ADC + i, Double.parseDouble(values[index - 1]));
+ }
+ }
+ }
+
+ if (isIncludeRpm(deviceSession.getDeviceId()) && index < values.length) {
+ position.set(Position.KEY_RPM, Integer.parseInt(values[index++]));
+ }
+
+ if (values.length - index >= 2) {
+ String driverUniqueId = values[index++];
+ if (values[index++].equals("1") && !driverUniqueId.isEmpty()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
+ }
+ }
+
+ if (isIncludeTemp(deviceSession.getDeviceId())) {
+ for (int i = 1; i <= 3; i++) {
+ String temperature = values[index++];
+ String value = temperature.substring(temperature.indexOf(':') + 1);
+ if (!value.isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, Double.parseDouble(value));
+ }
+ }
+
+ }
+
+ }
+
+ return position;
+ }
+
+ private Position decodeUniversal(
+ Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
+ int index = 0;
+
+ String type = values[index++];
+
+ if (!type.equals("STT") && !type.equals("ALT")) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+
+ int mask = Integer.parseInt(values[index++], 16);
+
+ if (BitUtil.check(mask, 1)) {
+ index += 1; // model
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_VERSION_FW, values[index++]);
+ }
+
+ if (BitUtil.check(mask, 3) && values[index++].equals("0")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++] + values[index++]));
+ }
+
+ if (BitUtil.check(mask, 6)) {
+ index += 1; // cell
+ }
+
+ if (BitUtil.check(mask, 7)) {
+ index += 1; // mcc
+ }
+
+ if (BitUtil.check(mask, 8)) {
+ index += 1; // mnc
+ }
+
+ if (BitUtil.check(mask, 9)) {
+ index += 1; // lac
+ }
+
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 11)) {
+ position.setLatitude(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 12)) {
+ position.setLongitude(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 13)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ }
+
+ if (BitUtil.check(mask, 14)) {
+ position.setCourse(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 16)) {
+ position.setValid(values[index++].equals("1"));
+ }
+
+ return position;
+ }
+
+ private Position decodeBinary(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // length
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(buf.readSlice(5)));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int mask = buf.readUnsignedMedium();
+
+ if (BitUtil.check(mask, 1)) {
+ buf.readUnsignedByte(); // model
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_VERSION_FW, String.format("%d.%d.%d",
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()));
+ }
+
+ if (BitUtil.check(mask, 3) && buf.readUnsignedByte() == 0) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) {
+ position.setTime(new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .getDate());
+ }
+
+ if (BitUtil.check(mask, 6)) {
+ buf.readUnsignedInt(); // cell
+ }
+
+ if (BitUtil.check(mask, 7)) {
+ buf.readUnsignedShort(); // mcc
+ }
+
+ if (BitUtil.check(mask, 8)) {
+ buf.readUnsignedShort(); // mnc
+ }
+
+ if (BitUtil.check(mask, 9)) {
+ buf.readUnsignedShort(); // lac
+ }
+
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(mask, 11)) {
+ long value = buf.readUnsignedInt();
+ if (BitUtil.check(value, 31)) {
+ value = -BitUtil.to(value, 31);
+ }
+ position.setLatitude(value / 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);
+ }
+
+ if (BitUtil.check(mask, 13)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() / 100.0));
+ }
+
+ if (BitUtil.check(mask, 14)) {
+ position.setCourse(buf.readUnsignedShort() / 100.0);
+ }
+
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(mask, 16)) {
+ position.setValid(buf.readUnsignedByte() > 0);
+ }
+
+ if (BitUtil.check(mask, 17)) {
+ int input = buf.readUnsignedByte();
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
+ }
+
+ if (BitUtil.check(mask, 18)) {
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(mask, 19)) {
+ int value = buf.readUnsignedByte();
+ if (type == 0x82) {
+ position.set(Position.KEY_ALARM, decodeAlert(value));
+ }
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (buf.getByte(buf.readerIndex() + 1) == 0) {
+
+ return decodeBinary(channel, remoteAddress, buf);
+
+ } else {
+
+ String[] values = buf.toString(StandardCharsets.US_ASCII).split(";");
+ prefix = values[0];
+
+ if (prefix.length() < 5) {
+ return decodeUniversal(channel, remoteAddress, values);
+ } else if (prefix.startsWith("ST9")) {
+ return decode9(channel, remoteAddress, values);
+ } else if (prefix.startsWith("ST4")) {
+ return decode4(channel, remoteAddress, values);
+ } else {
+ return decode2356(channel, remoteAddress, prefix.substring(0, 5), values);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
new file mode 100644
index 000000000..3b4995110
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
@@ -0,0 +1,78 @@
+/*
+ * 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 io.netty.channel.Channel;
+import org.traccar.BasePipelineFactory;
+import org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class SuntechProtocolEncoder extends StringProtocolEncoder {
+
+ public SuntechProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private String getPrefix(Channel channel) {
+ String prefix = "SA200CMD";
+ if (channel != null) {
+ SuntechProtocolDecoder protocolDecoder =
+ BasePipelineFactory.getHandler(channel.pipeline(), SuntechProtocolDecoder.class);
+ if (protocolDecoder != null) {
+ String decoderPrefix = protocolDecoder.getPrefix();
+ if (decoderPrefix != null && decoderPrefix.length() > 5) {
+ prefix = decoderPrefix.substring(0, decoderPrefix.length() - 3) + "CMD";
+ }
+ }
+ }
+ return prefix;
+ }
+
+ @Override
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ String prefix = getPrefix(channel);
+
+ switch (command.getType()) {
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatCommand(command, prefix + ";%s;02;Reboot\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, prefix + ";%s;02;\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);
+ }
+ }
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, prefix + ";%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);
+ case Command.TYPE_ALARM_ARM:
+ return formatCommand(command, prefix + ";%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);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SupermateProtocol.java b/src/main/java/org/traccar/protocol/SupermateProtocol.java
new file mode 100644
index 000000000..46625ddc7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SupermateProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class SupermateProtocol extends BaseProtocol {
+
+ public SupermateProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "#"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new SupermateProtocolDecoder(SupermateProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/SupermateProtocolDecoder.java b/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java
index d9b58a7f4..40a25bb91 100644
--- a/src/org/traccar/protocol/SupermateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SupermateProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,10 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -31,7 +33,7 @@ import java.util.regex.Pattern;
public class SupermateProtocolDecoder extends BaseProtocolDecoder {
- public SupermateProtocolDecoder(SupermateProtocol protocol) {
+ public SupermateProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,8 +67,7 @@ public class SupermateProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
String imei = parser.next();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
@@ -111,7 +112,8 @@ public class SupermateProtocolDecoder extends BaseProtocolDecoder {
String content = String.format("#1:%s:1:*,00000000,UP,%02x%02x%02x,%02x%02x%02x#", imei,
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH),
calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
- channel.write(ChannelBuffers.copiedBuffer(content, StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.copiedBuffer(content, StandardCharsets.US_ASCII), remoteAddress));
}
return position;
diff --git a/src/main/java/org/traccar/protocol/SviasProtocol.java b/src/main/java/org/traccar/protocol/SviasProtocol.java
new file mode 100644
index 000000000..accfa173f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SviasProtocol.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2018 - 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 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.model.Command;
+
+public class SviasProtocol extends BaseProtocol {
+
+ public SviasProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_SET_ODOMETER,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_ALARM_ARM,
+ Command.TYPE_ALARM_DISARM,
+ Command.TYPE_ALARM_REMOVE);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "]"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new SviasProtocolEncoder(SviasProtocol.this));
+ pipeline.addLast(new SviasProtocolDecoder(SviasProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java b/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java
new file mode 100644
index 000000000..7e783f6cd
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SviasProtocolDecoder.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.PatternBuilder;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+import org.traccar.DeviceSession;
+import org.traccar.helper.Parser;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+public class SviasProtocolDecoder extends BaseProtocolDecoder {
+
+ public SviasProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("[") // delimiter
+ .number("d{4},") // hardware version
+ .number("d{4},") // software version
+ .number("d+,") // index
+ .number("(d+),") // imei
+ .number("d+,") // hour meter
+ .number("(d+)(dd)(dd),") // date (dmmyy)
+ .number("(d+)(dd)(dd),") // time (hmmss)
+ .number("(-?)(d+)(dd)(d{5}),") // latitude
+ .number("(-?)(d+)(dd)(d{5}),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(d+),") // odometer
+ .number("(d+),") // input
+ .number("(d+),") // output / status
+ .number("(d),")
+ .number("(d),")
+ .number("(d+),") // power
+ .number("(d+),") // battery level
+ .number("(d+),") // rssi
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
+ throws Exception {
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage("@", remoteAddress));
+ }
+
+ 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.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_MIN));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_MIN));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble() * 0.01));
+ position.setCourse(parser.nextDouble() * 0.01);
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt() * 100);
+
+ int input = parser.nextInt();
+ int output = parser.nextInt();
+
+ position.set(Position.KEY_ALARM, BitUtil.check(input, 0) ? Position.ALARM_SOS : null);
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 4));
+ position.setValid(BitUtil.check(output, 0));
+
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.001);
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java b/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
new file mode 100644
index 000000000..d218f63ce
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class SviasProtocolEncoder extends StringProtocolEncoder {
+
+ public SviasProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "%s", Command.KEY_DATA);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, "AT+STR=1*");
+ case Command.TYPE_SET_ODOMETER:
+ return formatCommand(command, "AT+ODT=%s*", Command.KEY_DATA);
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, "AT+OUT=1,1*");
+ case Command.TYPE_ENGINE_RESUME:
+ return formatCommand(command, "AT+OUT=1,0*");
+ case Command.TYPE_ALARM_ARM:
+ return formatCommand(command, "AT+OUT=2,1*");
+ case Command.TYPE_ALARM_DISARM:
+ return formatCommand(command, "AT+OUT=2,0*");
+ case Command.TYPE_ALARM_REMOVE:
+ return formatCommand(command, "AT+PNC=600*");
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/T55Protocol.java b/src/main/java/org/traccar/protocol/T55Protocol.java
new file mode 100644
index 000000000..f5ec19094
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/T55Protocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class T55Protocol extends BaseProtocol {
+
+ public T55Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new T55ProtocolDecoder(T55Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new T55ProtocolDecoder(T55Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index 6b4ee6ebd..b75addfae 100644
--- a/src/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,23 +15,25 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.socket.DatagramChannel;
+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.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.channels.DatagramChannel;
import java.util.Date;
import java.util.regex.Pattern;
public class T55ProtocolDecoder extends BaseProtocolDecoder {
- public T55ProtocolDecoder(T55Protocol protocol) {
+ public T55ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -53,7 +55,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.number(",(d+)") // imei
.expression(",([01])") // ignition
.number(",(d+)") // fuel
- .number(",(d+)").optional(5) // battery
+ .number(",(d+)").optional(7) // battery
.number("((?:,d+)+)?") // parameters
.any()
.compile();
@@ -94,16 +96,28 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_GPIOP = new PatternBuilder()
+ .text("$GPIOP,")
+ .number("[01]{8},") // inputs
+ .number("[01]{8},") // outputs
+ .number("d+.d+,") // adc 1
+ .number("d+.d+,") // adc 2
+ .number("d+.d+,") // adc 3
+ .number("d+.d+,") // adc 4
+ .number("(d+.d+),") // power
+ .number("(d+.d+)") // battery
+ .any()
+ .compile();
+
private Position position = null;
private Position decodeGprmc(
DeviceSession deviceSession, String sentence, SocketAddress remoteAddress, Channel channel) {
- if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)) {
- if (Context.getIdentityManager().lookupAttributeBoolean(
- deviceSession.getDeviceId(), getProtocolName() + ".ack", false, true)) {
- channel.write("OK1\r\n");
- }
+ 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));
}
Parser parser = new Parser(PATTERN_GPRMC, sentence);
@@ -111,8 +125,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
if (deviceSession != null) {
position.setDeviceId(deviceSession.getDeviceId());
@@ -166,8 +179,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -189,8 +201,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date());
@@ -210,8 +221,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime());
@@ -228,6 +238,24 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeGpiop(DeviceSession deviceSession, String sentence) {
+
+ Parser parser = new Parser(PATTERN_GPIOP, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -252,12 +280,16 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
if (sentence.startsWith("$PGID")) {
getDeviceSession(channel, remoteAddress, sentence.substring(6, sentence.length() - 3));
+ } else if (sentence.startsWith("$DEVID")) {
+ getDeviceSession(channel, remoteAddress, sentence.substring(7, sentence.lastIndexOf('*')));
} else if (sentence.startsWith("$PCPTI")) {
getDeviceSession(channel, remoteAddress, sentence.substring(7, sentence.indexOf(",", 7)));
} else if (sentence.startsWith("IMEI")) {
- getDeviceSession(channel, remoteAddress, sentence.substring(5, sentence.length()));
+ getDeviceSession(channel, remoteAddress, sentence.substring(5));
+ } else if (sentence.startsWith("$IMEI")) {
+ getDeviceSession(channel, remoteAddress, sentence.substring(6));
} else if (sentence.startsWith("$GPFID")) {
- deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(7, sentence.length()));
+ deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(7));
if (deviceSession != null && position != null) {
Position position = this.position;
position.setDeviceId(deviceSession.getDeviceId());
@@ -274,6 +306,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return decodeGprma(deviceSession, sentence);
} else if (sentence.startsWith("$TRCCR") && deviceSession != null) {
return decodeTrccr(deviceSession, sentence);
+ } else if (sentence.startsWith("$GPIOP")) {
+ return decodeGpiop(deviceSession, sentence);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/T57FrameDecoder.java b/src/main/java/org/traccar/protocol/T57FrameDecoder.java
new file mode 100644
index 000000000..14ba31453
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/T57FrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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 T57FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ String type = buf.toString(buf.readerIndex() + 5, 2, StandardCharsets.US_ASCII);
+ int count = type.equals("F3") ? 12 : 14;
+
+ int index = 0;
+ while (index >= 0 && count > 0) {
+ index = buf.indexOf(index + 1, buf.writerIndex(), (byte) '#');
+ if (index > 0) {
+ count -= 1;
+ }
+ }
+
+ return index > 0 ? buf.readRetainedSlice(index + 1 - buf.readerIndex()) : null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/T57Protocol.java b/src/main/java/org/traccar/protocol/T57Protocol.java
new file mode 100644
index 000000000..f67f82318
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/T57Protocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class T57Protocol extends BaseProtocol {
+
+ public T57Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new T57FrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new T57ProtocolDecoder(T57Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java
new file mode 100644
index 000000000..2a3cca3e4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/T57ProtocolDecoder.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.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 T57ProtocolDecoder extends BaseProtocolDecoder {
+
+ public T57ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("*T57#")
+ .number("Fd#") // type
+ .number("([^#]+)#") // device id
+ .number("(dd)(dd)(dd)#") // date (ddmmyy)
+ .number("(dd)(dd)(dd)#") // time (hhmmss)
+ .number("(dd)(dd.d+)#") // latitude
+ .expression("([NS])#")
+ .number("(ddd)(dd.d+)#") // longitude
+ .expression("([EW])#")
+ .expression("[^#]+#")
+ .number("(d+.d+)#") // speed
+ .number("(d+.d+)#") // altitude
+ .expression("([AV])") // valid
+ .number("d#") // fix type
+ .number("(d+.d+)#") // battery
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+
+ position.setValid(parser.next().equals("A"));
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/T800xProtocol.java b/src/main/java/org/traccar/protocol/T800xProtocol.java
index 830ff4de6..8b91265cb 100644
--- a/src/org/traccar/protocol/T800xProtocol.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,31 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class T800xProtocol extends BaseProtocol {
public T800xProtocol() {
- super("t800x");
setSupportedDataCommands(
Command.TYPE_CUSTOM);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2, -5, 0));
- pipeline.addLast("objectEncoder", new T800xProtocolEncoder());
- pipeline.addLast("objectDecoder", new T800xProtocolDecoder(T800xProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2, -5, 0));
+ 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
new file mode 100644
index 000000000..9b146ec90
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -0,0 +1,314 @@
+/*
+ * 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.math.BigInteger;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class T800xProtocolDecoder extends BaseProtocolDecoder {
+
+ private short header = DEFAULT_HEADER;
+
+ public short getHeader() {
+ return header;
+ }
+
+ public T800xProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final short DEFAULT_HEADER = 0x2323;
+
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_GPS = 0x02;
+ public static final int MSG_HEARTBEAT = 0x03;
+ public static final int MSG_ALARM = 0x04;
+ public static final int MSG_NETWORK = 0x05;
+ public static final int MSG_COMMAND = 0x81;
+
+ private void sendResponse(Channel channel, short header, int type, int index, ByteBuf imei, int alarm) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer(alarm > 0 ? 16 : 15);
+ response.writeShort(header);
+ response.writeByte(type);
+ response.writeShort(response.capacity()); // length
+ response.writeShort(index);
+ response.writeBytes(imei);
+ if (alarm > 0) {
+ response.writeByte(alarm);
+ }
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_POWER_CUT;
+ case 2:
+ return Position.ALARM_LOW_BATTERY;
+ case 3:
+ return Position.ALARM_SOS;
+ case 4:
+ return Position.ALARM_OVERSPEED;
+ case 5:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 6:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 7:
+ return Position.ALARM_TOW;
+ case 8:
+ case 10:
+ return Position.ALARM_VIBRATION;
+ case 21:
+ return Position.ALARM_JAMMING;
+ case 23:
+ return Position.ALARM_POWER_RESTORED;
+ case 24:
+ return Position.ALARM_LOW_POWER;
+ default:
+ return null;
+ }
+ }
+
+ private Date readDate(ByteBuf buf) {
+ return new DateBuilder()
+ .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))
+ .getDate();
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ header = buf.readShort();
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // length
+ int index = buf.readUnsignedShort();
+ ByteBuf imei = buf.readSlice(8);
+
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, ByteBufUtil.hexDump(imei).substring(1));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (type != MSG_GPS && type != MSG_ALARM) {
+ sendResponse(channel, header, type, index, imei, 0);
+ }
+
+ if (type == MSG_GPS || type == MSG_ALARM) {
+
+ return decodePosition(channel, deviceSession, buf, type, index, imei);
+
+ } else if (type == MSG_NETWORK) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, readDate(buf));
+
+ position.set(Position.KEY_OPERATOR, buf.readCharSequence(
+ buf.readUnsignedByte(), StandardCharsets.UTF_16LE).toString());
+ position.set("networkTechnology", buf.readCharSequence(
+ buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString());
+ position.set("networkBand", buf.readCharSequence(
+ buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString());
+ buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII); // imsi
+ position.set(Position.KEY_ICCID, buf.readCharSequence(
+ buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString());
+
+ return position;
+
+ } else if (type == MSG_COMMAND) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ buf.readUnsignedByte(); // protocol number
+
+ position.set(Position.KEY_RESULT, buf.toString(StandardCharsets.UTF_16LE));
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+ private Position decodePosition(
+ Channel channel, DeviceSession deviceSession,
+ ByteBuf buf, int type, int index, ByteBuf imei) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, index);
+
+ if (header != 0x2727) {
+
+ buf.readUnsignedShort(); // acc on interval
+ buf.readUnsignedShort(); // acc off interval
+ buf.readUnsignedByte(); // angle compensation
+ buf.readUnsignedShort(); // distance compensation
+
+ position.set(Position.KEY_RSSI, BitUtil.to(buf.readUnsignedShort(), 7));
+
+ }
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_SATELLITES, BitUtil.to(status, 5));
+
+ if (header != 0x2727) {
+
+ buf.readUnsignedByte(); // gsensor manager status
+ buf.readUnsignedByte(); // other flags
+ buf.readUnsignedByte(); // heartbeat
+ buf.readUnsignedByte(); // relay status
+ buf.readUnsignedShort(); // drag alarm setting
+
+ int io = buf.readUnsignedShort();
+ position.set(Position.KEY_IGNITION, BitUtil.check(io, 14));
+ position.set("ac", BitUtil.check(io, 13));
+ for (int i = 0; i <= 2; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(io, 7 + i));
+ }
+
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+
+ }
+
+ int alarm = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+
+ if (header != 0x2727) {
+
+ buf.readUnsignedByte(); // reserved
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+
+ int battery = BcdUtil.readInteger(buf, 2);
+ if (battery == 0) {
+ battery = 100;
+ }
+ position.set(Position.KEY_BATTERY, battery);
+
+ }
+
+ if (BitUtil.check(status, 6)) {
+
+ position.setValid(!BitUtil.check(status, 7));
+ position.setTime(readDate(buf));
+ position.setAltitude(buf.readFloatLE());
+ position.setLongitude(buf.readFloatLE());
+ position.setLatitude(buf.readFloatLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4) * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+
+ } else {
+
+ getLastLocation(position, readDate(buf));
+
+ int mcc = buf.readUnsignedShortLE();
+ int mnc = buf.readUnsignedShortLE();
+
+ if (mcc != 0xffff && mnc != 0xffff) {
+ Network network = new Network();
+ for (int i = 0; i < 3; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShortLE(), buf.readUnsignedShortLE()));
+ }
+ position.setNetwork(network);
+ }
+
+ }
+
+ if (header == 0x2727) {
+
+ byte[] accelerationBytes = new byte[5];
+ buf.readBytes(accelerationBytes);
+ long acceleration = new BigInteger(accelerationBytes).longValue();
+ double accelerationZ = BitUtil.between(acceleration, 8, 15) + BitUtil.between(acceleration, 4, 8) * 0.1;
+ if (!BitUtil.check(acceleration, 15)) {
+ accelerationZ = -accelerationZ;
+ }
+ double accelerationY = BitUtil.between(acceleration, 20, 27) + BitUtil.between(acceleration, 16, 20) * 0.1;
+ if (!BitUtil.check(acceleration, 27)) {
+ accelerationY = -accelerationY;
+ }
+ double accelerationX = BitUtil.between(acceleration, 28, 32) + BitUtil.between(acceleration, 32, 39) * 0.1;
+ if (!BitUtil.check(acceleration, 39)) {
+ accelerationX = -accelerationX;
+ }
+ position.set(Position.KEY_G_SENSOR, "[" + accelerationX + "," + accelerationY + "," + accelerationZ + "]");
+
+ position.set(Position.KEY_BATTERY_LEVEL, BcdUtil.readInteger(buf, 2));
+ position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte());
+ position.set("lightSensor", BcdUtil.readInteger(buf, 2) * 0.1);
+ position.set(Position.KEY_BATTERY, BcdUtil.readInteger(buf, 2) * 0.1);
+ position.set("solarPanel", BcdUtil.readInteger(buf, 2) * 0.1);
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+
+ int inputStatus = buf.readUnsignedShort();
+ position.set(Position.KEY_IGNITION, BitUtil.check(inputStatus, 2));
+ position.set(Position.KEY_RSSI, BitUtil.between(inputStatus, 4, 11));
+
+ buf.readUnsignedShort(); // ignition on upload interval
+ buf.readUnsignedInt(); // ignition off upload interval
+ buf.readUnsignedByte(); // angle upload interval
+ buf.readUnsignedShort(); // distance upload interval
+ buf.readUnsignedByte(); // heartbeat
+
+ } else if (buf.readableBytes() >= 2) {
+
+ position.set(Position.KEY_POWER, BcdUtil.readInteger(buf, 4) * 0.01);
+
+ }
+
+ sendResponse(channel, header, type, index, imei, alarm);
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/T800xProtocolEncoder.java b/src/main/java/org/traccar/protocol/T800xProtocolEncoder.java
index 6ed5dbccd..74587c8b1 100644
--- a/src/org/traccar/protocol/T800xProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,13 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
+import org.traccar.Protocol;
-import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
public class T800xProtocolEncoder extends BaseProtocolEncoder {
@@ -30,16 +31,19 @@ public class T800xProtocolEncoder extends BaseProtocolEncoder {
public static final int MODE_BROADCAST = 0x02;
public static final int MODE_FORWARD = 0x03;
- private ChannelBuffer encodeContent(Command command, String content) {
+ public T800xProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(Command command, short header, String content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
- buf.writeByte('#');
- buf.writeByte('#');
+ buf.writeShort(header);
buf.writeByte(T800xProtocolDecoder.MSG_COMMAND);
buf.writeShort(7 + 8 + 1 + content.length());
buf.writeShort(1); // serial number
- buf.writeBytes(DatatypeConverter.parseHexBinary("0" + getUniqueId(command.getDeviceId())));
+ buf.writeBytes(DataConverter.parseHex("0" + getUniqueId(command.getDeviceId())));
buf.writeByte(MODE_SETTING);
buf.writeBytes(content.getBytes(StandardCharsets.US_ASCII));
@@ -47,17 +51,19 @@ public class T800xProtocolEncoder extends BaseProtocolEncoder {
}
@Override
- protected Object encodeCommand(Command command) {
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ short header = T800xProtocolDecoder.DEFAULT_HEADER;
+ if (channel != null) {
+ header = channel.pipeline().get(T800xProtocolDecoder.class).getHeader();
+ }
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return encodeContent(command, command.getString(Command.KEY_DATA));
+ return encodeContent(command, header, command.getString(Command.KEY_DATA));
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/TaipProtocol.java b/src/main/java/org/traccar/protocol/TaipProtocol.java
new file mode 100644
index 000000000..b8f40a183
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TaipProtocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TaipProtocol extends BaseProtocol {
+
+ public TaipProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '<'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
index e7117a5c9..8a0cb870b 100644
--- a/src/org/traccar/protocol/TaipProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
@@ -33,7 +35,7 @@ import java.util.regex.Pattern;
public class TaipProtocolDecoder extends BaseProtocolDecoder {
- public TaipProtocolDecoder(TaipProtocol protocol) {
+ public TaipProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -47,7 +49,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.groupEnd("?")
.number("(d{5})") // seconds
.or()
- .expression("(?:RGP|RCQ|RBR)") // type
+ .expression("(?:RGP|RCQ|RCV|RBR)") // type
.number("(dd)?") // event
.number("(dd)(dd)(dd)") // date (mmddyy)
.number("(dd)(dd)(dd)") // time (hhmmss)
@@ -62,12 +64,33 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)") // speed
.number("(ddd)") // course
.groupBegin()
+ .number("([023])") // fix mode
+ .number("xx") // data age
+ .number("(xx)") // input
+ .number("(dd)") // event
+ .number("(dd)") // hdop
+ .or()
+ .groupBegin()
.number("(xx)") // input
.number("(xx)") // satellites
.number("(ddd)") // battery
.number("(x{8})") // odometer
.number("[01]") // gps power
+ .groupBegin()
+ .number("([023])") // fix mode
+ .number("(dd)") // pdop
+ .number("dd") // satellites
+ .number("xxxx") // data age
+ .number("[01]") // modem power
+ .number("[0-5]") // gsm status
+ .number("(dd)") // rssi
+ .number("([-+]dddd)") // temperature 1
+ .number("xx") // seconds from last
+ .number("([-+]dddd)") // temperature 2
+ .number("xx") // seconds from last
+ .groupEnd("?")
.groupEnd("?")
+ .groupEnd()
.any()
.compile();
@@ -101,9 +124,9 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
+ Boolean valid = null;
Integer event = null;
if (parser.hasNext(3)) {
@@ -117,27 +140,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
event = parser.nextInt();
}
- if (event != null) {
- switch (event) {
- case 22:
- position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
- break;
- case 23:
- position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
- break;
- case 24:
- position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
- break;
- case 26:
- case 28:
- position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
- break;
- default:
- position.set(Position.KEY_EVENT, event);
- break;
- }
- }
-
if (parser.hasNext(6)) {
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
}
@@ -155,13 +157,51 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextDouble(0));
if (parser.hasNext(4)) {
+ valid = parser.nextInt() > 0;
+ int input = parser.nextHexInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 7));
+ position.set(Position.KEY_INPUT, input);
+ event = parser.nextInt();
+ position.set(Position.KEY_HDOP, parser.nextInt());
+ }
+
+ if (parser.hasNext(4)) {
position.set(Position.KEY_INPUT, parser.nextHexInt(0));
position.set(Position.KEY_SATELLITES, parser.nextHexInt(0));
position.set(Position.KEY_BATTERY, parser.nextInt(0));
position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
}
- position.setValid(true);
+ if (parser.hasNext(4)) {
+ valid = parser.nextInt() > 0;
+ position.set(Position.KEY_PDOP, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt() * 0.01);
+ position.set(Position.PREFIX_TEMP + 2, parser.nextInt() * 0.01);
+ }
+
+ position.setValid(valid == null || valid);
+
+ if (event != null) {
+ position.set(Position.KEY_EVENT, event);
+ switch (event) {
+ case 22:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 23:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 24:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ break;
+ case 26:
+ case 28:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ break;
+ }
+ }
String[] attributes = null;
beginIndex = sentence.indexOf(';');
@@ -231,14 +271,13 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
if (deviceSession != null) {
if (channel != null) {
if (messageIndex != null) {
- String response = ">ACK;" + messageIndex + ";ID=" + uniqueId + ";*";
+ String response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*";
response += String.format("%02X", Checksum.xor(response)) + "<";
- channel.write(response, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
} else {
- channel.write(uniqueId, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(uniqueId, remoteAddress));
}
}
-
return position;
}
diff --git a/src/main/java/org/traccar/protocol/TechTltProtocol.java b/src/main/java/org/traccar/protocol/TechTltProtocol.java
new file mode 100644
index 000000000..0cffb452d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TechTltProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 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;
+
+public class TechTltProtocol extends BaseProtocol {
+
+ public TechTltProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..17f5c80fa
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TechTltProtocolDecoder.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class TechTltProtocolDecoder extends BaseProtocolDecoder {
+
+ public TechTltProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN_STATUS = new PatternBuilder()
+ .number("(d+),") // id
+ .text("INFOGPRS,")
+ .number("V Bat=(d+.d),") // battery
+ .number("TEMP=(d+),") // temperature
+ .expression("[^,]*,")
+ .number("(d+)") // rssi
+ .compile();
+
+ private static final Pattern PATTERN_POSITION = new PatternBuilder()
+ .number("(d+)") // id
+ .text("*POS=Y,")
+ .number("(dd):(dd):(dd),") // time
+ .number("(dd)/(dd)/(dd),") // date
+ .number("(dd)(dd.d+)") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+)") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(d+.d+),") // altitude
+ .number("(d+),") // satellites
+ .number("(d+),") // lac
+ .number("(d+)") // cid
+ .compile();
+
+ private Position decodeStatus(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_STATUS, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_DEVICE_TEMP, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ return position;
+ }
+
+ private Position decodeLocation(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_POSITION, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setNetwork(new Network(CellTower.fromLacCid(parser.nextInt(), parser.nextInt())));
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = ((String) msg).trim();
+ if (sentence.contains("INFO")) {
+ return decodeStatus(channel, remoteAddress, sentence);
+ } else if (sentence.contains("POS")) {
+ return decodeLocation(channel, remoteAddress, sentence);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TekFrameDecoder.java b/src/main/java/org/traccar/protocol/TekFrameDecoder.java
new file mode 100644
index 000000000..c56e1f8f2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TekFrameDecoder.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 org.traccar.helper.BitUtil;
+
+public class TekFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 17) {
+ return null;
+ }
+
+ int length = 17 + buf.getUnsignedByte(16) + (BitUtil.from(buf.getUnsignedByte(15), 6) << 6);
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TekProtocol.java b/src/main/java/org/traccar/protocol/TekProtocol.java
new file mode 100644
index 000000000..c1d78e6f5
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TekProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class TekProtocol extends BaseProtocol {
+
+ public TekProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..33ff51d2d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TekProtocolDecoder.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.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 java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class TekProtocolDecoder extends BaseProtocolDecoder {
+
+ public TekProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .number(",d+,")
+ .number("(dd)(dd)(dd).d,") // time (hhmmss)
+ .number("(dd)(dd.d+)") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+)") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+),") // hdop
+ .number("(d+.d+),") // altitude
+ .number("(d+),") // fix mode
+ .number("(d+.d+),") // course
+ .number("d+.d+,") // speed km
+ .number("(d+.d+),") // speed kn
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(d+),") // satellites
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // product type
+ buf.readUnsignedByte(); // hardware version
+ buf.readUnsignedByte(); // firmware version
+ buf.readUnsignedByte(); // contact reason
+ buf.readUnsignedByte(); // alarm / status
+ buf.readUnsignedByte(); // rssi
+ buf.readUnsignedByte(); // battery / status
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type = BitUtil.to(buf.readUnsignedByte(), 6);
+ buf.readUnsignedByte(); // length
+
+ if (type == 4 || type == 8) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int count = buf.readUnsignedShort();
+ buf.readUnsignedByte(); // hours / tickets
+ buf.readUnsignedByte(); // error code
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedByte(); // logger speed
+ buf.readUnsignedByte(); // login time
+ buf.readUnsignedByte(); // minutes
+
+ for (int i = 0; i < count; i++) {
+ position.set("rssi" + (i + 1), buf.readUnsignedByte());
+ position.set("temp" + (i + 1), buf.readUnsignedByte() - 30);
+ int data = buf.readUnsignedShort();
+ position.set("src" + (i + 1), BitUtil.from(data, 10));
+ position.set("ullage" + (i + 1), BitUtil.to(data, 10));
+ }
+
+ return position;
+
+ } else if (type == 17) {
+
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
+
+ Parser parser = new Parser(PATTERN, sentence);
+ 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.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+
+ position.setAltitude(parser.nextDouble());
+ position.setValid(parser.nextInt() > 0);
+ position.setCourse(parser.nextDouble());
+ position.setSpeed(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/TelemaxProtocol.java b/src/main/java/org/traccar/protocol/TelemaxProtocol.java
new file mode 100644
index 000000000..838da9df1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TelemaxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class TelemaxProtocol extends BaseProtocol {
+
+ public TelemaxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TelemaxProtocolDecoder(TelemaxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java
new file mode 100644
index 000000000..9369ab101
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TelemaxProtocolDecoder.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class TelemaxProtocolDecoder extends BaseProtocolDecoder {
+
+ public TelemaxProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private String readValue(String sentence, int[] index, int length) {
+ String value = sentence.substring(index[0], index[0] + length);
+ index[0] += length;
+ return value;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (sentence.startsWith("%")) {
+ int length = Integer.parseInt(sentence.substring(1, 3));
+ getDeviceSession(channel, remoteAddress, sentence.substring(3, 3 + length));
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int[] index = {0};
+
+ if (!readValue(sentence, index, 1).equals("Y")) {
+ return null;
+ }
+
+ readValue(sentence, index, 8); // command id
+ readValue(sentence, index, 6); // password
+ readValue(sentence, index, Integer.parseInt(readValue(sentence, index, 2), 16)); // unit id
+ readValue(sentence, index, 2); // frame count
+
+ readValue(sentence, index, 2); // data format
+
+ int interval = Integer.parseInt(readValue(sentence, index, 4), 16);
+
+ readValue(sentence, index, 2); // info flags
+ readValue(sentence, index, 2); // version
+
+ int count = Integer.parseInt(readValue(sentence, index, 2), 16);
+
+ Date time = null;
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int speed = Integer.parseInt(readValue(sentence, index, 2), 16);
+
+ position.setValid(BitUtil.check(speed, 7));
+ position.setSpeed(BitUtil.to(speed, 7));
+
+ position.setLongitude((Integer.parseInt(readValue(sentence, index, 6), 16) - 5400000) / 30000.0);
+ position.setLatitude((Integer.parseInt(readValue(sentence, index, 6), 16) - 5400000) / 30000.0);
+
+ if (i == 0 | i == count - 1) {
+ time = new SimpleDateFormat("yyMMddHHmmss").parse(readValue(sentence, index, 12));
+ position.set(Position.KEY_STATUS, readValue(sentence, index, 8));
+ } else {
+ time = new Date(time.getTime() + interval * 1000);
+ }
+
+ position.setTime(time);
+
+ positions.add(position);
+
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/org/traccar/protocol/TelicFrameDecoder.java b/src/main/java/org/traccar/protocol/TelicFrameDecoder.java
index 245be28fb..d1fef1b5b 100644
--- a/src/org/traccar/protocol/TelicFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/TelicFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,32 +15,33 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.traccar.BaseFrameDecoder;
-public class TelicFrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class TelicFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 4) {
return null;
}
- long length = buf.getUnsignedInt(buf.readerIndex());
+ long length = buf.getUnsignedIntLE(buf.readerIndex());
if (length < 1024) {
if (buf.readableBytes() >= length + 4) {
- buf.readUnsignedInt();
- return buf.readBytes((int) length);
+ buf.readUnsignedIntLE();
+ return buf.readRetainedSlice((int) length);
}
} else {
int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
if (endIndex >= 0) {
- ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex());
+ ByteBuf frame = buf.readRetainedSlice(endIndex - buf.readerIndex());
buf.readByte();
if (frame.readableBytes() > 0) {
return frame;
diff --git a/src/main/java/org/traccar/protocol/TelicProtocol.java b/src/main/java/org/traccar/protocol/TelicProtocol.java
new file mode 100644
index 000000000..991befa19
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TelicProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TelicProtocol extends BaseProtocol {
+
+ public TelicProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TelicFrameDecoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new TelicProtocolDecoder(TelicProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TelicProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
index 2c0d714c6..457687b2e 100644
--- a/src/org/traccar/protocol/TelicProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class TelicProtocolDecoder extends BaseProtocolDecoder {
- public TelicProtocolDecoder(TelicProtocol protocol) {
+ public TelicProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -41,8 +42,8 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)(dd)(dd)") // date (ddmmyy)
.number("(dd)(dd)(dd),") // time (hhmmss)
.groupBegin()
- .number("(ddd)(dd)(dddd),") // longitude
- .number("(dd)(dd)(dddd),") // latitude
+ .number("(-?d{9}),") // longitude
+ .number("(-?d{8}),") // latitude
.or()
.number("(-?d+),") // longitude
.number("(-?d+),") // latitude
@@ -87,18 +88,16 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- int event = parser.nextInt(0);
+ int event = parser.nextInt();
position.set(Position.KEY_EVENT, event);
-
position.set(Position.KEY_ALARM, decodeAlarm(event));
if (event == 11) {
@@ -109,25 +108,22 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder {
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
- if (parser.hasNext(6)) {
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN));
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN));
+ if (parser.hasNext(2)) {
+ position.setLongitude(parser.nextDouble() / 1000000);
+ position.setLatitude(parser.nextDouble() / 1000000);
}
if (parser.hasNext(2)) {
- position.setLongitude(parser.nextDouble(0) / 10000);
- position.setLatitude(parser.nextDouble(0) / 10000);
+ position.setLongitude(parser.nextDouble() / 10000);
+ position.setLatitude(parser.nextDouble() / 10000);
}
- position.setValid(parser.nextInt(0) != 1);
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
-
- if (parser.hasNext()) {
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- }
+ position.setValid(parser.nextInt() != 1);
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_BATTERY, parser.nextInt());
return position;
}
diff --git a/src/org/traccar/protocol/TeltonikaFrameDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
index e7313b722..c30fee6e3 100644
--- a/src/org/traccar/protocol/TeltonikaFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,36 +15,37 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.traccar.BaseFrameDecoder;
-public class TeltonikaFrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class TeltonikaFrameDecoder extends BaseFrameDecoder {
private static final int MESSAGE_MINIMUM_LENGTH = 12;
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ while (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) {
+ buf.skipBytes(1);
+ }
- // Check minimum length
if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
return null;
}
- // Read packet
int length = buf.getUnsignedShort(buf.readerIndex());
if (length > 0) {
if (buf.readableBytes() >= (length + 2)) {
- return buf.readBytes(length + 2);
+ return buf.readRetainedSlice(length + 2);
}
} else {
int dataLength = buf.getInt(buf.readerIndex() + 4);
if (buf.readableBytes() >= (dataLength + 12)) {
- return buf.readBytes(dataLength + 12);
+ return buf.readRetainedSlice(dataLength + 12);
}
}
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
new file mode 100644
index 000000000..5817b86be
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class TeltonikaProtocol extends BaseProtocol {
+
+ public TeltonikaProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TeltonikaFrameDecoder());
+ pipeline.addLast(new TeltonikaProtocolEncoder(TeltonikaProtocol.this));
+ pipeline.addLast(new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TeltonikaProtocolEncoder(TeltonikaProtocol.this));
+ pipeline.addLast(new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 80f0045d5..cc2868a20 100644
--- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,13 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.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;
@@ -30,57 +34,149 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
+ private static final int IMAGE_PACKET_MAX = 2048;
+
private boolean connectionless;
private boolean extended;
+ private Map<Long, ByteBuf> photos = new HashMap<>();
public void setExtended(boolean extended) {
this.extended = extended;
}
- public TeltonikaProtocolDecoder(TeltonikaProtocol protocol, boolean connectionless) {
+ public TeltonikaProtocolDecoder(Protocol protocol, boolean connectionless) {
super(protocol);
this.connectionless = connectionless;
this.extended = Context.getConfig().getBoolean(getProtocolName() + ".extended");
}
- private DeviceSession parseIdentification(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private void parseIdentification(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
int length = buf.readUnsignedShort();
String imei = buf.toString(buf.readerIndex(), length, StandardCharsets.US_ASCII);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(1);
+ ByteBuf response = Unpooled.buffer(1);
if (deviceSession != null) {
response.writeByte(1);
} else {
response.writeByte(0);
}
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
- return deviceSession;
}
public static final int CODEC_GH3000 = 0x07;
- public static final int CODEC_FM4X00 = 0x08;
+ public static final int CODEC_8 = 0x08;
+ public static final int CODEC_8_EXT = 0x8E;
public static final int CODEC_12 = 0x0C;
+ public static final int CODEC_16 = 0x10;
+
+ private void sendImageRequest(Channel channel, SocketAddress remoteAddress, long id, int offset, int size) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeInt(0);
+ response.writeShort(0);
+ response.writeShort(19); // length
+ response.writeByte(CODEC_12);
+ response.writeByte(1); // nod
+ response.writeByte(0x0D); // camera
+ response.writeInt(11); // payload length
+ response.writeByte(2); // command
+ response.writeInt((int) id);
+ response.writeInt(offset);
+ response.writeShort(size);
+ response.writeByte(1); // nod
+ response.writeShort(0);
+ response.writeShort(Checksum.crc16(
+ Checksum.CRC16_IBM, response.nioBuffer(8, response.readableBytes() - 10)));
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
- private void decodeSerial(Position position, ChannelBuffer buf) {
+ private void decodeSerial(Channel channel, SocketAddress remoteAddress, Position position, ByteBuf buf) {
getLastLocation(position, null);
- position.set(Position.KEY_TYPE, buf.readUnsignedByte());
+ int type = buf.readUnsignedByte();
+ if (type == 0x0D) {
+
+ buf.readInt(); // length
+ int subtype = buf.readUnsignedByte();
+ if (subtype == 0x01) {
+
+ long photoId = buf.readUnsignedInt();
+ ByteBuf photo = Unpooled.buffer(buf.readInt());
+ photos.put(photoId, photo);
+ sendImageRequest(
+ channel, remoteAddress, photoId,
+ 0, Math.min(IMAGE_PACKET_MAX, photo.capacity()));
+
+ } else if (subtype == 0x02) {
+
+ long photoId = buf.readUnsignedInt();
+ buf.readInt(); // offset
+ ByteBuf photo = photos.get(photoId);
+ photo.writeBytes(buf, buf.readUnsignedShort());
+ if (photo.writableBytes() > 0) {
+ sendImageRequest(
+ 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"));
+ } finally {
+ photo.release();
+ }
+ }
+
+ }
+
+ } else {
+
+ position.set(Position.KEY_TYPE, type);
- position.set(Position.KEY_RESULT, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII));
+ int length = buf.readInt();
+ boolean readable = true;
+ for (int i = 0; i < length; i++) {
+ byte b = buf.getByte(buf.readerIndex() + i);
+ if (b < 32 && b != '\r' && b != '\n') {
+ readable = false;
+ break;
+ }
+ }
+ if (readable) {
+ String data = buf.readSlice(length).toString(StandardCharsets.US_ASCII).trim();
+ if (data.startsWith("UUUUww") && data.endsWith("SSS")) {
+ String[] values = data.substring(6, data.length() - 4).split(";");
+ for (int i = 0; i < 8; i++) {
+ position.set("axle" + (i + 1), Double.parseDouble(values[i]));
+ }
+ position.set("loadTruck", Double.parseDouble(values[8]));
+ position.set("loadTrailer", Double.parseDouble(values[9]));
+ position.set("totalTruck", Double.parseDouble(values[10]));
+ position.set("totalTrailer", Double.parseDouble(values[11]));
+ } else {
+ position.set(Position.KEY_RESULT, data);
+ }
+ } else {
+ position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(buf.readSlice(length)));
+ }
+ }
}
- private long readValue(ChannelBuffer buf, int length, boolean signed) {
+ private long readValue(ByteBuf buf, int length, boolean signed) {
switch (length) {
case 1:
return signed ? buf.readByte() : buf.readUnsignedByte();
@@ -93,7 +189,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeOtherParameter(Position position, int id, ChannelBuffer buf, int length) {
+ private void decodeOtherParameter(Position position, int id, ByteBuf buf, int length) {
switch (id) {
case 1:
case 2:
@@ -104,6 +200,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
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 17:
position.set("axisX", readValue(buf, length, true));
break;
@@ -116,27 +215,45 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
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), 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:
- position.set(Position.PREFIX_TEMP + 1, readValue(buf, length, true) * 0.1);
+ case 69:
+ position.set("gpsStatus", readValue(buf, length, false));
break;
+ case 72:
case 73:
- position.set(Position.PREFIX_TEMP + 2, readValue(buf, length, true) * 0.1);
- break;
case 74:
- position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
+ position.set(Position.PREFIX_TEMP + (id - 71), readValue(buf, length, true) * 0.1);
break;
case 78:
- position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", readValue(buf, length, false)));
+ 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 129:
+ case 130:
+ case 131:
+ case 132:
+ case 133:
+ case 134:
+ String driver = id == 129 || id == 132 ? "" : position.getString("driver1");
+ position.set("driver" + (id >= 132 ? 2 : 1),
+ driver + buf.readSlice(length).toString(StandardCharsets.US_ASCII).trim());
+ break;
case 179:
position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1);
break;
@@ -149,6 +266,29 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 182:
position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
break;
+ case 236:
+ if (readValue(buf, length, false) == 1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+ break;
+ case 237:
+ position.set(Position.KEY_MOTION, readValue(buf, length, false) == 0);
+ break;
+ case 238:
+ 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;
case 239:
position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
break;
@@ -164,7 +304,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeGh3000Parameter(Position position, int id, ChannelBuffer buf, int length) {
+ 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));
@@ -207,7 +347,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeParameter(Position position, int id, ChannelBuffer buf, int length, int codec) {
+ private void decodeParameter(Position position, int id, ByteBuf buf, int length, int codec) {
if (codec == CODEC_GH3000) {
decodeGh3000Parameter(position, id, buf, length);
} else {
@@ -228,7 +368,22 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeLocation(Position position, ChannelBuffer buf, int codec) {
+ private int readExtByte(ByteBuf buf, int codec, int... codecs) {
+ boolean ext = false;
+ for (int c : codecs) {
+ if (codec == c) {
+ ext = true;
+ break;
+ }
+ }
+ if (ext) {
+ return buf.readUnsignedShort();
+ } else {
+ return buf.readUnsignedByte();
+ }
+ }
+
+ private void decodeLocation(Position position, ByteBuf buf, int codec) {
int globalMask = 0x0f;
@@ -262,9 +417,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(locationMask, 4)) {
- int satellites = buf.readUnsignedByte();
- position.set(Position.KEY_SATELLITES, satellites);
- position.setValid(satellites >= 3);
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
}
if (BitUtil.check(locationMask, 5)) {
@@ -313,49 +466,67 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
- position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ position.set(Position.KEY_EVENT, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16));
+ if (codec == CODEC_16) {
+ buf.readUnsignedByte(); // generation type
+ }
- buf.readUnsignedByte(); // total IO data records
+ readExtByte(buf, codec, CODEC_8_EXT); // total IO data records
}
// Read 1 byte data
if (BitUtil.check(globalMask, 1)) {
- int cnt = buf.readUnsignedByte();
+ int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 1, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 1, codec);
}
}
// Read 2 byte data
if (BitUtil.check(globalMask, 2)) {
- int cnt = buf.readUnsignedByte();
+ int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 2, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 2, codec);
}
}
// Read 4 byte data
if (BitUtil.check(globalMask, 3)) {
- int cnt = buf.readUnsignedByte();
+ int cnt = readExtByte(buf, codec, CODEC_8_EXT);
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 4, codec);
+ decodeParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 4, codec);
}
}
// Read 8 byte data
- if (codec == CODEC_FM4X00) {
- int cnt = buf.readUnsignedByte();
+ 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, buf.readUnsignedByte(), buf, 8);
+ decodeOtherParameter(position, readExtByte(buf, codec, CODEC_8_EXT, CODEC_16), buf, 8);
}
}
// Read 16 byte data
if (extended) {
- int cnt = buf.readUnsignedByte();
+ int cnt = readExtByte(buf, codec, CODEC_8_EXT);
+ for (int j = 0; j < cnt; j++) {
+ int id = readExtByte(buf, codec, CODEC_8_EXT, CODEC_16);
+ position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(16)));
+ }
+ }
+
+ // Read X byte data
+ if (codec == CODEC_8_EXT) {
+ int cnt = buf.readUnsignedShort();
for (int j = 0; j < cnt; j++) {
- position.set(Position.PREFIX_IO + buf.readUnsignedByte(), ChannelBuffers.hexDump(buf.readBytes(16)));
+ int id = buf.readUnsignedShort();
+ int length = buf.readUnsignedShort();
+ if (id == 256) {
+ position.set(Position.KEY_VIN, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ } else {
+ position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(length)));
+ }
}
}
@@ -364,7 +535,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
private List<Position> parseData(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int locationPacketId, String... imei) {
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf, int locationPacketId, String... imei) {
List<Position> positions = new LinkedList<>();
if (!connectionless) {
@@ -381,43 +552,45 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
for (int i = 0; i < count; i++) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ position.setValid(true);
if (codec == CODEC_12) {
- decodeSerial(position, buf);
+ decodeSerial(channel, remoteAddress, position, buf);
} else {
decodeLocation(position, buf, codec);
}
- positions.add(position);
+ if (!position.getOutdated() || !position.getAttributes().isEmpty()) {
+ positions.add(position);
+ }
}
if (channel != null) {
if (connectionless) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeShort(5);
response.writeShort(0);
response.writeByte(0x01);
response.writeByte(locationPacketId);
response.writeByte(count);
- channel.write(response, remoteAddress);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
} else {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeInt(count);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
- return positions;
+ return positions.isEmpty() ? null : positions;
}
@Override
protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (connectionless) {
return decodeUdp(channel, remoteAddress, buf);
@@ -426,7 +599,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) throws Exception {
+ private Object decodeTcp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
if (buf.getUnsignedShort(0) > 0) {
parseIdentification(channel, remoteAddress, buf);
@@ -438,13 +611,13 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) throws Exception {
+ private Object decodeUdp(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception {
buf.readUnsignedShort(); // length
buf.readUnsignedShort(); // packet id
buf.readUnsignedByte(); // packet type
int locationPacketId = buf.readUnsignedByte();
- String imei = buf.readBytes(buf.readUnsignedShort()).toString(StandardCharsets.US_ASCII);
+ String imei = buf.readSlice(buf.readUnsignedShort()).toString(StandardCharsets.US_ASCII);
return parseData(channel, remoteAddress, buf, locationPacketId, imei);
diff --git a/src/org/traccar/protocol/TeltonikaProtocolEncoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolEncoder.java
index fd6eae744..8a614618a 100644
--- a/src/org/traccar/protocol/TeltonikaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,32 +15,36 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
-import org.traccar.helper.Log;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import java.nio.charset.StandardCharsets;
public class TeltonikaProtocolEncoder extends BaseProtocolEncoder {
- private ChannelBuffer encodeContent(String content) {
+ public TeltonikaProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(byte[] content) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+ ByteBuf buf = Unpooled.buffer();
buf.writeInt(0);
- buf.writeInt(content.length() + 10);
+ buf.writeInt(content.length + 8);
buf.writeByte(TeltonikaProtocolDecoder.CODEC_12);
buf.writeByte(1); // quantity
buf.writeByte(5); // type
- buf.writeInt(content.length() + 2);
- buf.writeBytes(content.getBytes(StandardCharsets.US_ASCII));
- buf.writeByte('\r');
- buf.writeByte('\n');
+ buf.writeInt(content.length);
+ buf.writeBytes(content);
buf.writeByte(1); // quantity
- buf.writeInt(Checksum.crc16(Checksum.CRC16_IBM, buf.toByteBuffer(8, buf.writerIndex() - 8)));
+ buf.writeInt(Checksum.crc16(Checksum.CRC16_IBM, buf.nioBuffer(8, buf.writerIndex() - 8)));
return buf;
}
@@ -48,15 +52,16 @@ public class TeltonikaProtocolEncoder extends BaseProtocolEncoder {
@Override
protected Object encodeCommand(Command command) {
- switch (command.getType()) {
- case Command.TYPE_CUSTOM:
- return encodeContent(command.getString(Command.KEY_DATA));
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ if (command.getType().equals(Command.TYPE_CUSTOM)) {
+ String data = command.getString(Command.KEY_DATA);
+ if (data.matches("(\\p{XDigit}{2})+")) {
+ return encodeContent(DataConverter.parseHex(data));
+ } else {
+ return encodeContent((data + "\r\n").getBytes(StandardCharsets.US_ASCII));
+ }
+ } else {
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
new file mode 100644
index 000000000..ca1237cef
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class ThinkRaceProtocol extends BaseProtocol {
+
+ public ThinkRaceProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2 + 12 + 1 + 1, 2, 2, 0));
+ pipeline.addLast(new ThinkRaceProtocolDecoder(ThinkRaceProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/ThinkRaceProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java
index 4a97fa1a1..0928b25e0 100644
--- a/src/org/traccar/protocol/ThinkRaceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
@@ -31,7 +33,7 @@ import java.util.Date;
public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder {
- public ThinkRaceProtocolDecoder(ThinkRaceProtocol protocol) {
+ public ThinkRaceProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -52,10 +54,10 @@ public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
- ChannelBuffer id = buf.readBytes(12);
+ ByteBuf id = buf.readSlice(12);
buf.readUnsignedByte(); // separator
int type = buf.readUnsignedByte();
buf.readUnsignedShort(); // length
@@ -68,7 +70,7 @@ public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder {
String imei = buf.toString(buf.readerIndex(), 15, StandardCharsets.US_ASCII);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession != null && channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(0x48); response.writeByte(0x52); // header
response.writeBytes(id);
response.writeByte(0x2c); // separator
@@ -76,7 +78,7 @@ public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder {
response.writeShort(0x0002); // length
response.writeShort(0x8000);
response.writeShort(0x0000); // checksum
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
@@ -87,8 +89,7 @@ public class ThinkRaceProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(new Date(buf.readUnsignedInt() * 1000));
diff --git a/src/main/java/org/traccar/protocol/Tk102Protocol.java b/src/main/java/org/traccar/protocol/Tk102Protocol.java
new file mode 100644
index 000000000..9f2463cd6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tk102Protocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Tk102Protocol extends BaseProtocol {
+
+ public Tk102Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1 + 1 + 10, 1, 1, 0));
+ pipeline.addLast(new Tk102ProtocolDecoder(Tk102Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Tk102ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java
index 41d5b7436..da0c6928b 100644
--- a/src/org/traccar/protocol/Tk102ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk102ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -31,7 +33,7 @@ import java.util.regex.Pattern;
public class Tk102ProtocolDecoder extends BaseProtocolDecoder {
- public Tk102ProtocolDecoder(Tk102Protocol protocol) {
+ public Tk102ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -59,16 +61,17 @@ public class Tk102ProtocolDecoder extends BaseProtocolDecoder {
.text(")")
.compile();
- private void sendResponse(Channel channel, int type, ChannelBuffer dataSequence, ChannelBuffer content) {
+ private void sendResponse(Channel channel, int type, ByteBuf dataSequence, ByteBuf content) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte('[');
response.writeByte(type);
response.writeBytes(dataSequence);
response.writeByte(content.readableBytes());
response.writeBytes(content);
+ content.release();
response.writeByte(']');
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
@@ -76,16 +79,16 @@ public class Tk102ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(1); // header
int type = buf.readUnsignedByte();
- ChannelBuffer dataSequence = buf.readBytes(10);
+ ByteBuf dataSequence = buf.readSlice(10);
int length = buf.readUnsignedByte();
if (type == MSG_LOGIN_REQUEST || type == MSG_LOGIN_REQUEST_2) {
- ChannelBuffer data = buf.readBytes(length);
+ ByteBuf data = buf.readSlice(length);
String id;
if (type == MSG_LOGIN_REQUEST) {
@@ -95,7 +98,7 @@ public class Tk102ProtocolDecoder extends BaseProtocolDecoder {
}
if (getDeviceSession(channel, remoteAddress, id) != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(MODE_GPRS);
response.writeBytes(data);
sendResponse(channel, MSG_LOGIN_RESPONSE, dataSequence, response);
@@ -103,7 +106,7 @@ public class Tk102ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_HEARTBEAT_REQUEST) {
- sendResponse(channel, MSG_HEARTBEAT_RESPONSE, dataSequence, buf.readBytes(length));
+ sendResponse(channel, MSG_HEARTBEAT_RESPONSE, dataSequence, buf.readRetainedSlice(length));
} else {
@@ -112,13 +115,12 @@ public class Tk102ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Parser parser = new Parser(PATTERN, buf.readBytes(length).toString(StandardCharsets.US_ASCII));
+ Parser parser = new Parser(PATTERN, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/main/java/org/traccar/protocol/Tk103FrameDecoder.java b/src/main/java/org/traccar/protocol/Tk103FrameDecoder.java
new file mode 100644
index 000000000..b61a42563
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tk103FrameDecoder.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 Valerii Vyshniak (val@val.one)
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class Tk103FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 2) {
+ return null;
+ }
+
+ int frameStartIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '(');
+ if (frameStartIndex == -1) {
+ buf.clear();
+ return null;
+ }
+
+ int frameEndIndex, freeTextSymbolCounter;
+ for (frameEndIndex = frameStartIndex, freeTextSymbolCounter = 0;; frameEndIndex++) {
+ int freeTextIndex = frameEndIndex;
+ frameEndIndex = buf.indexOf(frameEndIndex, buf.writerIndex(), (byte) ')');
+ if (frameEndIndex == -1) {
+ break;
+ }
+ for (;; freeTextIndex++, freeTextSymbolCounter++) {
+ freeTextIndex = buf.indexOf(freeTextIndex, frameEndIndex, (byte) '$');
+ if (freeTextIndex == -1 || freeTextIndex >= frameEndIndex) {
+ break;
+ }
+ }
+ if (freeTextSymbolCounter % 2 == 0) {
+ break;
+ }
+ }
+
+ if (frameEndIndex == -1) {
+ while (buf.readableBytes() > 1024) {
+ int discardUntilIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) '(');
+ if (discardUntilIndex == -1) {
+ buf.clear();
+ } else {
+ buf.readerIndex(discardUntilIndex);
+ }
+ }
+ return null;
+ }
+
+ buf.readerIndex(frameStartIndex);
+
+ return buf.readRetainedSlice(frameEndIndex + 1 - frameStartIndex);
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Tk103Protocol.java b/src/main/java/org/traccar/protocol/Tk103Protocol.java
new file mode 100644
index 000000000..ff0bedfb7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tk103Protocol.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Christoph Krey (c@ckrey.de)
+ * 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 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.model.Command;
+
+public class Tk103Protocol extends BaseProtocol {
+
+ public Tk103Protocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_GET_DEVICE_STATUS,
+ Command.TYPE_IDENTIFICATION,
+ Command.TYPE_MODE_DEEP_SLEEP,
+ Command.TYPE_MODE_POWER_SAVING,
+ Command.TYPE_ALARM_SOS,
+ Command.TYPE_SET_CONNECTION,
+ Command.TYPE_SOS_NUMBER,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_POSITION_PERIODIC,
+ Command.TYPE_POSITION_STOP,
+ Command.TYPE_GET_VERSION,
+ Command.TYPE_POWER_OFF,
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_SET_ODOMETER,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME,
+ Command.TYPE_OUTPUT_CONTROL);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Tk103FrameDecoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Tk103ProtocolEncoder(Tk103Protocol.this));
+ pipeline.addLast(new Tk103ProtocolDecoder(Tk103Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Tk103ProtocolEncoder(Tk103Protocol.this));
+ pipeline.addLast(new Tk103ProtocolDecoder(Tk103Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
index be3def453..feedb2c5e 100644
--- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,10 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+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.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -26,6 +28,7 @@ import org.traccar.helper.PatternBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.util.regex.Pattern;
@@ -34,13 +37,18 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
private boolean decodeLow;
- public Tk103ProtocolDecoder(Tk103Protocol protocol) {
+ public Tk103ProtocolDecoder(Protocol protocol) {
super(protocol);
decodeLow = Context.getConfig().getBoolean(getProtocolName() + ".decodeLow");
}
private static final Pattern PATTERN = new PatternBuilder()
- .number("(d+)(,)?") // device id
+ .text("(").optional()
+ .groupBegin()
+ .expression("(.{12})") // device id
+ .or()
+ .expression("(.+),") // device id
+ .groupEnd()
.expression("(.{4}),?") // command
.number("(d*)")
.number("(dd)(dd)(dd),?") // date (mmddyy if comma-delimited, otherwise yyddmm)
@@ -51,33 +59,38 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW]),?")
.number("(d+.d)(?:d*,)?") // speed
.number("(dd)(dd)(dd),?") // time (hhmmss)
- .number("(d+.?d{1,2}),?") // course
.groupBegin()
+ .number("(?:([d.]{6})|(dd)),?") // course
.number("([01])") // charge
.number("([01])") // ignition
.number("(x)") // io
.number("(x)") // io
.number("(x)") // io
- .number("(xxx),?") // fuel
- .groupEnd("?")
- .number("(?:L(x+))?") // odometer
+ .number("(xxx)") // fuel
+ .number("L(x+)") // odometer
+ .or()
+ .number("(d+.d+)") // course
+ .groupEnd()
.any()
.number("([+-]ddd.d)?") // temperature
.text(")").optional()
.compile();
private static final Pattern PATTERN_BATTERY = new PatternBuilder()
+ .text("(").optional()
.number("(d+),") // device id
.text("ZC20,")
.number("(dd)(dd)(dd),") // date (ddmmyy)
.number("(dd)(dd)(dd),") // time (hhmmss)
- .number("d+,") // battery level
+ .number("(d+),") // battery level
.number("(d+),") // battery voltage
.number("(d+),") // power voltage
.number("d+") // installed
+ .any()
.compile();
private static final Pattern PATTERN_NETWORK = new PatternBuilder()
+ .text("(").optional()
.number("(d{12})") // device id
.text("BZ00,")
.number("(d+),") // mcc
@@ -87,6 +100,31 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_LBSWIFI = new PatternBuilder()
+ .text("(").optional()
+ .number("(d+),") // device id
+ .expression("(.{4}),") // command
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(d+),") // lac
+ .number("(d+),") // cid
+ .number("(d+),") // number of wifi macs
+ .number("((?:(?:xx:){5}(?:xx)\\*[-+]?d+\\*d+,)*)")
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .any()
+ .compile();
+
+ private static final Pattern PATTERN_COMMAND_RESULT = new PatternBuilder()
+ .text("(").optional()
+ .number("(d+),") // device id
+ .expression(".{4},") // command
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("\\$([\\s\\S]*?)(?:\\$|$)") // message
+ .any()
+ .compile();
+
private String decodeAlarm(int value) {
switch (value) {
case 1:
@@ -112,30 +150,53 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, decodeAlarm(data.charAt(0) - '0'));
break;
case "ZC11":
+ case "DW31":
+ case "DW51":
position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
break;
case "ZC12":
+ case "DW32":
+ case "DW52":
position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
break;
case "ZC13":
+ case "DW33":
+ case "DW53":
position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
break;
case "ZC15":
+ case "DW35":
+ case "DW55":
position.set(Position.KEY_IGNITION, true);
break;
case "ZC16":
+ case "DW36":
+ case "DW56":
position.set(Position.KEY_IGNITION, false);
break;
+ case "ZC29":
+ case "DW42":
+ case "DW62":
+ position.set(Position.KEY_IGNITION, true);
+ break;
case "ZC17":
+ case "DW37":
+ case "DW57":
position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
break;
case "ZC25":
+ case "DW3E":
+ case "DW5E":
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
break;
case "ZC26":
+ case "DW3F":
+ case "DW5F":
position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING);
break;
case "ZC27":
+ case "DW40":
+ case "DW60":
position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
break;
default:
@@ -143,6 +204,23 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private Integer decodeBattery(int value) {
+ switch (value) {
+ case 6:
+ return 100;
+ case 5:
+ return 80;
+ case 4:
+ return 50;
+ case 3:
+ return 20;
+ case 2:
+ return 10;
+ default:
+ return null;
+ }
+ }
+
private Position decodeBattery(Channel channel, SocketAddress remoteAddress, String sentence) {
Parser parser = new Parser(PATTERN_BATTERY, sentence);
if (!parser.matches()) {
@@ -154,12 +232,16 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ int batterylevel = parser.nextInt(0);
+ if (batterylevel != 255) {
+ position.set(Position.KEY_BATTERY_LEVEL, decodeBattery(batterylevel));
+ }
+
int battery = parser.nextInt(0);
if (battery != 65535) {
position.set(Position.KEY_BATTERY, battery * 0.01);
@@ -184,8 +266,7 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
@@ -195,25 +276,87 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- @Override
+
+ private Position decodeLbsWifi(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_LBSWIFI, 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());
+
+ decodeType(position, parser.next(), "0");
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ network.addCellTower(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()));
+
+ int wifiCount = parser.nextInt();
+ if (parser.hasNext()) {
+ String[] wifimacs = parser.next().split(",");
+ if (wifimacs.length == wifiCount) {
+ for (int i = 0; i < wifiCount; i++) {
+ String[] wifiinfo = wifimacs[i].split("\\*");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ wifiinfo[0], Integer.parseInt(wifiinfo[1]), Integer.parseInt(wifiinfo[2])));
+ }
+ }
+ }
+
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ return position;
+ }
+
+ private Position decodeCommandResult(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_COMMAND_RESULT, 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, parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.set(Position.KEY_RESULT, parser.next());
+
+ return position;
+
+ }
+
+@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
- int beginIndex = sentence.indexOf('(');
- if (beginIndex != -1) {
- sentence = sentence.substring(beginIndex + 1);
- }
-
if (channel != null) {
- String id = sentence.substring(0, 12);
- String type = sentence.substring(12, 16);
+ String id = sentence.substring(1, 13);
+ String type = sentence.substring(13, 17);
if (type.equals("BP00")) {
- channel.write("(" + id + "AP01HSO)");
+ channel.writeAndFlush(new NetworkMessage("(" + id + "AP01HSO)", remoteAddress));
return null;
} else if (type.equals("BP05")) {
- channel.write("(" + id + "AP05)");
+ channel.writeAndFlush(new NetworkMessage("(" + id + "AP05)", remoteAddress));
}
}
@@ -221,6 +364,10 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return decodeBattery(channel, remoteAddress, sentence);
} else if (sentence.contains("BZ00")) {
return decodeNetwork(channel, remoteAddress, sentence);
+ } else if (sentence.contains("ZC03")) {
+ return decodeCommandResult(channel, remoteAddress, sentence);
+ } else if (sentence.contains("DW5")) {
+ return decodeLbsWifi(channel, remoteAddress, sentence);
}
Parser parser = new Parser(PATTERN, sentence);
@@ -228,17 +375,24 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ String id = null;
+ boolean alternative = false;
+ if (parser.hasNext()) {
+ id = parser.next();
+ }
+ if (parser.hasNext()) {
+ id = parser.next();
+ alternative = true;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- boolean alternative = parser.next() != null;
-
decodeType(position, parser.next(), parser.next());
DateBuilder dateBuilder = new DateBuilder();
@@ -257,9 +411,14 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
position.setTime(dateBuilder.getDate());
- position.setCourse(parser.nextDouble(0));
+ if (parser.hasNext()) {
+ position.setCourse(parser.nextDouble());
+ }
+ if (parser.hasNext()) {
+ position.setCourse(parser.nextDouble());
+ }
- if (parser.hasNext(6)) {
+ if (parser.hasNext(7)) {
position.set(Position.KEY_CHARGE, parser.nextInt() == 0);
position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
@@ -289,10 +448,11 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
}
position.set(Position.KEY_FUEL_LEVEL, parser.nextHexInt());
+ position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
}
if (parser.hasNext()) {
- position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
+ position.setCourse(parser.nextDouble());
}
if (parser.hasNext()) {
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
new file mode 100644
index 000000000..5d7e63920
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017 Christoph Krey (c@ckrey.de)
+ * 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.protocol;
+
+import org.traccar.Context;
+import org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class Tk103ProtocolEncoder extends StringProtocolEncoder {
+
+ private final boolean forceAlternative;
+
+ public Tk103ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ this.forceAlternative = false;
+ }
+
+ public Tk103ProtocolEncoder(Protocol protocol, boolean forceAlternative) {
+ super(protocol);
+ this.forceAlternative = forceAlternative;
+ }
+
+ private String formatAlt(Command command, String format, String... keys) {
+ return formatCommand(command, "[begin]sms2," + format + ",[end]", keys);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ boolean alternative = forceAlternative || Context.getIdentityManager().lookupAttributeBoolean(
+ command.getDeviceId(), getProtocolName() + ".alternative", false, false, true);
+
+ initDevicePassword(command, "123456");
+
+ if (alternative) {
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatAlt(command, "%s", Command.KEY_DATA);
+ case Command.TYPE_GET_VERSION:
+ return formatAlt(command, "*about*");
+ case Command.TYPE_POWER_OFF:
+ return formatAlt(command, "*turnoff*");
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatAlt(command, "88888888");
+ case Command.TYPE_POSITION_SINGLE:
+ return formatAlt(command, "*getposl*");
+ case Command.TYPE_POSITION_PERIODIC:
+ return formatAlt(command, "*routetrack*99*");
+ case Command.TYPE_POSITION_STOP:
+ return formatAlt(command, "*routetrackoff*");
+ case Command.TYPE_GET_DEVICE_STATUS:
+ return formatAlt(command, "*status*");
+ case Command.TYPE_IDENTIFICATION:
+ return formatAlt(command, "999999");
+ case Command.TYPE_MODE_DEEP_SLEEP:
+ return formatAlt(command, command.getBoolean(Command.KEY_ENABLE) ? "*sleep*2*" : "*sleepoff*");
+ case Command.TYPE_MODE_POWER_SAVING:
+ return formatAlt(command, command.getBoolean(Command.KEY_ENABLE) ? "*sleepv*" : "*sleepoff*");
+ case Command.TYPE_ALARM_SOS:
+ return formatAlt(command, command.getBoolean(Command.KEY_ENABLE) ? "*soson*" : "*sosoff*");
+ case Command.TYPE_SET_CONNECTION:
+ String server = command.getString(Command.KEY_SERVER).replace(".", "*");
+ return formatAlt(command, "*setip*" + server + "*%s*", Command.KEY_PORT);
+ case Command.TYPE_SOS_NUMBER:
+ return formatAlt(command, "*master*%s*%s*", Command.KEY_DEVICE_PASSWORD, Command.KEY_PHONE);
+ default:
+ return null;
+ }
+ } else {
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "(%s%s)", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ case Command.TYPE_GET_VERSION:
+ return formatCommand(command, "(%sAP07)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatCommand(command, "(%sAT00)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_SET_ODOMETER:
+ return formatCommand(command, "(%sAX01)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, "(%sAP00)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_POSITION_PERIODIC:
+ String frequency = String.format("%04X", command.getInteger(Command.KEY_FREQUENCY));
+ return formatCommand(command, "(%sAR00" + frequency + "0000)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_POSITION_STOP:
+ return formatCommand(command, "(%sAR0000000000)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, "(%sAV010)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ENGINE_RESUME:
+ return formatCommand(command, "(%sAV011)", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_OUTPUT_CONTROL:
+ return formatCommand(command, "(%sAV00%s)", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ default:
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
new file mode 100644
index 000000000..12fd92afa
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Tlt2hProtocol extends BaseProtocol {
+
+ public Tlt2hProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(32 * 1024, "##\r\n"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Tlt2hProtocolDecoder(Tlt2hProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index cbc851de0..7ad99b37e 100644
--- a/src/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -30,16 +31,23 @@ import java.util.regex.Pattern;
public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
- public Tlt2hProtocolDecoder(Tlt2hProtocol protocol) {
+ public Tlt2hProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN_HEADER = new PatternBuilder()
- .number("#(d+)#") // imei
- .expression("[^#]*#")
- .number("d+#")
- .expression("([^#]+)#") // status
- .number("d+") // number of records
+ .number("#(d+)") // imei
+ .expression("#[^#]*") // user
+ .number("#d+") // password
+ .groupBegin()
+ .number("#([01])") // door
+ .number("#(d+)") // fuel voltage
+ .number("#(d+)") // power
+ .number("#(d+)") // battery
+ .number("#(d+)") // temperature
+ .groupEnd("?")
+ .expression("#([^#]+)") // status
+ .number("#d+") // number of records
.compile();
private static final Pattern PATTERN_POSITION = new PatternBuilder()
@@ -57,6 +65,45 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private void decodeStatus(Position position, String status) {
+ switch (status) {
+ case "AUTOSTART":
+ case "AUTO":
+ position.set(Position.KEY_IGNITION, true);
+ break;
+ case "AUTOSTOP":
+ case "AUTOLOW":
+ position.set(Position.KEY_IGNITION, false);
+ break;
+ case "TOWED":
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ break;
+ case "SOS":
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ break;
+ case "DEF":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case "BLP":
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case "CLP":
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ break;
+ case "OS":
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT);
+ break;
+ case "RS":
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER);
+ break;
+ case "OVERSPEED":
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ default:
+ break;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -75,6 +122,19 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ Boolean door = null;
+ Double adc = null;
+ Double power = null;
+ Double battery = null;
+ Double temperature = null;
+ if (parser.hasNext(5)) {
+ door = parser.nextInt() == 1;
+ adc = parser.nextInt() * 0.1;
+ power = parser.nextInt() * 0.1;
+ battery = parser.nextInt() * 0.1;
+ temperature = parser.nextInt() * 0.1;
+ }
+
String status = parser.next();
String[] messages = sentence.substring(sentence.indexOf('\n') + 1).split("\r\n");
@@ -84,8 +144,7 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
parser = new Parser(PATTERN_POSITION, message);
if (parser.matches()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
parser.next(); // base station info
@@ -102,7 +161,12 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
position.setTime(dateBuilder.getDate());
- position.set(Position.KEY_STATUS, status);
+ position.set(Position.KEY_DOOR, door);
+ position.set(Position.PREFIX_ADC + 1, adc);
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_BATTERY, battery);
+ position.set(Position.PREFIX_TEMP + 1, temperature);
+ decodeStatus(position, status);
positions.add(position);
}
diff --git a/src/org/traccar/protocol/TlvProtocol.java b/src/main/java/org/traccar/protocol/TlvProtocol.java
index da8d88b8a..94f5da94f 100644
--- a/src/org/traccar/protocol/TlvProtocol.java
+++ b/src/main/java/org/traccar/protocol/TlvProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,27 +15,19 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class TlvProtocol extends BaseProtocol {
public TlvProtocol() {
- super("tlv");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder('\0'));
- pipeline.addLast("objectDecoder", new TlvProtocolDecoder(TlvProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '\0'));
+ pipeline.addLast(new TlvProtocolDecoder(TlvProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/TlvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java
index 41d65be09..36cf7859f 100644
--- a/src/org/traccar/protocol/TlvProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TlvProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -29,47 +31,47 @@ import java.util.Date;
public class TlvProtocolDecoder extends BaseProtocolDecoder {
- public TlvProtocolDecoder(TlvProtocol protocol) {
+ public TlvProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private void sendResponse(Channel channel, String type, String... arguments) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String type, String... arguments) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
- response.writeBytes(ChannelBuffers.copiedBuffer(type, StandardCharsets.US_ASCII));
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence(type, StandardCharsets.US_ASCII);
for (String argument : arguments) {
response.writeByte(argument.length());
- response.writeBytes(ChannelBuffers.copiedBuffer(argument, StandardCharsets.US_ASCII));
+ response.writeCharSequence(argument, StandardCharsets.US_ASCII);
}
response.writeByte(0);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
- private String readArgument(ChannelBuffer buf) {
- return buf.readBytes(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII);
+ private String readArgument(ByteBuf buf) {
+ return buf.readSlice(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII);
}
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- String type = buf.readBytes(2).toString(StandardCharsets.US_ASCII);
+ String type = buf.readSlice(2).toString(StandardCharsets.US_ASCII);
if (channel != null) {
switch (type) {
case "0A":
case "0C":
- sendResponse(channel, type);
+ sendResponse(channel, remoteAddress, type);
break;
case "0B":
- sendResponse(channel, type, "1482202689", "10", "20", "15");
+ sendResponse(channel, remoteAddress, type, "1482202689", "10", "20", "15");
break;
case "0E":
case "0F":
- sendResponse(channel, type, "30", "Unknown");
+ sendResponse(channel, remoteAddress, type, "30", "Unknown");
break;
default:
break;
@@ -83,8 +85,7 @@ public class TlvProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setValid(true);
diff --git a/src/main/java/org/traccar/protocol/TmgFrameDecoder.java b/src/main/java/org/traccar/protocol/TmgFrameDecoder.java
new file mode 100644
index 000000000..205adaa51
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TmgFrameDecoder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseFrameDecoder;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class TmgFrameDecoder extends BaseFrameDecoder {
+
+ private boolean isLetter(byte c) {
+ return c >= 'a' && c <= 'z';
+ }
+
+ private int findHeader(ByteBuf buffer) {
+ int guessedIndex = buffer.indexOf(buffer.readerIndex(), buffer.writerIndex(), (byte) '$');
+ while (guessedIndex != -1 && buffer.writerIndex() - guessedIndex >= 5) {
+ if (buffer.getByte(guessedIndex + 4) == ','
+ && isLetter(buffer.getByte(guessedIndex + 1))
+ && isLetter(buffer.getByte(guessedIndex + 2))
+ && isLetter(buffer.getByte(guessedIndex + 3))) {
+ return guessedIndex;
+ }
+ guessedIndex = buffer.indexOf(guessedIndex, buffer.writerIndex(), (byte) '$');
+ }
+ return -1;
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int beginIndex = findHeader(buf);
+
+ if (beginIndex >= 0) {
+
+ buf.readerIndex(beginIndex);
+
+ int endIndex = buf.indexOf(beginIndex, buf.writerIndex(), (byte) '\n');
+
+ if (endIndex >= 0) {
+ ByteBuf frame = buf.readRetainedSlice(endIndex - beginIndex);
+ buf.readByte(); // delimiter
+ return frame;
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TmgProtocol.java b/src/main/java/org/traccar/protocol/TmgProtocol.java
new file mode 100644
index 000000000..020332ce7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TmgProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TmgProtocol extends BaseProtocol {
+
+ public TmgProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TmgFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TmgProtocolDecoder(TmgProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java
index b8458dd52..d27849f8c 100644
--- a/src/org/traccar/protocol/TmgProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TmgProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -29,14 +30,14 @@ import java.util.regex.Pattern;
public class TmgProtocolDecoder extends BaseProtocolDecoder {
- public TmgProtocolDecoder(TmgProtocol protocol) {
+ public TmgProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN = new PatternBuilder()
.text("$")
.expression("(...),") // type
- .expression("[LH],") // history
+ .expression("[LH],").optional() // history
.number("(d+),") // imei
.number("(dd)(dd)(dddd),") // date (ddmmyyyy)
.number("(dd)(dd)(dd),") // time (hhmmss)
@@ -47,6 +48,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW]),")
.number("(d+.?d*),") // speed
.number("(d+.?d*),") // course
+ .groupBegin()
.number("(-?d+.?d*),") // altitude
.number("(d+.d+),") // hdop
.number("(d+),") // satellites
@@ -65,6 +67,20 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
.number("d+.?d*,") // trip meter
.expression("([^,]*),") // software version
.expression("([^,]*),").optional() // rfid
+ .or()
+ .number("[^,]*,") // cid
+ .number("(d+),") // rssi
+ .number("(d+),") // satellites
+ .number("[^,]*,") // battery level
+ .expression("([01]),") // ignition
+ .expression("([LH]{4}),") // input
+ .expression("[NT]{4},") // tamper status
+ .expression("([LH]{2}),") // output
+ .number("(d+.d+),") // adc1
+ .number("(d+.d+),") // adc1
+ .number("[^,]*,") // device id
+ .number("(d+),") // odometer
+ .groupEnd()
.any()
.compile();
@@ -84,8 +100,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
switch (type) {
@@ -115,36 +130,63 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
- position.setValid(parser.nextInt(0) > 0);
+ position.setValid(parser.nextInt() > 0);
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- position.set(Position.KEY_HDOP, parser.nextDouble(0));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- position.set(Position.KEY_SATELLITES_VISIBLE, parser.nextInt(0));
- position.set(Position.KEY_OPERATOR, parser.next());
- position.set(Position.KEY_RSSI, parser.nextInt(0));
- position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1);
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
- position.set(Position.KEY_POWER, parser.nextDouble(0));
-
- int input = parser.nextBinInt(0);
- int output = parser.nextBinInt(0);
-
- if (!BitUtil.check(input, 0)) {
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ if (parser.hasNext(15)) {
+
+ position.setAltitude(parser.nextDouble());
+
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_SATELLITES_VISIBLE, parser.nextInt());
+ position.set(Position.KEY_OPERATOR, parser.next());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+
+ int input = parser.nextBinInt();
+ int output = parser.nextBinInt();
+
+ if (!BitUtil.check(input, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.KEY_OUTPUT, output);
+
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
+ position.set(Position.KEY_VERSION_FW, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+
}
- position.set(Position.KEY_INPUT, input);
- position.set(Position.KEY_OUTPUT, output);
+ if (parser.hasNext(6)) {
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
- position.set(Position.PREFIX_ADC + 1, parser.nextDouble(0));
- position.set(Position.PREFIX_ADC + 2, parser.nextDouble(0));
- position.set(Position.KEY_VERSION_FW, parser.next());
- position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ char[] input = parser.next().toCharArray();
+ for (int i = 0; i < input.length; i++) {
+ position.set(Position.PREFIX_IN + (i + 1), input[i] == 'H');
+ }
+
+ char[] output = parser.next().toCharArray();
+ for (int i = 0; i < output.length; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), output[i] == 'H');
+ }
+
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocol.java b/src/main/java/org/traccar/protocol/TopflytechProtocol.java
new file mode 100644
index 000000000..303072bdb
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TopflytechProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TopflytechProtocol extends BaseProtocol {
+
+ public TopflytechProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ')'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new TopflytechProtocolDecoder(TopflytechProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TopflytechProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java
index 837ca2557..6de053c32 100644
--- a/src/org/traccar/protocol/TopflytechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopflytechProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class TopflytechProtocolDecoder extends BaseProtocolDecoder {
- public TopflytechProtocolDecoder(TopflytechProtocol protocol) {
+ public TopflytechProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -53,8 +54,7 @@ public class TopflytechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java
new file mode 100644
index 000000000..844dd7518
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TopinProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class TopinProtocol extends BaseProtocol {
+
+ public TopinProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..eee0e9ae8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2019 - 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.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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.DateBuilder;
+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.util.TimeZone;
+
+public class TopinProtocolDecoder extends BaseProtocolDecoder {
+
+ public TopinProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_GPS = 0x10;
+ public static final int MSG_GPS_OFFLINE = 0x11;
+ public static final int MSG_STATUS = 0x13;
+ public static final int MSG_WIFI_OFFLINE = 0x17;
+ public static final int MSG_WIFI = 0x69;
+
+ private void sendResponse(Channel channel, int length, int type, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(0x7878);
+ response.writeByte(length);
+ response.writeByte(type);
+ response.writeBytes(content);
+ response.writeByte('\r');
+ response.writeByte('\n');
+ content.release();
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ int length = buf.readUnsignedByte();
+
+ int type = buf.readUnsignedByte();
+
+ DeviceSession deviceSession;
+ if (type == MSG_LOGIN) {
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(deviceSession != null ? 0x01 : 0x44);
+ sendResponse(channel, length, type, content);
+ return null;
+ } else {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+ }
+
+ if (type == MSG_GPS || type == MSG_GPS_OFFLINE) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ ByteBuf time = buf.slice(buf.readerIndex(), 6);
+
+ Gt06ProtocolDecoder.decodeGps(position, buf, false, TimeZone.getTimeZone("UTC"));
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeBytes(time);
+ sendResponse(channel, length, type, content);
+
+ return position;
+
+ } else if (type == MSG_STATUS) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ int battery = buf.readUnsignedByte();
+ int firmware = buf.readUnsignedByte();
+ int timezone = buf.readUnsignedByte();
+ int interval = buf.readUnsignedByte();
+ int signal = 0;
+ if (length >= 7) {
+ signal = buf.readUnsignedByte();
+ position.set(Position.KEY_RSSI, signal);
+ }
+
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ position.set(Position.KEY_VERSION_FW, firmware);
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(battery);
+ content.writeByte(firmware);
+ content.writeByte(timezone);
+ content.writeByte(interval);
+ if (length >= 7) {
+ content.writeByte(signal);
+ }
+ sendResponse(channel, length, type, content);
+
+ return position;
+
+ } else if (type == MSG_WIFI || type == MSG_WIFI_OFFLINE) {
+
+ 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));
+ time.resetReaderIndex();
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ Network network = new Network();
+ for (int i = 0; i < length; i++) {
+ String mac = String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(),
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ network.addWifiAccessPoint(WifiAccessPoint.from(mac, 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()));
+ }
+
+ position.setNetwork(network);
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeBytes(time);
+ sendResponse(channel, length, type, content);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/TotemFrameDecoder.java b/src/main/java/org/traccar/protocol/TotemFrameDecoder.java
index 6940d7b46..3fa5abc7a 100644
--- a/src/org/traccar/protocol/TotemFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/TotemFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,25 +15,26 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.StringFinder;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
import java.nio.charset.StandardCharsets;
-public class TotemFrameDecoder extends FrameDecoder {
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
+
+public class TotemFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 10) {
return null;
}
- int beginIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("$$"));
+ int beginIndex = BufferUtil.indexOf("$$", buf);
if (beginIndex == -1) {
return null;
} else if (beginIndex > buf.readerIndex()) {
@@ -42,15 +43,14 @@ public class TotemFrameDecoder extends FrameDecoder {
int length;
- int flagIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("AA"));
- if (flagIndex != -1 && flagIndex - beginIndex == 6) {
+ if (buf.getByte(buf.readerIndex() + 2) == (byte) '0') {
length = Integer.parseInt(buf.toString(buf.readerIndex() + 2, 4, StandardCharsets.US_ASCII));
} else {
length = Integer.parseInt(buf.toString(buf.readerIndex() + 2, 2, StandardCharsets.US_ASCII), 16);
}
if (length <= buf.readableBytes()) {
- return buf.readBytes(length);
+ return buf.readRetainedSlice(length);
}
return null;
diff --git a/src/org/traccar/protocol/TotemProtocol.java b/src/main/java/org/traccar/protocol/TotemProtocol.java
index 1c5cf5b02..f8cda8358 100644
--- a/src/org/traccar/protocol/TotemProtocol.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,36 +15,28 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
+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.model.Command;
-import java.util.List;
-
public class TotemProtocol extends BaseProtocol {
public TotemProtocol() {
- super("totem");
setSupportedDataCommands(
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ENGINE_STOP
);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new TotemFrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new TotemProtocolEncoder());
- pipeline.addLast("objectDecoder", new TotemProtocolDecoder(TotemProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TotemFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TotemProtocolEncoder(TotemProtocol.this));
+ pipeline.addLast(new TotemProtocolDecoder(TotemProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
index a3e8c9921..cd7f684b8 100644
--- a/src/org/traccar/protocol/TotemProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -31,7 +35,7 @@ import java.util.regex.Pattern;
public class TotemProtocolDecoder extends BaseProtocolDecoder {
- public TotemProtocolDecoder(TotemProtocol protocol) {
+ public TotemProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -131,7 +135,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN4 = new PatternBuilder()
.text("$$") // header
.number("dddd") // length
- .expression("A[ABC]") // type
+ .number("(xx)") // type
.number("(d+)|") // imei
.number("(x{8})") // status
.number("(dd)(dd)(dd)") // date (yymmdd)
@@ -146,10 +150,14 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.number("(dddd)") // adc 4
.groupEnd("?")
.number("(dddd)") // temperature 1
- .number("(dddd)") // temperature 2
+ .number("(dddd)?") // temperature 2
.groupEnd("?")
.number("(xxxx)") // lac
.number("(xxxx)") // cid
+ .groupBegin()
+ .number("(dd)") // mcc
+ .number("(ddd)") // mnc
+ .groupEnd("?")
.number("(dd)") // satellites
.number("(dd)") // gsm (rssi)
.number("(ddd)") // course
@@ -163,7 +171,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
- private String decodeAlarm(Short value) {
+ private String decodeAlarm123(int value) {
switch (value) {
case 0x01:
return Position.ALARM_SOS;
@@ -182,10 +190,31 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private String decodeAlarm4(int value) {
+ switch (value) {
+ case 0x01:
+ return Position.ALARM_SOS;
+ case 0x02:
+ return Position.ALARM_OVERSPEED;
+ case 0x04:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 0x05:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 0x40:
+ return Position.ALARM_SHOCK;
+ case 0x42:
+ return Position.ALARM_ACCELERATION;
+ case 0x43:
+ return Position.ALARM_BRAKING;
+ default:
+ return null;
+ }
+ }
+
private boolean decode12(Position position, Parser parser, Pattern pattern) {
if (parser.hasNext()) {
- position.set(Position.KEY_ALARM, decodeAlarm(Short.parseShort(parser.next(), 16)));
+ position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16)));
}
DateBuilder dateBuilder = new DateBuilder();
int year = 0, month = 0, day = 0;
@@ -221,12 +250,29 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_HDOP, parser.nextDouble());
}
- position.set(Position.PREFIX_IO + 1, parser.next());
+ int io = parser.nextBinInt();
+ position.set(Position.KEY_STATUS, io);
if (pattern == PATTERN1) {
+ position.set(Position.KEY_ALARM, BitUtil.check(io, 0) ? Position.ALARM_SOS : null);
+ position.set(Position.PREFIX_IN + 3, BitUtil.check(io, 4));
+ position.set(Position.PREFIX_IN + 4, BitUtil.check(io, 5));
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(io, 6));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(io, 7));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 8));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 9));
position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.01);
} else {
+ position.set(Position.KEY_ANTENNA, BitUtil.check(io, 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(io, 1));
+ for (int i = 1; i <= 6; i++) {
+ position.set(Position.PREFIX_IN + i, BitUtil.check(io, 1 + i));
+ }
+ for (int i = 1; i <= 4; i++) {
+ position.set(Position.PREFIX_OUT + i, BitUtil.check(io, 7 + i));
+ }
position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.1);
}
+
position.set(Position.KEY_POWER, parser.nextDouble(0));
position.set(Position.PREFIX_ADC + 1, parser.next());
@@ -245,7 +291,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
private boolean decode3(Position position, Parser parser) {
if (parser.hasNext()) {
- position.set(Position.KEY_ALARM, decodeAlarm(Short.parseShort(parser.next(), 16)));
+ position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16)));
}
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
@@ -276,31 +322,63 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
private boolean decode4(Position position, Parser parser) {
- position.set(Position.KEY_STATUS, parser.next());
+ long status = parser.nextHexLong();
+
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 1) ? Position.ALARM_SOS : null);
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 32 - 2));
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 3) ? Position.ALARM_OVERSPEED : null);
+ position.set(Position.KEY_CHARGE, BitUtil.check(status, 32 - 4));
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 5) ? Position.ALARM_GEOFENCE_EXIT : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 6) ? Position.ALARM_GEOFENCE_ENTER : null);
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 32 - 9));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 32 - 10));
+ position.set(Position.PREFIX_OUT + 3, BitUtil.check(status, 32 - 11));
+ position.set(Position.PREFIX_OUT + 4, BitUtil.check(status, 32 - 12));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(status, 32 - 13));
+ position.set(Position.PREFIX_IN + 3, BitUtil.check(status, 32 - 14));
+ position.set(Position.PREFIX_IN + 4, BitUtil.check(status, 32 - 15));
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 16) ? Position.ALARM_SHOCK : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 18) ? Position.ALARM_LOW_BATTERY : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 22) ? Position.ALARM_JAMMING : null);
+
position.setTime(parser.nextDateTime());
- position.set(Position.KEY_BATTERY, parser.nextDouble(0) * 0.1);
- position.set(Position.KEY_POWER, parser.nextDouble(0));
+ position.set(Position.KEY_BATTERY, parser.nextDouble() * 0.1);
+ position.set(Position.KEY_POWER, parser.nextDouble());
position.set(Position.PREFIX_ADC + 1, parser.next());
position.set(Position.PREFIX_ADC + 2, parser.next());
position.set(Position.PREFIX_ADC + 3, parser.next());
position.set(Position.PREFIX_ADC + 4, parser.next());
position.set(Position.PREFIX_TEMP + 1, parser.next());
- position.set(Position.PREFIX_TEMP + 2, parser.next());
- CellTower cellTower = CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- cellTower.setSignalStrength(parser.nextInt(0));
+ if (parser.hasNext()) {
+ position.set(Position.PREFIX_TEMP + 2, parser.next());
+ position.setValid(BitUtil.check(status, 32 - 20));
+ } else {
+ position.setValid(BitUtil.check(status, 32 - 18));
+ }
+
+ int lac = parser.nextHexInt();
+ int cid = parser.nextHexInt();
+ CellTower cellTower;
+ if (parser.hasNext(2)) {
+ int mnc = parser.nextInt();
+ int mcc = parser.nextInt();
+ cellTower = CellTower.from(mcc, mnc, lac, cid);
+ } else {
+ cellTower = CellTower.fromLacCid(lac, cid);
+ }
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ cellTower.setSignalStrength(parser.nextInt());
position.setNetwork(new Network(cellTower));
- position.setCourse(parser.nextDouble(0));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.set(Position.KEY_HDOP, parser.nextDouble(0));
- position.set(Position.KEY_ODOMETER, parser.nextInt(0) * 1000);
+ position.setCourse(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt() * 1000);
- position.setValid(true);
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
@@ -313,7 +391,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
Pattern pattern = PATTERN3;
- if (sentence.indexOf("A") == 6) {
+ if (sentence.charAt(2) == '0') {
pattern = PATTERN4;
} else if (sentence.contains("$GPRMC")) {
pattern = PATTERN1;
@@ -329,8 +407,11 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
+
+ if (pattern == PATTERN4) {
+ position.set(Position.KEY_ALARM, decodeAlarm4(parser.nextHexInt()));
+ }
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -349,9 +430,11 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
if (channel != null) {
if (pattern == PATTERN4) {
- channel.write("$$0014AA" + sentence.substring(sentence.length() - 6));
+ String response = "$$0014AA" + sentence.substring(sentence.length() - 6, sentence.length() - 2);
+ response += String.format("%02X", Checksum.xor(response)).toUpperCase();
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
} else {
- channel.write("ACK OK\r\n");
+ channel.writeAndFlush(new NetworkMessage("ACK OK\r\n", remoteAddress));
}
}
diff --git a/src/org/traccar/protocol/TotemProtocolEncoder.java b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
index ff41a7df3..a96dd1ee3 100644
--- a/src/org/traccar/protocol/TotemProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Irving Gonzalez
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,11 +17,15 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class TotemProtocolEncoder extends StringProtocolEncoder {
+ public TotemProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -30,15 +34,12 @@ public class TotemProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
// 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 formatCommand(command, "*%s,025,C,1#", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "*{%s},025,C,0#", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "*%s,025,C,0#", Command.KEY_DEVICE_PASSWORD);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/Tr20Protocol.java b/src/main/java/org/traccar/protocol/Tr20Protocol.java
new file mode 100644
index 000000000..3eee9d9c3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tr20Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Tr20Protocol extends BaseProtocol {
+
+ public Tr20Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Tr20ProtocolDecoder(Tr20Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Tr20ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
index 403a2fda2..c2e6c381f 100644
--- a/src/org/traccar/protocol/Tr20ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tr20ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +30,7 @@ import java.util.regex.Pattern;
public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
- public Tr20ProtocolDecoder(Tr20Protocol protocol) {
+ public Tr20ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -50,6 +52,9 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)(dd.d+),") // longitude
.number("(d+),") // speed
.number("(d+),") // course
+ .number("(?:NA|[FC]?(-?d+)),") // temperature
+ .number("(x{8}),") // status
+ .number("(d+)") // event
.any()
.compile();
@@ -60,7 +65,8 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
Parser parser = new Parser(PATTERN_PING, (String) msg);
if (parser.matches()) {
if (channel != null) {
- channel.write("&&" + parser.next() + "\r\n"); // keep-alive response
+ channel.writeAndFlush(new NetworkMessage(
+ "&&" + parser.next() + "\r\n", remoteAddress)); // keep-alive response
}
return null;
}
@@ -70,8 +76,7 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -85,8 +90,12 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.KEY_STATUS, parser.nextHexLong());
+ position.set(Position.KEY_EVENT, parser.nextInt());
return position;
}
diff --git a/src/main/java/org/traccar/protocol/Tr900Protocol.java b/src/main/java/org/traccar/protocol/Tr900Protocol.java
new file mode 100644
index 000000000..b70521b35
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tr900Protocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Tr900Protocol extends BaseProtocol {
+
+ public Tr900Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Tr900ProtocolDecoder(Tr900Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Tr900ProtocolDecoder(Tr900Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Tr900ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java
index 7dbdc5697..319194c21 100644
--- a/src/org/traccar/protocol/Tr900ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tr900ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 -2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
@@ -27,7 +28,7 @@ import java.util.regex.Pattern;
public class Tr900ProtocolDecoder extends BaseProtocolDecoder {
- public Tr900ProtocolDecoder(Tr900Protocol protocol) {
+ public Tr900ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -63,8 +64,7 @@ public class Tr900ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocol.java b/src/main/java/org/traccar/protocol/TrackboxProtocol.java
new file mode 100644
index 000000000..5da5abd64
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TrackboxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TrackboxProtocol extends BaseProtocol {
+
+ public TrackboxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TrackboxProtocolDecoder(TrackboxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TrackboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java
index 7e542af93..db8022738 100644
--- a/src/org/traccar/protocol/TrackboxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrackboxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +30,7 @@ import java.util.regex.Pattern;
public class TrackboxProtocolDecoder extends BaseProtocolDecoder {
- public TrackboxProtocolDecoder(TrackboxProtocol protocol) {
+ public TrackboxProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -46,9 +48,9 @@ public class TrackboxProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)") // satellites
.compile();
- private void sendResponse(Channel channel) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress) {
if (channel != null) {
- channel.write("=OK=\r\n");
+ channel.writeAndFlush(new NetworkMessage("=OK=\r\n", remoteAddress));
}
}
@@ -61,7 +63,7 @@ public class TrackboxProtocolDecoder extends BaseProtocolDecoder {
if (sentence.startsWith("a=connect")) {
String id = sentence.substring(sentence.indexOf("i=") + 2);
if (getDeviceSession(channel, remoteAddress, id) != null) {
- sendResponse(channel);
+ sendResponse(channel, remoteAddress);
}
return null;
}
@@ -75,10 +77,9 @@ public class TrackboxProtocolDecoder extends BaseProtocolDecoder {
if (!parser.matches()) {
return null;
}
- sendResponse(channel);
+ sendResponse(channel, remoteAddress);
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocol.java b/src/main/java/org/traccar/protocol/TrakMateProtocol.java
new file mode 100644
index 000000000..bda5df10f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TrakMateProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TrakMateProtocol extends BaseProtocol {
+
+ public TrakMateProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TrakMateProtocolDecoder(TrakMateProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TrakMateProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java
index 8965a18b4..4d5cb18f5 100644
--- a/src/org/traccar/protocol/TrakMateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrakMateProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,25 +15,21 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
import org.traccar.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 TrakMateProtocolDecoder extends BaseProtocolDecoder {
- private final TimeZone timeZone = TimeZone.getTimeZone("UTC");
-
- public TrakMateProtocolDecoder(TrakMateProtocol protocol) {
+ public TrakMateProtocolDecoder(Protocol protocol) {
super(protocol);
- timeZone.setRawOffset(Context.getConfig().getInteger(getProtocolName() + ".timezone") * 1000);
}
private static final Pattern PATTERN_SRT = new PatternBuilder()
@@ -49,7 +45,8 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN_PER = new PatternBuilder()
- .text("^TMPER|")
+ .text("^TM")
+ .expression("...|") // type
.expression("([^ ]+)|") // uid
.number("(d+)|") // seq
.number("(d+.d+)|") // latitude
@@ -58,17 +55,23 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)(dd)(dd)|") // date (ddmmyy)
.number("(d+.d+)|") // speed
.number("(d+.d+)|") // heading
- .number("(d+)|") // ignition
+ .number("(d+)|").optional() // satellites
+ .number("([01])|") // ignition
+ .groupBegin()
.number("(d+)|") // dop1
.number("(d+)|") // dop2
.number("(d+.d+)|") // analog
.number("(d+.d+)|") // internal battery
+ .or()
+ .number("-?d+ -?d+ -?d+|") // accelerometer
+ .number("([01])|") // movement
+ .groupEnd()
.number("(d+.d+)|") // vehicle battery
.number("(d+.d+)|") // gps odometer
- .number("(d+.d+)|") // pulse odometer
- .number("(d+)|") // main power status
- .number("(d+)|") // gps data validity
- .number("(d+)|") // live or cache
+ .number("(d+.d+)|").optional() // pulse odometer
+ .number("([01])|") // main power status
+ .number("([01])|") // gps data validity
+ .number("([01])|") // live or cache
.any()
.compile();
@@ -112,12 +115,11 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
@@ -139,21 +141,20 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
parser.next(); // seq
- position.set(Position.KEY_ALARM, decodeAlarm(parser.nextInt(0)));
+ position.set(Position.KEY_ALARM, decodeAlarm(parser.nextInt()));
parser.next(); // alert status or data
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
return position;
}
@@ -170,33 +171,41 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
parser.next(); // seq
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+
+ if (parser.hasNext(4)) {
+ position.set("dop1", parser.nextInt());
+ position.set("dop2", parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ }
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_MOTION, parser.nextInt(0) > 0);
+ }
- position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1);
- position.set("dop1", parser.next());
- position.set("dop2", parser.next());
- position.set(Position.KEY_INPUT, parser.next());
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
position.set(Position.KEY_POWER, parser.nextDouble());
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0));
- position.set("pulseOdometer", parser.next());
- position.set(Position.KEY_STATUS, parser.nextInt(0));
+ position.set(Position.KEY_ODOMETER, parser.nextDouble());
+ position.set("pulseOdometer", parser.nextDouble());
+ position.set(Position.KEY_STATUS, parser.nextInt());
- position.setValid(parser.nextInt(0) != 0);
+ position.setValid(parser.nextInt() > 0);
- position.set(Position.KEY_ARCHIVE, parser.nextInt(0) == 1);
+ position.set(Position.KEY_ARCHIVE, parser.nextInt() > 0);
return position;
}
@@ -216,10 +225,8 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
return decodeAlt(channel, remoteAddress, sentence);
case "SRT":
return decodeSrt(channel, remoteAddress, sentence);
- case "PER":
- return decodePer(channel, remoteAddress, sentence);
default:
- return null;
+ return decodePer(channel, remoteAddress, sentence);
}
}
diff --git a/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java b/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java
new file mode 100644
index 000000000..e4c94dc77
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java
@@ -0,0 +1,46 @@
+/*
+ * 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class TramigoFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 20) {
+ return null;
+ }
+
+ int length;
+ if (buf.getUnsignedByte(buf.readerIndex()) == 0x80) {
+ length = buf.getUnsignedShortLE(buf.readerIndex() + 6);
+ } else {
+ length = buf.getUnsignedShort(buf.readerIndex() + 6);
+ }
+
+ if (length <= buf.readableBytes()) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TramigoProtocol.java b/src/main/java/org/traccar/protocol/TramigoProtocol.java
new file mode 100644
index 000000000..f683ccc5d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TramigoProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class TramigoProtocol extends BaseProtocol {
+
+ public TramigoProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TramigoFrameDecoder());
+ pipeline.addLast(new TramigoProtocolDecoder(TramigoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TramigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java
index b1e28e17d..e42e2f670 100644
--- a/src/org/traccar/protocol/TramigoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,11 +15,13 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -36,7 +38,7 @@ import java.util.regex.Pattern;
public class TramigoProtocolDecoder extends BaseProtocolDecoder {
- public TramigoProtocolDecoder(TramigoProtocol protocol) {
+ public TramigoProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -49,20 +51,21 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
int protocol = buf.readUnsignedByte();
+ boolean legacy = protocol == 0x80;
+
buf.readUnsignedByte(); // version id
- int index = buf.readUnsignedShort();
- int type = buf.readUnsignedShort();
+ int index = legacy ? buf.readUnsignedShort() : buf.readUnsignedShortLE();
+ int type = legacy ? buf.readUnsignedShort() : buf.readUnsignedShortLE();
buf.readUnsignedShort(); // length
buf.readUnsignedShort(); // mask
buf.readUnsignedShort(); // checksum
- long id = buf.readUnsignedInt();
+ long id = legacy ? buf.readUnsignedInt() : buf.readUnsignedIntLE();
buf.readUnsignedInt(); // time
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.set(Position.KEY_INDEX, index);
position.setValid(true);
@@ -76,36 +79,37 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder {
// need to send ack?
- buf.readUnsignedShort(); // report trigger
- buf.readUnsignedShort(); // state flag
+ buf.readUnsignedShortLE(); // report trigger
+ buf.readUnsignedShortLE(); // state flag
- position.setLatitude(buf.readUnsignedInt() * 0.0000001);
- position.setLongitude(buf.readUnsignedInt() * 0.0000001);
+ position.setLatitude(buf.readUnsignedIntLE() * 0.0000001);
+ position.setLongitude(buf.readUnsignedIntLE() * 0.0000001);
- position.set(Position.KEY_RSSI, buf.readUnsignedShort());
- position.set(Position.KEY_SATELLITES, buf.readUnsignedShort());
- position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedShort());
- position.set("gpsAntennaStatus", buf.readUnsignedShort());
+ position.set(Position.KEY_RSSI, buf.readUnsignedShortLE());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedShortLE());
+ position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedShortLE());
+ position.set("gpsAntennaStatus", buf.readUnsignedShortLE());
- position.setSpeed(buf.readUnsignedShort() * 0.194384);
- position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(buf.readUnsignedShortLE() * 0.194384);
+ position.setCourse(buf.readUnsignedShortLE());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE());
- position.set(Position.KEY_CHARGE, buf.readUnsignedShort());
+ position.set(Position.KEY_CHARGE, buf.readUnsignedShortLE());
- position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
// parse other data
return position;
- } else if (protocol == 0x80) {
+ } else if (legacy) {
if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer("gprs,ack," + index, StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.copiedBuffer("gprs,ack," + index, StandardCharsets.US_ASCII), remoteAddress));
}
String sentence = buf.toString(StandardCharsets.US_ASCII);
diff --git a/src/main/java/org/traccar/protocol/TrvProtocol.java b/src/main/java/org/traccar/protocol/TrvProtocol.java
new file mode 100644
index 000000000..99a164cf1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TrvProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class TrvProtocol extends BaseProtocol {
+
+ public TrvProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TrvProtocolDecoder(TrvProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
index 918748f7b..05312b820 100644
--- a/src/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -25,6 +27,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.text.SimpleDateFormat;
@@ -33,13 +36,14 @@ import java.util.regex.Pattern;
public class TrvProtocolDecoder extends BaseProtocolDecoder {
- public TrvProtocolDecoder(TrvProtocol protocol) {
+ public TrvProtocolDecoder(Protocol protocol) {
super(protocol);
}
private static final Pattern PATTERN = new PatternBuilder()
.expression("[A-Z]{2,3}")
- .number("APdd")
+ .expression("[A-Z]P")
+ .number("dd")
.number("(dd)(dd)(dd)") // date (yymmdd)
.expression("([AV])") // validity
.number("(dd)(dd.d+)") // latitude
@@ -54,7 +58,8 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)") // battery
.number("(d)") // acc
.number("(dd)") // arm status
- .number("(dd),") // working mode
+ .number("(dd)") // working mode
+ .number("(?:[0-2]{3})?,")
.number("(d+),") // mcc
.number("(d+),") // mnc
.number("(d+),") // lac
@@ -84,6 +89,25 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_LBS = new PatternBuilder()
+ .expression("[A-Z]{2,3}")
+ .text("AP02,")
+ .expression("[^,]+,") // language
+ .number("[01],") // reply
+ .number("d+,") // cell count
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .expression("(")
+ .groupBegin()
+ .number("d+|") // lac
+ .number("d+|") // cid
+ .number("d+,") // rssi
+ .groupEnd("+")
+ .expression(")")
+ .number("d+,") // wifi count
+ .expression("(.*)") // wifi
+ .compile();
+
private Boolean decodeOptionalValue(Parser parser, int activeValue) {
int value = parser.nextInt();
if (value != 0) {
@@ -119,11 +143,11 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
String responseHeader = id + (char) (type.charAt(0) + 1) + type.substring(1);
if (type.equals("AP00") && id.equals("IW")) {
String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
- channel.write(responseHeader + "," + time + ",0#");
+ channel.writeAndFlush(new NetworkMessage(responseHeader + "," + time + ",0#", remoteAddress));
} else if (type.equals("AP14")) {
- channel.write(responseHeader + ",0.000,0.000#");
+ channel.writeAndFlush(new NetworkMessage(responseHeader + ",0.000,0.000#", remoteAddress));
} else {
- channel.write(responseHeader + "#");
+ channel.writeAndFlush(new NetworkMessage(responseHeader + "#", remoteAddress));
}
}
@@ -144,8 +168,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
@@ -160,15 +183,14 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
return position;
- } else if (type.equals("AP01") || type.equals("AP10")) {
+ } else if (type.equals("AP01") || type.equals("AP10") || type.equals("YP03")) {
Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -191,6 +213,45 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type.equals("AP02")) {
+
+ Parser parser = new Parser(PATTERN_LBS, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+
+ Network network = new Network();
+
+ for (String cell : parser.next().split(",")) {
+ if (!cell.isEmpty()) {
+ String[] values = cell.split("\\|");
+ network.addCellTower(CellTower.from(
+ mcc, mnc,
+ Integer.parseInt(values[0]),
+ Integer.parseInt(values[1]),
+ Integer.parseInt(values[2])));
+ }
+ }
+
+ for (String wifi : parser.next().split("&")) {
+ if (!wifi.isEmpty()) {
+ String[] values = wifi.split("\\|");
+ network.addWifiAccessPoint(WifiAccessPoint.from(values[1], Integer.parseInt(values[2])));
+ }
+ }
+
+ position.setNetwork(network);
+
+ return position;
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/Tt8850Protocol.java b/src/main/java/org/traccar/protocol/Tt8850Protocol.java
new file mode 100644
index 000000000..66a13da9e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Tt8850Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class Tt8850Protocol extends BaseProtocol {
+
+ public Tt8850Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "$"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Tt8850ProtocolDecoder(Tt8850Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Tt8850ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java
index 5e30d0994..1010528c4 100644
--- a/src/org/traccar/protocol/Tt8850ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tt8850ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -30,7 +31,7 @@ import java.util.regex.Pattern;
public class Tt8850ProtocolDecoder extends BaseProtocolDecoder {
- public Tt8850ProtocolDecoder(Tt8850Protocol protocol) {
+ public Tt8850ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -69,8 +70,7 @@ public class Tt8850ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/TytanProtocol.java b/src/main/java/org/traccar/protocol/TytanProtocol.java
index 0c27fb12b..32e9acae1 100644
--- a/src/org/traccar/protocol/TytanProtocol.java
+++ b/src/main/java/org/traccar/protocol/TytanProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,25 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class TytanProtocol extends BaseProtocol {
public TytanProtocol() {
- super("tytan");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new TytanProtocolDecoder(TytanProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TytanProtocolDecoder(TytanProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/TytanProtocolDecoder.java b/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java
index 0ae669784..93d3a63d2 100644
--- a/src/org/traccar/protocol/TytanProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TytanProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -32,11 +35,11 @@ import java.util.List;
public class TytanProtocolDecoder extends BaseProtocolDecoder {
- public TytanProtocolDecoder(TytanProtocol protocol) {
+ public TytanProtocolDecoder(Protocol protocol) {
super(protocol);
}
- private void decodeExtraData(Position position, ChannelBuffer buf, int end) {
+ private void decodeExtraData(Position position, ByteBuf buf, int end) {
while (buf.readerIndex() < end) {
int type = buf.readUnsignedByte();
@@ -73,10 +76,10 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder {
position.set("antihijack", buf.readUnsignedByte());
break;
case 9:
- position.set("unauthorized", ChannelBuffers.hexDump(buf.readBytes(8)));
+ position.set("unauthorized", ByteBufUtil.hexDump(buf.readSlice(8)));
break;
case 10:
- position.set("authorized", ChannelBuffers.hexDump(buf.readBytes(8)));
+ position.set("authorized", ByteBufUtil.hexDump(buf.readSlice(8)));
break;
case 24:
for (int i = 0; i < length / 2; i++) {
@@ -84,7 +87,7 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder {
}
break;
case 28:
- position.set("weight", buf.readUnsignedShort());
+ position.set(Position.KEY_AXLE_WEIGHT, buf.readUnsignedShort());
buf.readUnsignedByte();
break;
case 90:
@@ -124,16 +127,15 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.readUnsignedByte(); // protocol
buf.readUnsignedShort(); // length
int index = buf.readUnsignedByte() >> 3;
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.copiedBuffer(
- "^" + index, StandardCharsets.US_ASCII);
- channel.write(response, remoteAddress);
+ ByteBuf response = Unpooled.copiedBuffer("^" + index, StandardCharsets.US_ASCII);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
String id = String.valueOf(buf.readUnsignedInt());
@@ -146,8 +148,7 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder {
while (buf.readableBytes() > 2) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
int end = buf.readerIndex() + buf.readUnsignedByte();
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocol.java b/src/main/java/org/traccar/protocol/TzoneProtocol.java
new file mode 100644
index 000000000..6e855d138
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TzoneProtocol.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+
+public class TzoneProtocol extends BaseProtocol {
+
+ public TzoneProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 2, 2, 0));
+ pipeline.addLast(new TzoneProtocolDecoder(TzoneProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
index 079ad3126..4f6854098 100644
--- a/src/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,13 +15,15 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
@@ -30,7 +32,7 @@ import java.net.SocketAddress;
public class TzoneProtocolDecoder extends BaseProtocolDecoder {
- public TzoneProtocolDecoder(TzoneProtocol protocol) {
+ public TzoneProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -57,7 +59,78 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeCards(Position position, ChannelBuffer buf) {
+ private boolean decodeGps(Position position, ByteBuf buf, int hardware) {
+
+ int blockLength = buf.readUnsignedShort();
+ int blockEnd = buf.readerIndex() + blockLength;
+
+ if (blockLength < 22) {
+ return false;
+ }
+
+ if (hardware == 0x413) {
+ buf.readUnsignedByte(); // status
+ } else {
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ }
+
+ if (hardware == 0x413) {
+ position.setFixTime(new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()).getDate());
+ }
+
+ double lat;
+ double lon;
+
+ if (hardware == 0x10A || hardware == 0x10B) {
+ lat = buf.readUnsignedInt() / 600000.0;
+ lon = buf.readUnsignedInt() / 600000.0;
+ } else {
+ lat = buf.readUnsignedInt() / 100000.0 / 60.0;
+ lon = buf.readUnsignedInt() / 100000.0 / 60.0;
+ }
+
+ if (hardware == 0x413) {
+
+ position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
+
+ position.setAltitude(buf.readUnsignedShort());
+ position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ } else {
+
+ position.setFixTime(new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()).getDate());
+
+ position.setSpeed(buf.readUnsignedShort() * 0.01);
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+
+ int flags = buf.readUnsignedShort();
+ position.setCourse(BitUtil.to(flags, 9));
+ if (!BitUtil.check(flags, 10)) {
+ lat = -lat;
+ }
+ position.setLatitude(lat);
+ if (BitUtil.check(flags, 9)) {
+ lon = -lon;
+ }
+ position.setLongitude(lon);
+ position.setValid(BitUtil.check(flags, 11));
+
+ }
+
+ buf.readerIndex(blockEnd);
+
+ return true;
+ }
+
+ private void decodeCards(Position position, ByteBuf buf) {
int index = 1;
for (int i = 0; i < 4; i++) {
@@ -77,7 +150,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
length += 1;
}
- String num = ChannelBuffers.hexDump(buf.readBytes(length / 2));
+ String num = ByteBufUtil.hexDump(buf.readSlice(length / 2));
if (odd) {
num = num.substring(1);
@@ -92,7 +165,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
- private void decodePassengers(Position position, ChannelBuffer buf) {
+ private void decodePassengers(Position position, ByteBuf buf) {
int blockLength = buf.readUnsignedShort();
int blockEnd = buf.readerIndex() + blockLength;
@@ -108,11 +181,43 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
+ private void decodeTags(Position position, ByteBuf buf) {
+
+ int blockLength = buf.readUnsignedShort();
+ int blockEnd = buf.readerIndex() + blockLength;
+
+ if (blockLength > 0) {
+
+ buf.readUnsignedByte(); // tag type
+
+ int count = buf.readUnsignedByte();
+ int tagLength = buf.readUnsignedByte();
+
+ for (int i = 1; i <= count; i++) {
+ int tagEnd = buf.readerIndex() + tagLength;
+
+ buf.readUnsignedByte(); // status
+ buf.readUnsignedShortLE(); // battery voltage
+
+ position.set(Position.PREFIX_TEMP + i, (buf.readShortLE() & 0x3fff) * 0.1);
+
+ buf.readUnsignedByte(); // humidity
+ buf.readUnsignedByte(); // rssi
+
+ buf.readerIndex(tagEnd);
+ }
+
+ }
+
+ buf.readerIndex(blockEnd);
+
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(2); // header
buf.readUnsignedShort(); // length
@@ -122,14 +227,13 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
int hardware = buf.readUnsignedShort();
long firmware = buf.readUnsignedInt();
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_VERSION_HW, hardware);
@@ -141,54 +245,18 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
// GPS info
- int blockLength = buf.readUnsignedShort();
- int blockEnd = buf.readerIndex() + blockLength;
-
- if (blockLength < 22) {
- return null;
- }
-
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ if (hardware == 0x406 || !decodeGps(position, buf, hardware)) {
- double lat;
- double lon;
+ getLastLocation(position, position.getDeviceTime());
- if (hardware == 0x10A || hardware == 0x10B) {
- lat = buf.readUnsignedInt() / 600000.0;
- lon = buf.readUnsignedInt() / 600000.0;
- } else {
- lat = buf.readUnsignedInt() / 100000.0 / 60.0;
- lon = buf.readUnsignedInt() / 100000.0 / 60.0;
- }
-
- position.setFixTime(new DateBuilder()
- .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()).getDate());
-
- position.setSpeed(buf.readUnsignedShort() * 0.01);
-
- position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
-
- int flags = buf.readUnsignedShort();
- position.setCourse(BitUtil.to(flags, 9));
- if (!BitUtil.check(flags, 10)) {
- lat = -lat;
- }
- position.setLatitude(lat);
- if (BitUtil.check(flags, 9)) {
- lon = -lon;
}
- position.setLongitude(lon);
- position.setValid(BitUtil.check(flags, 11));
-
- buf.readerIndex(blockEnd);
// LBS info
- blockLength = buf.readUnsignedShort();
- blockEnd = buf.readerIndex() + blockLength;
+ int blockLength = buf.readUnsignedShort();
+ int blockEnd = buf.readerIndex() + blockLength;
- if (blockLength > 0 && (hardware == 0x10A || hardware == 0x10B)) {
+ if (blockLength > 0 && (hardware == 0x10A || hardware == 0x10B || hardware == 0x406)) {
position.setNetwork(new Network(
CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
}
@@ -227,7 +295,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
buf.readerIndex(blockEnd);
- if (hardware == 0x10A || hardware == 0x10B) {
+ if (hardware == 0x10B) {
decodeCards(position, buf);
@@ -238,6 +306,12 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
+ if (hardware == 0x406) {
+
+ decodeTags(position, buf);
+
+ }
+
return position;
}
diff --git a/src/org/traccar/protocol/UlbotechFrameDecoder.java b/src/main/java/org/traccar/protocol/UlbotechFrameDecoder.java
index 8e7b497c5..f141dc9b7 100644
--- a/src/org/traccar/protocol/UlbotechFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/UlbotechFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,19 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.traccar.BaseFrameDecoder;
-public class UlbotechFrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class UlbotechFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 2) {
return null;
@@ -37,7 +36,7 @@ public class UlbotechFrameDecoder extends FrameDecoder {
int index = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 0xF8);
if (index != -1) {
- ChannelBuffer result = ChannelBuffers.buffer(index + 1 - buf.readerIndex());
+ ByteBuf result = Unpooled.buffer(index + 1 - buf.readerIndex());
while (buf.readerIndex() <= index) {
int b = buf.readUnsignedByte();
@@ -60,7 +59,7 @@ public class UlbotechFrameDecoder extends FrameDecoder {
int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '#');
if (index != -1) {
- return buf.readBytes(index + 1 - buf.readerIndex());
+ return buf.readRetainedSlice(index + 1 - buf.readerIndex());
}
}
diff --git a/src/org/traccar/protocol/UlbotechProtocol.java b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
index 40f4594a5..b99ec1cc6 100644
--- a/src/org/traccar/protocol/UlbotechProtocol.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class UlbotechProtocol extends BaseProtocol {
public UlbotechProtocol() {
- super("ulbotech");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new UlbotechFrameDecoder());
- pipeline.addLast("objectDecoder", new UlbotechProtocolDecoder(UlbotechProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new UlbotechFrameDecoder());
+ pipeline.addLast(new UlbotechProtocolDecoder(UlbotechProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
index 31a3d2cfe..7fec0bf8b 100644
--- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
@@ -39,11 +41,8 @@ import java.util.regex.Pattern;
public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
- private final long timeZone;
-
- public UlbotechProtocolDecoder(UlbotechProtocol protocol) {
+ public UlbotechProtocolDecoder(Protocol protocol) {
super(protocol);
- timeZone = Context.getConfig().getInteger(getProtocolName() + ".timezone", 0);
}
private static final short DATA_GPS = 0x01;
@@ -62,18 +61,18 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
private static final short DATA_RFID = 0x0E;
private static final short DATA_EVENT = 0x10;
- private void decodeObd(Position position, ChannelBuffer buf, int length) {
+ private void decodeObd(Position position, ByteBuf buf, int length) {
int end = buf.readerIndex() + length;
while (buf.readerIndex() < end) {
int parameterLength = buf.getUnsignedByte(buf.readerIndex()) >> 4;
int mode = buf.readUnsignedByte() & 0x0F;
- position.add(ObdDecoder.decode(mode, ChannelBuffers.hexDump(buf.readBytes(parameterLength - 1))));
+ position.add(ObdDecoder.decode(mode, ByteBufUtil.hexDump(buf.readSlice(parameterLength - 1))));
}
}
- private void decodeJ1708(Position position, ChannelBuffer buf, int length) {
+ private void decodeJ1708(Position position, ByteBuf buf, int length) {
int end = buf.readerIndex() + length;
@@ -85,14 +84,14 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
if (type == 3) {
id += 256;
}
- String value = ChannelBuffers.hexDump(buf.readBytes(len - 1));
+ String value = ByteBufUtil.hexDump(buf.readSlice(len - 1));
if (type == 2 || type == 3) {
position.set("pid" + id, value);
}
}
}
- private void decodeDriverBehavior(Position position, ChannelBuffer buf) {
+ private void decodeDriverBehavior(Position position, ByteBuf buf) {
int value = buf.readUnsignedByte();
@@ -141,7 +140,7 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private void decodeAdc(Position position, ChannelBuffer buf, int length) {
+ private void decodeAdc(Position position, ByteBuf buf, int length) {
for (int i = 0; i < length / 2; i++) {
int value = buf.readUnsignedShort();
int id = BitUtil.from(value, 12);
@@ -188,8 +187,7 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
@@ -203,26 +201,29 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private Object decodeBinary(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ private Object decodeBinary(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
buf.readUnsignedByte(); // header
buf.readUnsignedByte(); // version
buf.readUnsignedByte(); // type
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ if (deviceSession.getTimeZone() == null) {
+ deviceSession.setTimeZone(getTimeZone(deviceSession.getDeviceId()));
+ }
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
long seconds = buf.readUnsignedInt() & 0x7fffffffL;
seconds += 946684800L; // 2000-01-01 00:00
- seconds -= timeZone;
+ seconds -= deviceSession.getTimeZone().getRawOffset() / 1000;
Date time = new Date(seconds * 1000);
boolean hasLocation = false;
@@ -236,12 +237,13 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
case DATA_GPS:
hasLocation = true;
- position.setValid(true);
position.setLatitude(buf.readInt() / 1000000.0);
position.setLongitude(buf.readInt() / 1000000.0);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
position.setCourse(buf.readUnsignedShort());
- position.set(Position.KEY_HDOP, buf.readUnsignedShort());
+ int hdop = buf.readUnsignedShort();
+ position.setValid(hdop < 9999);
+ position.set(Position.KEY_HDOP, hdop * 0.01);
break;
case DATA_LBS:
@@ -296,7 +298,7 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
break;
case DATA_CANBUS:
- position.set("can", ChannelBuffers.hexDump(buf.readBytes(length)));
+ position.set("can", ByteBufUtil.hexDump(buf.readSlice(length)));
break;
case DATA_J1708:
@@ -304,12 +306,12 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
break;
case DATA_VIN:
- position.set(Position.KEY_VIN, buf.readBytes(length).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_VIN, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
break;
case DATA_RFID:
position.set(Position.KEY_DRIVER_UNIQUE_ID,
- buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII));
+ buf.readSlice(length - 1).toString(StandardCharsets.US_ASCII));
position.set("authorized", buf.readUnsignedByte() != 0);
break;
@@ -339,28 +341,28 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (buf.getUnsignedByte(buf.readerIndex()) == 0xF8) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ ByteBuf response = Unpooled.buffer();
response.writeByte(0xF8);
response.writeByte(DATA_GPS);
response.writeByte(0xFE);
response.writeShort(buf.getShort(response.writerIndex() - 1 - 2));
- response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.toByteBuffer(1, 4)));
+ response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.nioBuffer(1, 4)));
response.writeByte(0xF8);
- channel.write(response);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
return decodeBinary(channel, remoteAddress, buf);
} else {
if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer(String.format("*TS01,ACK:%04X#",
- Checksum.crc16(Checksum.CRC16_XMODEM, buf.toByteBuffer(1, buf.writerIndex() - 2))),
- StandardCharsets.US_ASCII));
+ channel.writeAndFlush(new NetworkMessage(Unpooled.copiedBuffer(String.format("*TS01,ACK:%04X#",
+ Checksum.crc16(Checksum.CRC16_XMODEM, buf.nioBuffer(1, buf.writerIndex() - 2))),
+ StandardCharsets.US_ASCII), remoteAddress));
}
return decodeText(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
diff --git a/src/main/java/org/traccar/protocol/UproProtocol.java b/src/main/java/org/traccar/protocol/UproProtocol.java
new file mode 100644
index 000000000..4e60ffeb6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/UproProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class UproProtocol extends BaseProtocol {
+
+ public UproProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..a17003fc8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2012 - 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 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.NetworkMessage;
+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.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class UproProtocolDecoder extends BaseProtocolDecoder {
+
+ public UproProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN_HEADER = new PatternBuilder()
+ .text("*")
+ .expression("(..20)") // head
+ .expression("([01])") // ack
+ .number("(d+),") // device id
+ .expression("(.)") // type
+ .expression("(.)") // subtype
+ .any()
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .number("(dd)(dd)(dddd)") // latitude
+ .number("(ddd)(dd)(dddd)") // longitude
+ .number("(d)") // flags
+ .number("(dd)") // speed
+ .number("(dd)") // course
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .compile();
+
+ private void decodeLocation(Position position, String data) {
+ Parser parser = new Parser(PATTERN_LOCATION, data);
+ if (parser.matches()) {
+
+ 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));
+ if (!BitUtil.check(flags, 1)) {
+ position.setLatitude(-position.getLatitude());
+ }
+ if (!BitUtil.check(flags, 2)) {
+ position.setLongitude(-position.getLongitude());
+ }
+
+ position.setSpeed(parser.nextInt(0) * 2);
+ position.setCourse(parser.nextInt(0) * 10);
+
+ dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ position.setTime(dateBuilder.getDate());
+
+ }
+ }
+
+ private String decodeAlarm(int alarm) {
+ if (BitUtil.check(alarm, 2)) {
+ return Position.ALARM_TAMPERING;
+ }
+ return null;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (buf.getByte(buf.readerIndex()) != '*') {
+ return null;
+ }
+
+ int headerIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '&');
+ if (headerIndex < 0) {
+ headerIndex = buf.writerIndex();
+ }
+ String header = buf.readSlice(headerIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
+
+ Parser parser = new Parser(PATTERN_HEADER, header);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String head = parser.next();
+ boolean reply = parser.next().equals("1");
+
+ 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();
+ String subtype = parser.next();
+
+ if (reply && channel != null) {
+ channel.writeAndFlush(new NetworkMessage("*" + head + "Y" + type + subtype + "#", remoteAddress));
+ }
+
+ while (buf.isReadable()) {
+
+ buf.readByte(); // skip delimiter
+
+ byte dataType = buf.readByte();
+
+ int delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '&');
+ if (delimiterIndex < 0) {
+ delimiterIndex = buf.writerIndex();
+ }
+
+ ByteBuf data = buf.readSlice(delimiterIndex - buf.readerIndex());
+
+ switch (dataType) {
+ case 'A':
+ decodeLocation(position, data.toString(StandardCharsets.US_ASCII));
+ break;
+ case 'B':
+ position.set(Position.KEY_STATUS, data.toString(StandardCharsets.US_ASCII));
+ break;
+ case 'C':
+ long odometer = 0;
+ while (data.isReadable()) {
+ odometer <<= 4;
+ odometer += data.readByte() - (byte) '0';
+ }
+ position.set(Position.KEY_ODOMETER, odometer * 2 * 1852 / 3600);
+ break;
+ case 'F':
+ position.setSpeed(
+ Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII)) * 0.1);
+ break;
+ case 'G':
+ position.setAltitude(
+ Integer.parseInt(data.readSlice(6).toString(StandardCharsets.US_ASCII)) * 0.1);
+ break;
+ case 'J':
+ if (data.readableBytes() == 6) {
+ char index = (char) data.readUnsignedByte();
+ int status = data.readUnsignedByte();
+ double value = Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII)) * 0.1;
+ if (BitUtil.check(status, 0)) {
+ value = -value;
+ }
+ position.set(Position.PREFIX_TEMP + index, value);
+ }
+ break;
+ case 'K':
+ position.set("statusExtended", data.toString(StandardCharsets.US_ASCII));
+ break;
+ case 'M':
+ if (data.readableBytes() == 3) {
+ position.set(Position.KEY_BATTERY_LEVEL,
+ Integer.parseInt(data.readSlice(3).toString(StandardCharsets.US_ASCII)) * 0.1);
+ } else if (data.readableBytes() == 4) {
+ char index = (char) data.readUnsignedByte();
+ data.readUnsignedByte(); // status
+ position.set(
+ "humidity" + index,
+ Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII)));
+ }
+ break;
+ case 'N':
+ position.set(Position.KEY_RSSI,
+ Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII)));
+ break;
+ case 'O':
+ position.set(Position.KEY_SATELLITES,
+ Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII)));
+ break;
+ case 'P':
+ if (data.readableBytes() >= 16) {
+ position.setNetwork(new Network(CellTower.from(
+ Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII)),
+ Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII)),
+ Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII), 16),
+ Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII), 16))));
+ }
+ break;
+ case 'Q':
+ position.set("obdPid", ByteBufUtil.hexDump(data));
+ break;
+ case 'R':
+ if (head.startsWith("HQ")) {
+ position.set(Position.KEY_RSSI,
+ Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII)));
+ position.set(Position.KEY_SATELLITES,
+ Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII)));
+ } else {
+ position.set("odbTravel", ByteBufUtil.hexDump(data));
+ }
+ break;
+ case 'S':
+ position.set("obdTraffic", ByteBufUtil.hexDump(data));
+ break;
+ case 'V':
+ position.set(Position.KEY_POWER,
+ Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII)) * 0.1);
+ break;
+ case 'W':
+ position.set(Position.KEY_ALARM,
+ decodeAlarm(Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII))));
+ break;
+ case 'X':
+ Network network = new Network();
+ int mcc = 0, mnc = 0;
+ String[] cells = data.toString(StandardCharsets.US_ASCII).split(";");
+ if (!cells[0].startsWith("(")) {
+ for (int i = 0; i < cells.length; i++) {
+ String[] values = cells[i].split(",");
+ int index = 0;
+ if (i == 0) {
+ mcc = Integer.parseInt(values[index++]);
+ mnc = Integer.parseInt(values[index++]);
+ }
+ network.addCellTower(CellTower.from(
+ mcc, mnc,
+ Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index++]),
+ Integer.parseInt(values[index])));
+ }
+ position.setNetwork(network);
+ }
+ break;
+ case 'Y':
+ position.set(Position.KEY_POWER,
+ Integer.parseInt(data.readSlice(5).toString(StandardCharsets.US_ASCII)) * 0.001);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ if (position.getLatitude() == 0 || position.getLongitude() == 0) {
+ if (position.getAttributes().isEmpty()) {
+ return null;
+ }
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/V680Protocol.java b/src/main/java/org/traccar/protocol/V680Protocol.java
new file mode 100644
index 000000000..dc0922cd4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/V680Protocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class V680Protocol extends BaseProtocol {
+
+ public V680Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##"));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new V680ProtocolDecoder(V680Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new V680ProtocolDecoder(V680Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/V680ProtocolDecoder.java b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
index 079a8eb08..0342404a6 100644
--- a/src/org/traccar/protocol/V680ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class V680ProtocolDecoder extends BaseProtocolDecoder {
- public V680ProtocolDecoder(V680Protocol protocol) {
+ public V680ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -70,8 +71,7 @@ public class V680ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession;
if (parser.hasNext()) {
diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocol.java b/src/main/java/org/traccar/protocol/VisiontekProtocol.java
new file mode 100644
index 000000000..2c6af45a8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/VisiontekProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class VisiontekProtocol extends BaseProtocol {
+
+ public VisiontekProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '#'));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new VisiontekProtocolDecoder(VisiontekProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java
index f32c9fbfe..c4787bda2 100644
--- a/src/org/traccar/protocol/VisiontekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/VisiontekProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class VisiontekProtocolDecoder extends BaseProtocolDecoder {
- public VisiontekProtocolDecoder(VisiontekProtocol protocol) {
+ public VisiontekProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -81,8 +82,7 @@ public class VisiontekProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next(), parser.next());
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/VnetProtocol.java b/src/main/java/org/traccar/protocol/VnetProtocol.java
new file mode 100644
index 000000000..0fed30e13
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/VnetProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class VnetProtocol extends BaseProtocol {
+
+ public VnetProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..1ee00bd3f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class VnetProtocolDecoder extends BaseProtocolDecoder {
+
+ public VnetProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x0000;
+ public static final int MSG_LBS = 0x32;
+ public static final int MSG_GPS = 0x33;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ int type = BitUtil.to(buf.readUnsignedShortLE(), 15);
+ buf.readUnsignedShortLE(); // length
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2))
+ .setTime(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
+
+ if (type == MSG_LOGIN) {
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ getDeviceSession(channel, remoteAddress, imei);
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ buf.retainedSlice(0, buf.writerIndex()), channel.remoteAddress()));
+ }
+
+ } else if (type == MSG_GPS) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(dateBuilder.getDate());
+
+ int value;
+ int degrees;
+
+ value = BcdUtil.readInteger(buf, 8);
+ degrees = value / 1000000;
+ double lat = degrees + value % 1000000 * 0.0001 / 60;
+
+ value = BcdUtil.readInteger(buf, 10);
+ degrees = value / 10000000;
+ double lon = degrees + value % 10000000 * 0.00001 / 60;
+
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 0));
+ position.setLatitude(BitUtil.check(flags, 1) ? lat : -lat);
+ position.setLongitude(BitUtil.check(flags, 2) ? lon : -lon);
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.setCourse(buf.readUnsignedByte() * 2);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Vt200FrameDecoder.java b/src/main/java/org/traccar/protocol/Vt200FrameDecoder.java
index adde12118..0fd83e715 100644
--- a/src/org/traccar/protocol/Vt200FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Vt200FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,22 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.traccar.BaseFrameDecoder;
-public class Vt200FrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class Vt200FrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ')') + 1;
if (endIndex > 0) {
- ChannelBuffer frame = ChannelBuffers.dynamicBuffer();
+ ByteBuf frame = Unpooled.buffer();
while (buf.readerIndex() < endIndex) {
int b = buf.readByte();
diff --git a/src/org/traccar/protocol/Vt200Protocol.java b/src/main/java/org/traccar/protocol/Vt200Protocol.java
index 59c61cb61..2a9ef6ab5 100644
--- a/src/org/traccar/protocol/Vt200Protocol.java
+++ b/src/main/java/org/traccar/protocol/Vt200Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,26 +15,18 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class Vt200Protocol extends BaseProtocol {
public Vt200Protocol() {
- super("vt200");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Vt200FrameDecoder());
- pipeline.addLast("objectDecoder", new Vt200ProtocolDecoder(Vt200Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Vt200FrameDecoder());
+ pipeline.addLast(new Vt200ProtocolDecoder(Vt200Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
index 2ae24efbb..b1564abd9 100644
--- a/src/org/traccar/protocol/Vt200ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,11 +15,12 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
+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.Protocol;
import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
@@ -32,7 +33,7 @@ import java.util.Date;
public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
- public Vt200ProtocolDecoder(Vt200Protocol protocol) {
+ public Vt200ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -42,7 +43,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes * 0.0001 / 60;
}
- protected Date decodeDate(ChannelBuffer buf) {
+ protected Date decodeDate(ByteBuf buf) {
DateBuilder dateBuilder = new DateBuilder()
.setDateReverse(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2))
.setTime(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
@@ -53,11 +54,11 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
buf.skipBytes(1); // header
- String id = ChannelBuffers.hexDump(buf.readBytes(6));
+ String id = ByteBufUtil.hexDump(buf.readSlice(6));
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
@@ -68,8 +69,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
if (type == 0x2086 || type == 0x2084 || type == 0x2082) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
buf.readUnsignedByte(); // data type
@@ -85,7 +85,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
if (!BitUtil.check(flags, 1)) {
position.setLatitude(-position.getLatitude());
}
- if (!BitUtil.check(flags, 1)) {
+ if (!BitUtil.check(flags, 2)) {
position.setLongitude(-position.getLongitude());
}
@@ -103,8 +103,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == 0x3088) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
diff --git a/src/org/traccar/protocol/VtfmsFrameDecoder.java b/src/main/java/org/traccar/protocol/VtfmsFrameDecoder.java
index 2e6033fcc..62a189960 100644
--- a/src/org/traccar/protocol/VtfmsFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/VtfmsFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,22 +15,23 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.traccar.BaseFrameDecoder;
-public class VtfmsFrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class VtfmsFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ')');
if (endIndex > 0) {
endIndex += 1 + 3;
if (buf.writerIndex() >= endIndex) {
- return buf.readBytes(endIndex - buf.readerIndex());
+ return buf.readRetainedSlice(endIndex - buf.readerIndex());
}
}
diff --git a/src/main/java/org/traccar/protocol/VtfmsProtocol.java b/src/main/java/org/traccar/protocol/VtfmsProtocol.java
new file mode 100644
index 000000000..2826a86e6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/VtfmsProtocol.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import io.netty.handler.codec.string.StringDecoder;
+
+public class VtfmsProtocol extends BaseProtocol {
+
+ public VtfmsProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new VtfmsFrameDecoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new VtfmsProtocolDecoder(VtfmsProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new VtfmsProtocolDecoder(VtfmsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/VtfmsProtocolDecoder.java b/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java
index 5fb687e6d..17fac4311 100644
--- a/src/org/traccar/protocol/VtfmsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/VtfmsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -30,7 +31,7 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
private static final String[] DIRECTIONS = new String[] {"N", "NE", "E", "SE", "S", "SW", "W", "NW"};
- public VtfmsProtocolDecoder(VtfmsProtocol protocol) {
+ public VtfmsProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -59,7 +60,7 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+),") // power voltage
.number("[^,]*,") // reserved
.number("(d+)?,") // fuel level
- .number("(d+.d+),") // adc 1
+ .number("(d+.d+)?,") // adc 1
.number("[^,]*,") // reserved
.number("(d+.d+)?,") // adc 2
.expression("([01]),") // di 1
@@ -92,6 +93,11 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private double convertToDegrees(double value) {
+ double degrees = Math.floor(value / 100);
+ return degrees + (value - degrees * 100) / 60;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -106,8 +112,7 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_ALARM, decodeAlarm(parser.nextInt()));
@@ -116,12 +121,18 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
position.setValid(parser.next().equals("A"));
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY));
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
- if (parser.hasNext()) {
- position.setCourse(parser.nextDouble(0));
+ double latitude = parser.nextDouble();
+ double longitude = parser.nextDouble();
+ if (Math.abs(latitude) > 90 || Math.abs(longitude) > 180) {
+ position.setLatitude(convertToDegrees(latitude));
+ position.setLongitude(convertToDegrees(longitude));
+ } else {
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
}
+
+ position.setCourse(parser.nextDouble(0));
if (parser.hasNext()) {
String direction = parser.next();
for (int i = 0; i < DIRECTIONS.length; i++) {
@@ -132,9 +143,9 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
}
}
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
- position.set(Position.KEY_HOURS, parser.nextInt());
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(parser.nextInt()));
position.set("idleHours", parser.nextInt());
position.set(Position.KEY_ODOMETER, parser.nextInt() * 100);
position.set(Position.KEY_CHARGE, parser.next().equals("1"));
diff --git a/src/main/java/org/traccar/protocol/WatchFrameDecoder.java b/src/main/java/org/traccar/protocol/WatchFrameDecoder.java
new file mode 100644
index 000000000..f99bd52e2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/WatchFrameDecoder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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 WatchFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ']') + 1;
+ if (endIndex > 0) {
+ ByteBuf frame = Unpooled.buffer();
+ while (buf.readerIndex() < endIndex) {
+ byte b1 = buf.readByte();
+ if (b1 == '}') {
+ byte b2 = buf.readByte();
+ switch (b2) {
+ case 0x01:
+ frame.writeByte('}');
+ break;
+ case 0x02:
+ frame.writeByte('[');
+ break;
+ case 0x03:
+ frame.writeByte(']');
+ break;
+ case 0x04:
+ frame.writeByte(',');
+ break;
+ case 0x05:
+ frame.writeByte('*');
+ break;
+ default:
+ throw new IllegalArgumentException(String.format(
+ "unexpected byte at %d: 0x%02x", buf.readerIndex() - 1, b2));
+ }
+ } else {
+ frame.writeByte(b1);
+ }
+ }
+ return frame;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/WatchProtocol.java b/src/main/java/org/traccar/protocol/WatchProtocol.java
index 2be2dc9ae..6dc3bf9fb 100644
--- a/src/org/traccar/protocol/WatchProtocol.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,19 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
-
public class WatchProtocol extends BaseProtocol {
public WatchProtocol() {
- super("watch");
setSupportedDataCommands(
Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
@@ -36,24 +31,21 @@ public class WatchProtocol extends BaseProtocol {
Command.TYPE_ALARM_SOS,
Command.TYPE_ALARM_BATTERY,
Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_POWER_OFF,
Command.TYPE_ALARM_REMOVE,
Command.TYPE_SILENCE_TIME,
Command.TYPE_ALARM_CLOCK,
Command.TYPE_SET_PHONEBOOK,
+ Command.TYPE_MESSAGE,
Command.TYPE_VOICE_MESSAGE,
Command.TYPE_SET_TIMEZONE,
Command.TYPE_SET_INDICATOR);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new WatchFrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new WatchProtocolEncoder());
- pipeline.addLast("objectDecoder", new WatchProtocolDecoder(WatchProtocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new WatchFrameDecoder());
+ pipeline.addLast(new WatchProtocolEncoder(WatchProtocol.this));
+ pipeline.addLast(new WatchProtocolDecoder(WatchProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 86dc9456d..0647afdee 100644
--- a/src/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+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.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -36,7 +41,9 @@ import java.util.regex.Pattern;
public class WatchProtocolDecoder extends BaseProtocolDecoder {
- public WatchProtocolDecoder(WatchProtocol protocol) {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WatchProtocolDecoder.class);
+
+ public WatchProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -48,7 +55,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
.expression("([NS]),")
.number(" *(-?d+.d+),") // longitude
.expression("([EW])?,")
- .number("(d+.d+),") // speed
+ .number("(d+.?d*),") // speed
.number("(d+.?d*),") // course
.number("(d+.?d*),") // altitude
.number("(d+),") // satellites
@@ -60,10 +67,18 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
.expression("(.*)") // cell and wifi
.compile();
- private void sendResponse(Channel channel, String manufacturer, String id, String content) {
+ private void sendResponse(Channel channel, String id, String index, String content) {
if (channel != null) {
- channel.write(String.format(
- "[%s*%s*%04x*%s]", manufacturer, id, content.length(), content));
+ String response;
+ if (index != null) {
+ response = String.format("[%s*%s*%s*%04x*%s]",
+ manufacturer, id, index, content.length(), content);
+ } else {
+ response = String.format("[%s*%s*%04x*%s]",
+ manufacturer, id, content.length(), content);
+ }
+ ByteBuf buf = Unpooled.copiedBuffer(response, StandardCharsets.US_ASCII);
+ channel.writeAndFlush(new NetworkMessage(buf, channel.remoteAddress()));
}
}
@@ -92,8 +107,38 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private void decodeTail(Position position, String data) {
- String[] values = data.split(",");
+ private Position decodePosition(DeviceSession deviceSession, String data) {
+
+ Parser parser = new Parser(PATTERN_POSITION, data);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt(0));
+ position.set(Position.KEY_RSSI, parser.nextInt(0));
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+
+ position.set(Position.KEY_STEPS, parser.nextInt(0));
+
+ int status = parser.nextHexInt(0);
+ position.set(Position.KEY_ALARM, decodeAlarm(status));
+ if (BitUtil.check(status, 4)) {
+ position.set(Position.KEY_MOTION, true);
+ }
+
+ String[] values = parser.next().split(",");
int index = 0;
Network network = new Network();
@@ -122,50 +167,78 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
position.setNetwork(network);
}
+
+ return position;
+ }
+
+ private boolean hasIndex;
+ private String manufacturer;
+
+ public boolean getHasIndex() {
+ return hasIndex;
+ }
+
+ public String getManufacturer() {
+ return manufacturer;
}
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
- buf.skipBytes(1); // header
- String manufacturer = buf.readBytes(2).toString(StandardCharsets.US_ASCII);
- buf.skipBytes(1); // delimiter
+ buf.skipBytes(1); // '[' header
+ manufacturer = buf.readSlice(2).toString(StandardCharsets.US_ASCII);
+ buf.skipBytes(1); // '*' delimiter
- String id = buf.readBytes(10).toString(StandardCharsets.US_ASCII);
+ int idIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*');
+ String id = buf.readSlice(idIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
- buf.skipBytes(1); // delimiter
+ buf.skipBytes(1); // '*' delimiter
+
+ String index = null;
+ int contentIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*');
+ if (contentIndex + 5 < buf.writerIndex() && buf.getByte(contentIndex + 5) == '*'
+ && buf.toString(contentIndex + 1, 4, StandardCharsets.US_ASCII).matches("\\p{XDigit}+")) {
+ int indexLength = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '*') - buf.readerIndex();
+ hasIndex = true;
+ index = buf.readSlice(indexLength).toString(StandardCharsets.US_ASCII);
+ buf.skipBytes(1); // '*' delimiter
+ }
+
buf.skipBytes(4); // length
- buf.skipBytes(1); // delimiter
+ buf.skipBytes(1); // '*' delimiter
- buf.writerIndex(buf.writerIndex() - 1); // ignore ending
+ buf.writerIndex(buf.writerIndex() - 1); // ']' ignore ending
- int contentIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
+ contentIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
if (contentIndex < 0) {
contentIndex = buf.writerIndex();
}
- String type = buf.readBytes(contentIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
+ String type = buf.readSlice(contentIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
if (contentIndex < buf.writerIndex()) {
buf.readerIndex(contentIndex + 1);
}
- if (type.equals("LK")) {
+ if (type.equals("INIT")) {
+
+ sendResponse(channel, id, index, "INIT,1");
+
+ } else if (type.equals("LK")) {
- sendResponse(channel, manufacturer, id, "LK");
+ sendResponse(channel, id, index, "LK");
- if (buf.readable()) {
+ if (buf.isReadable()) {
String[] values = buf.toString(StandardCharsets.US_ASCII).split(",");
if (values.length >= 3) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
@@ -176,65 +249,46 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
}
}
- } else if (type.equals("UD") || type.equals("UD2") || type.equals("UD3")
- || type.equals("AL") || type.equals("WT")) {
+ } else if (type.startsWith("UD") || type.equals("AL") || type.equals("WT")) {
- if (type.equals("AL")) {
- sendResponse(channel, manufacturer, id, "AL");
- }
-
- Parser parser = new Parser(PATTERN_POSITION, buf.toString(StandardCharsets.US_ASCII));
- if (!parser.matches()) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
-
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
+ Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- position.set(Position.KEY_RSSI, parser.nextInt(0));
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
-
- position.set(Position.KEY_STEPS, parser.nextInt(0));
-
- int status = parser.nextHexInt(0);
- position.set(Position.KEY_ALARM, decodeAlarm(status));
- if (BitUtil.check(status, 4)) {
- position.set(Position.KEY_MOTION, true);
+ if (type.equals("AL")) {
+ if (position != null) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ sendResponse(channel, id, index, "AL");
}
- decodeTail(position, parser.next());
-
return position;
} else if (type.equals("TKQ")) {
- sendResponse(channel, manufacturer, id, "TKQ");
+ sendResponse(channel, id, index, "TKQ");
- } else if (type.equals("PULSE") || type.equals("heart")) {
+ } else if (type.equalsIgnoreCase("PULSE")
+ || type.equalsIgnoreCase("HEART")
+ || type.equalsIgnoreCase("BLOOD")
+ || type.equalsIgnoreCase("BPHRT")) {
- if (buf.readable()) {
+ if (buf.isReadable()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, new Date());
- position.setValid(false);
- String pulse = buf.toString(StandardCharsets.US_ASCII);
- position.set("pulse", pulse);
- position.set(Position.KEY_RESULT, pulse);
+ String[] values = buf.toString(StandardCharsets.US_ASCII).split(",");
+ int valueIndex = 0;
+
+ if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) {
+ position.set("pressureHigh", values[valueIndex++]);
+ position.set("pressureLow", values[valueIndex++]);
+ }
+
+ if (valueIndex <= values.length - 1) {
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+ }
return position;
@@ -242,8 +296,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
} else if (type.equals("img")) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
@@ -256,8 +309,15 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
} else if (type.equals("TK")) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ if (buf.readableBytes() == 1) {
+ byte result = buf.readByte();
+ if (result != '1') {
+ LOGGER.warn(type + "," + result);
+ }
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, null);
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
new file mode 100644
index 000000000..f285267ba
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2016 - 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.StringProtocolEncoder;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+import java.nio.charset.StandardCharsets;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+public class WatchProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
+
+ public WatchProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ public String formatValue(String key, Object value) {
+ if (key.equals(Command.KEY_TIMEZONE)) {
+ double offset = TimeZone.getTimeZone((String) value).getRawOffset() / 3600000.0;
+ DecimalFormat fmt = new DecimalFormat("+#.##;-#.##", DecimalFormatSymbols.getInstance(Locale.US));
+ return fmt.format(offset);
+ } else if (key.equals(Command.KEY_MESSAGE)) {
+ return DataConverter.printHex(value.toString().getBytes(StandardCharsets.UTF_16BE));
+ } else if (key.equals(Command.KEY_ENABLE)) {
+ return (boolean) value ? "1" : "0";
+ }
+
+ return null;
+ }
+
+ protected ByteBuf formatTextCommand(Channel channel, Command command, String format, String... keys) {
+ String content = formatCommand(command, format, this, keys);
+ ByteBuf buf = Unpooled.copiedBuffer(content, StandardCharsets.US_ASCII);
+
+ return formatBinaryCommand(channel, command, "", buf);
+ }
+
+ protected ByteBuf formatBinaryCommand(Channel channel, Command command, String textPrefix, ByteBuf data) {
+ boolean hasIndex = false;
+ String manufacturer = "CS";
+ if (channel != null) {
+ WatchProtocolDecoder decoder = channel.pipeline().get(WatchProtocolDecoder.class);
+ if (decoder != null) {
+ hasIndex = decoder.getHasIndex();
+ manufacturer = decoder.getManufacturer();
+ }
+ }
+
+ ByteBuf buf = Unpooled.buffer();
+ buf.writeByte('[');
+ buf.writeCharSequence(manufacturer, StandardCharsets.US_ASCII);
+ buf.writeByte('*');
+ buf.writeCharSequence(getUniqueId(command.getDeviceId()), StandardCharsets.US_ASCII);
+ buf.writeByte('*');
+ if (hasIndex) {
+ buf.writeCharSequence("0001", StandardCharsets.US_ASCII);
+ buf.writeByte('*');
+ }
+ buf.writeCharSequence(String.format("%04x", data.readableBytes() + textPrefix.length()),
+ StandardCharsets.US_ASCII);
+ buf.writeByte('*');
+ buf.writeCharSequence(textPrefix, StandardCharsets.US_ASCII);
+ buf.writeBytes(data);
+ buf.writeByte(']');
+
+ return buf;
+ }
+
+ private static Map<Byte, Byte> mapping = new HashMap<>();
+
+ static {
+ mapping.put((byte) 0x7d, (byte) 0x01);
+ mapping.put((byte) 0x5B, (byte) 0x02);
+ mapping.put((byte) 0x5D, (byte) 0x03);
+ mapping.put((byte) 0x2C, (byte) 0x04);
+ mapping.put((byte) 0x2A, (byte) 0x05);
+ }
+
+ private ByteBuf getBinaryData(Command command) {
+ byte[] data = DataConverter.parseHex(command.getString(Command.KEY_DATA));
+
+ int encodedLength = data.length;
+ for (byte b : data) {
+ if (mapping.containsKey(b)) {
+ encodedLength += 1;
+ }
+ }
+
+ int index = 0;
+ byte[] encodedData = new byte[encodedLength];
+
+ for (byte b : data) {
+ Byte replacement = mapping.get(b);
+ if (replacement != null) {
+ encodedData[index] = 0x7D;
+ index += 1;
+ encodedData[index] = replacement;
+ } else {
+ encodedData[index] = b;
+ }
+ index += 1;
+ }
+
+ return Unpooled.copiedBuffer(encodedData);
+ }
+
+ @Override
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatTextCommand(channel, command, command.getString(Command.KEY_DATA));
+ case Command.TYPE_POSITION_SINGLE:
+ return formatTextCommand(channel, command, "RG");
+ case Command.TYPE_SOS_NUMBER:
+ return formatTextCommand(channel, command, "SOS%s,%s", Command.KEY_INDEX, Command.KEY_PHONE);
+ case Command.TYPE_ALARM_SOS:
+ return formatTextCommand(channel, command, "SOSSMS,%s", Command.KEY_ENABLE);
+ case Command.TYPE_ALARM_BATTERY:
+ return formatTextCommand(channel, command, "LOWBAT,%s", Command.KEY_ENABLE);
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatTextCommand(channel, command, "RESET");
+ case Command.TYPE_POWER_OFF:
+ return formatTextCommand(channel, command, "POWEROFF");
+ case Command.TYPE_ALARM_REMOVE:
+ return formatTextCommand(channel, command, "REMOVE,%s", Command.KEY_ENABLE);
+ case Command.TYPE_SILENCE_TIME:
+ return formatTextCommand(channel, command, "SILENCETIME,%s", Command.KEY_DATA);
+ case Command.TYPE_ALARM_CLOCK:
+ return formatTextCommand(channel, command, "REMIND,%s", Command.KEY_DATA);
+ case Command.TYPE_SET_PHONEBOOK:
+ return formatTextCommand(channel, command, "PHB,%s", Command.KEY_DATA);
+ case Command.TYPE_MESSAGE:
+ return formatTextCommand(channel, command, "MESSAGE,%s", Command.KEY_MESSAGE);
+ case Command.TYPE_VOICE_MESSAGE:
+ return formatBinaryCommand(channel, command, "TK,", getBinaryData(command));
+ case Command.TYPE_POSITION_PERIODIC:
+ return formatTextCommand(channel, command, "UPLOAD,%s", Command.KEY_FREQUENCY);
+ case Command.TYPE_SET_TIMEZONE:
+ return formatTextCommand(channel, command, "LZ,%s,%s", Command.KEY_LANGUAGE, Command.KEY_TIMEZONE);
+ case Command.TYPE_SET_INDICATOR:
+ return formatTextCommand(channel, command, "FLOWER,%s", Command.KEY_DATA);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/WialonProtocol.java b/src/main/java/org/traccar/protocol/WialonProtocol.java
new file mode 100644
index 000000000..fd183dd2c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/WialonProtocol.java
@@ -0,0 +1,71 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.Context;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+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;
+public class WialonProtocol extends BaseProtocol {
+
+ public WialonProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_SEND_USSD,
+ Command.TYPE_IDENTIFICATION,
+ Command.TYPE_OUTPUT_CONTROL);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(4 * 1024));
+ boolean utf8 = Context.getConfig().getBoolean(getName() + ".utf8");
+ if (utf8) {
+ pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
+ pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
+ } else {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ }
+ pipeline.addLast(new WialonProtocolEncoder(WialonProtocol.this));
+ pipeline.addLast(new WialonProtocolDecoder(WialonProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(4 * 1024));
+ boolean utf8 = Context.getConfig().getBoolean(getName() + ".utf8");
+ if (utf8) {
+ pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
+ pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
+ } else {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ }
+ pipeline.addLast(new WialonProtocolEncoder(WialonProtocol.this));
+ pipeline.addLast(new WialonProtocolDecoder(WialonProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
index 4eb3b9b8e..8488ca2d1 100644
--- a/src/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,9 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -32,10 +34,18 @@ import java.util.regex.Pattern;
public class WialonProtocolDecoder extends BaseProtocolDecoder {
- public WialonProtocolDecoder(WialonProtocol protocol) {
+ public WialonProtocolDecoder(Protocol protocol) {
super(protocol);
}
+ private static final Pattern PATTERN_ANY = new PatternBuilder()
+ .expression("([^#]*)?") // imei
+ .text("#") // start byte
+ .expression("([^#]+)") // type
+ .text("#") // separator
+ .expression("(.*)") // message
+ .compile();
+
private static final Pattern PATTERN = new PatternBuilder()
.number("(dd)(dd)(dd);") // date (ddmmyy)
.number("(dd)(dd)(dd);") // time (hhmmss)
@@ -45,7 +55,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW]);")
.number("(d+.?d*)?;") // speed
.number("(d+.?d*)?;") // course
- .number("(?:NA|(d+.?d*));") // altitude
+ .number("(?:NA|(-?d+.?d*));") // altitude
.number("(?:NA|(d+))") // satellites
.groupBegin().text(";")
.number("(?:NA|(d+.?d*));") // hdop
@@ -57,20 +67,22 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
.groupEnd("?")
.compile();
- private void sendResponse(Channel channel, String prefix, Integer number) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, String type, Integer number) {
if (channel != null) {
- StringBuilder response = new StringBuilder(prefix);
+ StringBuilder response = new StringBuilder("#A");
+ response.append(type);
+ response.append("#");
if (number != null) {
response.append(number);
}
response.append("\r\n");
- channel.write(response.toString());
+ channel.writeAndFlush(new NetworkMessage(response.toString(), remoteAddress));
}
}
- private Position decodePosition(Channel channel, SocketAddress remoteAddress, String substring) {
+ private Position decodePosition(Channel channel, SocketAddress remoteAddress, String id, String substring) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
@@ -80,8 +92,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
@@ -134,60 +145,76 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
- if (sentence.startsWith("#L#")) {
+ Parser parser = new Parser(PATTERN_ANY, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
- String[] values = sentence.substring(3).split(";");
+ String id = parser.next();
+ String type = parser.next();
+ String data = parser.next();
- String imei = values[0].indexOf('.') >= 0 ? values[1] : values[0];
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession != null) {
- sendResponse(channel, "#AL#", 1);
- }
+ switch (type) {
- } else if (sentence.startsWith("#P#")) {
+ case "L":
+ String[] values = data.split(";");
- sendResponse(channel, "#AP#", null); // heartbeat
+ String imei = values[0].indexOf('.') >= 0 ? values[1] : values[0];
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession != null) {
+ sendResponse(channel, remoteAddress, type, 1);
+ }
+ break;
- } else if (sentence.startsWith("#SD#") || sentence.startsWith("#D#")) {
+ case "P":
+ sendResponse(channel, remoteAddress, type, null); // heartbeat
+ break;
- Position position = decodePosition(
- channel, remoteAddress, sentence.substring(sentence.indexOf('#', 1) + 1));
+ case "D":
+ case "SD":
+ Position position = decodePosition(
+ channel, remoteAddress, id, data);
- if (position != null) {
- sendResponse(channel, "#AD#", 1);
- return position;
- }
+ if (position != null) {
+ sendResponse(channel, remoteAddress, type, 1);
+ return position;
+ }
+ break;
- } else if (sentence.startsWith("#B#")) {
+ case "B":
+ String[] messages = data.split("\\|");
+ List<Position> positions = new LinkedList<>();
- String[] messages = sentence.substring(sentence.indexOf('#', 1) + 1).split("\\|");
- List<Position> positions = new LinkedList<>();
+ for (String message : messages) {
+ position = decodePosition(channel, remoteAddress, id, message);
+ if (position != null) {
+ position.set(Position.KEY_ARCHIVE, true);
+ positions.add(position);
+ }
+ }
- for (String message : messages) {
- Position position = decodePosition(channel, remoteAddress, message);
- if (position != null) {
- position.set(Position.KEY_ARCHIVE, true);
- positions.add(position);
+ sendResponse(channel, remoteAddress, type, messages.length);
+ if (!positions.isEmpty()) {
+ return positions;
}
- }
+ break;
+
+ case "M":
+ deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession != null) {
+ position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, new Date());
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, data);
+ sendResponse(channel, remoteAddress, type, 1);
+ return position;
+ }
+ break;
- sendResponse(channel, "#AB#", messages.length);
- if (!positions.isEmpty()) {
- return positions;
- }
+ default:
+ break;
- } else if (sentence.startsWith("#M#")) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession != null) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, new Date());
- position.setValid(false);
- position.set(Position.KEY_RESULT, sentence.substring(sentence.indexOf('#', 1) + 1));
- sendResponse(channel, "#AM#", 1);
- return position;
- }
}
return null;
diff --git a/src/org/traccar/protocol/WialonProtocolEncoder.java b/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
index 9c60a1356..93086bf8a 100644
--- a/src/org/traccar/protocol/WialonProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,26 +17,29 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class WialonProtocolEncoder extends StringProtocolEncoder {
+ public WialonProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
return formatCommand(command, "reboot\r\n");
case Command.TYPE_SEND_USSD:
- return formatCommand(command, "USSD:{%s}\r\n", Command.KEY_PHONE);
+ return formatCommand(command, "USSD:%s\r\n", Command.KEY_PHONE);
case Command.TYPE_IDENTIFICATION:
return formatCommand(command, "VER?\r\n");
case Command.TYPE_OUTPUT_CONTROL:
- return formatCommand(command, "L{%s}={%s}\r\n", Command.KEY_INDEX, Command.KEY_DATA);
+ return formatCommand(command, "L%s=%s\r\n", Command.KEY_INDEX, Command.KEY_DATA);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
- return null;
}
+
}
diff --git a/src/org/traccar/protocol/WondexFrameDecoder.java b/src/main/java/org/traccar/protocol/WondexFrameDecoder.java
index db65ff80f..39d83d761 100644
--- a/src/org/traccar/protocol/WondexFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/WondexFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,19 +15,21 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.StringFinder;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.NetworkMessage;
+import org.traccar.helper.BufferUtil;
-public class WondexFrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class WondexFrameDecoder extends BaseFrameDecoder {
private static final int KEEP_ALIVE_LENGTH = 8;
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < KEEP_ALIVE_LENGTH) {
return null;
@@ -36,17 +38,18 @@ public class WondexFrameDecoder extends FrameDecoder {
if (buf.getUnsignedByte(buf.readerIndex()) == 0xD0) {
// Send response
- ChannelBuffer frame = buf.readBytes(KEEP_ALIVE_LENGTH);
+ ByteBuf frame = buf.readRetainedSlice(KEEP_ALIVE_LENGTH);
if (channel != null) {
- channel.write(frame);
+ frame.retain();
+ channel.writeAndFlush(new NetworkMessage(frame, channel.remoteAddress()));
}
return frame;
} else {
- int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("\r\n"));
+ int index = BufferUtil.indexOf("\r\n", buf);
if (index != -1) {
- ChannelBuffer frame = buf.readBytes(index - buf.readerIndex());
+ ByteBuf frame = buf.readRetainedSlice(index - buf.readerIndex());
buf.skipBytes(2);
return frame;
}
diff --git a/src/main/java/org/traccar/protocol/WondexProtocol.java b/src/main/java/org/traccar/protocol/WondexProtocol.java
new file mode 100644
index 000000000..035dd9160
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/WondexProtocol.java
@@ -0,0 +1,55 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+import io.netty.handler.codec.string.StringEncoder;
+
+public class WondexProtocol extends BaseProtocol {
+
+ public WondexProtocol() {
+ setSupportedCommands(
+ Command.TYPE_GET_DEVICE_STATUS,
+ Command.TYPE_GET_MODEM_STATUS,
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_GET_VERSION,
+ Command.TYPE_IDENTIFICATION);
+ setTextCommandEncoder(new WondexProtocolEncoder(this));
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new WondexProtocolEncoder(WondexProtocol.this));
+ pipeline.addLast(new WondexProtocolDecoder(WondexProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/WondexProtocolDecoder.java b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
index e27745f38..b85ae2656 100644
--- a/src/org/traccar/protocol/WondexProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,10 +15,11 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -31,7 +32,7 @@ import java.util.regex.Pattern;
public class WondexProtocolDecoder extends BaseProtocolDecoder {
- public WondexProtocolDecoder(WondexProtocol protocol) {
+ public WondexProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -60,7 +61,7 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
if (buf.getUnsignedByte(0) == 0xD0) {
@@ -70,17 +71,17 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
return null;
} else if (buf.toString(StandardCharsets.US_ASCII).startsWith("$OK:")
|| buf.toString(StandardCharsets.US_ASCII).startsWith("$ERR:")
- || buf.toString(StandardCharsets.US_ASCII).startsWith("$MSG:")) {
+ || buf.toString(StandardCharsets.US_ASCII).startsWith("$MSG:")) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
getLastLocation(position, new Date());
position.set(Position.KEY_RESULT, buf.toString(StandardCharsets.US_ASCII));
return position;
+
} else {
Parser parser = new Parser(PATTERN, buf.toString(StandardCharsets.US_ASCII));
@@ -88,8 +89,7 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -120,6 +120,7 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, parser.next());
return position;
+
}
}
diff --git a/src/org/traccar/protocol/WondexProtocolEncoder.java b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
index 6258c1fe6..21f1ee321 100644
--- a/src/org/traccar/protocol/WondexProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -16,10 +16,15 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class WondexProtocolEncoder extends StringProtocolEncoder {
+
+ public WondexProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -27,23 +32,20 @@ public class WondexProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "$WP+REBOOT={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+REBOOT=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_GET_DEVICE_STATUS:
- return formatCommand(command, "$WP+TEST={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+TEST=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_GET_MODEM_STATUS:
- return formatCommand(command, "$WP+GSMINFO={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+GSMINFO=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_IDENTIFICATION:
- return formatCommand(command, "$WP+IMEI={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+IMEI=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "$WP+GETLOCATION={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+GETLOCATION=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_GET_VERSION:
- return formatCommand(command, "$WP+VER={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+VER=%s", Command.KEY_DEVICE_PASSWORD);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/WristbandProtocol.java b/src/main/java/org/traccar/protocol/WristbandProtocol.java
new file mode 100644
index 000000000..1e5ef2c01
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/WristbandProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+public class WristbandProtocol extends BaseProtocol {
+
+ public WristbandProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ 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
new file mode 100644
index 000000000..7f2b0af85
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/WristbandProtocolDecoder.java
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class WristbandProtocolDecoder extends BaseProtocolDecoder {
+
+ public WristbandProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(
+ Channel channel, String imei, String version, int type, String data) {
+
+ if (channel != null) {
+ String sentence = String.format("YX%s|%s|0|{F%02d#%s}\r\n", imei, version, type, data);
+ ByteBuf response = Unpooled.buffer();
+ if (type != 91) {
+ response.writeBytes(new byte[]{0x00, 0x01, 0x02});
+ response.writeShort(sentence.length());
+ }
+ response.writeCharSequence(sentence, StandardCharsets.US_ASCII);
+ if (type != 91) {
+ response.writeBytes(new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0xFC});
+ }
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("..") // header
+ .number("(d+)|") // imei
+ .number("([vV]d+.d+)|") // version
+ .number("d+|") // model
+ .text("{")
+ .number("F(d+)") // function
+ .groupBegin()
+ .text("#")
+ .expression("(.*)") // data
+ .groupEnd("?")
+ .text("}")
+ .text("\r\n")
+ .compile();
+
+ private Position decodePosition(DeviceSession deviceSession, String sentence) throws ParseException {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ String[] values = sentence.split(",");
+
+ position.setValid(true);
+ position.setLongitude(Double.parseDouble(values[0]));
+ position.setLatitude(Double.parseDouble(values[1]));
+ position.setTime(new SimpleDateFormat("yyyyMMddHHmm").parse(values[2]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[3])));
+
+ return position;
+ }
+
+ private Position decodeStatus(DeviceSession deviceSession, String sentence) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(sentence.split(",")[0]));
+
+ return position;
+ }
+
+ private Position decodeNetwork(DeviceSession deviceSession, String sentence, boolean wifi) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+ String[] fragments = sentence.split("\\|");
+
+ if (wifi) {
+ for (String item : fragments[0].split("_")) {
+ String[] values = item.split(",");
+ network.addWifiAccessPoint(WifiAccessPoint.from(values[0], Integer.parseInt(values[1])));
+ }
+ }
+
+ for (String item : fragments[wifi ? 1 : 0].split(":")) {
+ String[] values = item.split(",");
+ int lac = Integer.parseInt(values[0]);
+ int mnc = Integer.parseInt(values[1]);
+ int mcc = Integer.parseInt(values[2]);
+ int cid = Integer.parseInt(values[3]);
+ int rssi = Integer.parseInt(values[4]);
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi));
+ }
+
+ position.setNetwork(network);
+
+ return position;
+ }
+
+ private List<Position> decodeMessage(
+ Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String imei = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ String version = parser.next();
+ int type = parser.nextInt();
+
+ List<Position> positions = new LinkedList<>();
+ String data = parser.next();
+
+ switch (type) {
+ case 90:
+ sendResponse(channel, imei, version, type, getServer(channel, ','));
+ break;
+ case 91:
+ String time = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+ sendResponse(channel, imei, version, type, time + "|" + getServer(channel, ','));
+ break;
+ case 1:
+ positions.add(decodeStatus(deviceSession, data));
+ sendResponse(channel, imei, version, type, data.split(",")[1]);
+ break;
+ case 2:
+ for (String fragment : data.split("\\|")) {
+ positions.add(decodePosition(deviceSession, fragment));
+ }
+ break;
+ case 3:
+ case 4:
+ positions.add(decodeNetwork(deviceSession, data, type == 3));
+ break;
+ case 64:
+ sendResponse(channel, imei, version, type, data);
+ break;
+ default:
+ break;
+ }
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ buf.skipBytes(3); // header
+ buf.readUnsignedShort(); // length
+
+ String sentence = buf.toString(buf.readerIndex(), buf.readableBytes() - 3, StandardCharsets.US_ASCII);
+
+ buf.skipBytes(3); // footer
+
+ return decodeMessage(channel, remoteAddress, sentence);
+ }
+
+}
diff --git a/src/org/traccar/protocol/XexunFrameDecoder.java b/src/main/java/org/traccar/protocol/XexunFrameDecoder.java
index 801fb4d59..114e94061 100644
--- a/src/org/traccar/protocol/XexunFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/XexunFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,31 +15,32 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.StringFinder;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
-public class XexunFrameDecoder extends FrameDecoder {
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+
+public class XexunFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
if (buf.readableBytes() < 80) {
return null;
}
- int beginIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("GPRMC"));
+ int beginIndex = BufferUtil.indexOf("GPRMC", buf);
if (beginIndex == -1) {
- beginIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("GNRMC"));
+ beginIndex = BufferUtil.indexOf("GNRMC", buf);
if (beginIndex == -1) {
return null;
}
}
- int identifierIndex = buf.indexOf(beginIndex, buf.writerIndex(), new StringFinder("imei:"));
+ int identifierIndex = BufferUtil.indexOf("imei:", buf, beginIndex, buf.writerIndex());
if (identifierIndex == -1) {
return null;
}
@@ -51,7 +52,7 @@ public class XexunFrameDecoder extends FrameDecoder {
buf.skipBytes(beginIndex - buf.readerIndex());
- return buf.readBytes(endIndex - beginIndex + 1);
+ return buf.readRetainedSlice(endIndex - beginIndex + 1);
}
}
diff --git a/src/org/traccar/protocol/XexunProtocol.java b/src/main/java/org/traccar/protocol/XexunProtocol.java
index b90cbfaaf..401844e7b 100644
--- a/src/org/traccar/protocol/XexunProtocol.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,42 +15,35 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.Context;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.model.Command;
-import java.util.List;
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
public class XexunProtocol extends BaseProtocol {
public XexunProtocol() {
- super("xexun");
setSupportedDataCommands(
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ addServer(new TrackerServer(false, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
boolean full = Context.getConfig().getBoolean(getName() + ".extended");
if (full) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); // tracker bug \n\r
+ pipeline.addLast(new LineBasedFrameDecoder(1024)); // tracker bug \n\r
} else {
- pipeline.addLast("frameDecoder", new XexunFrameDecoder());
+ pipeline.addLast(new XexunFrameDecoder());
}
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new XexunProtocolEncoder());
- pipeline.addLast("objectDecoder", new XexunProtocolDecoder(XexunProtocol.this, full));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new XexunProtocolEncoder(XexunProtocol.this));
+ pipeline.addLast(new XexunProtocolDecoder(XexunProtocol.this, full));
}
});
}
diff --git a/src/org/traccar/protocol/XexunProtocolDecoder.java b/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java
index bb4b4f48c..73d386477 100644
--- a/src/org/traccar/protocol/XexunProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -30,7 +31,7 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
private final boolean full;
- public XexunProtocolDecoder(XexunProtocol protocol, boolean full) {
+ public XexunProtocolDecoder(Protocol protocol, boolean full) {
super(protocol);
this.full = full;
}
@@ -76,6 +77,7 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_IGNITION, false);
break;
case "help me!":
+ case "help me":
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
break;
case "low battery":
@@ -106,8 +108,7 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
if (full) {
position.set("serial", parser.next());
diff --git a/src/org/traccar/protocol/XexunProtocolEncoder.java b/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
index cdf3ac6f7..4f2707c2a 100644
--- a/src/org/traccar/protocol/XexunProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -16,11 +16,15 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
import org.traccar.model.Command;
+import org.traccar.Protocol;
public class XexunProtocolEncoder extends StringProtocolEncoder {
+ public XexunProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
@Override
protected Object encodeCommand(Command command) {
@@ -28,15 +32,12 @@ public class XexunProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "powercar{%s} 11", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "powercar%s 11", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "powercar{%s} 00", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "powercar%s 00", Command.KEY_DEVICE_PASSWORD);
default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
+ return null;
}
-
- return null;
}
}
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocol.java b/src/main/java/org/traccar/protocol/XirgoProtocol.java
new file mode 100644
index 000000000..1be5b6c4b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/XirgoProtocol.java
@@ -0,0 +1,53 @@
+/*
+ * 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.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+
+public class XirgoProtocol extends BaseProtocol {
+
+ public XirgoProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_OUTPUT_CONTROL);
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "##"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new XirgoProtocolEncoder(XirgoProtocol.this));
+ pipeline.addLast(new XirgoProtocolDecoder(XirgoProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new XirgoProtocolEncoder(XirgoProtocol.this));
+ pipeline.addLast(new XirgoProtocolDecoder(XirgoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
new file mode 100644
index 000000000..4d0cc314b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/XirgoProtocolDecoder.java
@@ -0,0 +1,371 @@
+/*
+ * 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 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+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.Matcher;
+import java.util.regex.Pattern;
+
+public class XirgoProtocolDecoder extends BaseProtocolDecoder {
+
+ private Boolean newFormat;
+ private String form;
+
+ public XirgoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ form = Context.getConfig().getString(getProtocolName() + ".form");
+ }
+
+ public void setForm(String form) {
+ this.form = form;
+ }
+
+ private static final Pattern PATTERN_OLD = new PatternBuilder()
+ .text("$$")
+ .number("(d+),") // imei
+ .number("(d+),") // event
+ .number("(dddd)/(dd)/(dd),") // date (yyyy/mm/dd)
+ .number("(dd):(dd):(dd),") // time (hh:mm:ss)
+ .number("(-?d+.?d*),") // latitude
+ .number("(-?d+.?d*),") // longitude
+ .number("(-?d+.?d*),") // altitude
+ .number("(d+.?d*),") // speed
+ .number("(d+.?d*),") // course
+ .number("(d+),") // satellites
+ .number("(d+.?d*),") // hdop
+ .number("(d+.d+),") // battery
+ .number("(d+),") // gsm
+ .number("(d+.?d*),") // odometer
+ .number("(d+),") // gps
+ .any()
+ .compile();
+
+ private static final Pattern PATTERN_NEW = new PatternBuilder()
+ .text("$$")
+ .number("(d+),") // imei
+ .number("(d+),") // event
+ .number("(dddd)/(dd)/(dd),") // date (yyyy/mm/dd)
+ .number("(dd):(dd):(dd),") // time (hh:mm:ss)
+ .number("(-?d+.?d*),") // latitude
+ .number("(-?d+.?d*),") // longitude
+ .number("(-?d+.?d*),") // altitude
+ .number("(d+.?d*),") // speed
+ .number("d+.?d*,") // acceleration
+ .number("d+.?d*,") // deceleration
+ .number("d+,")
+ .number("(d+.?d*),") // course
+ .number("(d+),") // satellites
+ .number("(d+.?d*),") // hdop
+ .number("(d+.?d*),") // odometer
+ .number("(d+.?d*),") // fuel consumption
+ .number("(d+.d+),") // battery
+ .number("(d+),") // gsm
+ .number("(d+),") // gps
+ .groupBegin()
+ .number("d,") // reset mode
+ .expression("([01])") // input 1
+ .expression("([01])") // input 1
+ .expression("([01])") // input 1
+ .expression("([01]),") // output 1
+ .number("(d+.?d*),") // adc 1
+ .number("(d+.?d*),") // fuel level
+ .number("d+,") // engine load
+ .number("(d+),") // engine hours
+ .number("(d+),") // oil pressure
+ .number("(d+),") // oil level
+ .number("(-?d+),") // oil temperature
+ .number("(d+),") // coolant pressure
+ .number("(d+),") // coolant level
+ .number("(-?d+)") // coolant temperature
+ .groupEnd("?")
+ .any()
+ .compile();
+
+ private void decodeEvent(Position position, int event) {
+
+ position.set(Position.KEY_EVENT, event);
+
+ switch (event) {
+ case 4001:
+ case 4003:
+ case 6011:
+ case 6013:
+ position.set(Position.KEY_IGNITION, true);
+ break;
+ case 4002:
+ case 4004:
+ case 6012:
+ case 6014:
+ position.set(Position.KEY_IGNITION, false);
+ break;
+ case 4005:
+ position.set(Position.KEY_CHARGE, false);
+ break;
+ case 6002:
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ case 6006:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 6007:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 6008:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ break;
+ case 6009:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case 6010:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
+ break;
+ case 6016:
+ position.set(Position.KEY_ALARM, Position.ALARM_IDLE);
+ break;
+ case 6017:
+ position.set(Position.KEY_ALARM, Position.ALARM_TOW);
+ break;
+ case 6030:
+ case 6071:
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 6031:
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 6032:
+ position.set(Position.KEY_ALARM, Position.ALARM_PARKING);
+ break;
+ case 6090:
+ position.set(Position.KEY_ALARM, Position.ALARM_REMOVING);
+ break;
+ case 6091:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (channel instanceof NioDatagramChannel) {
+ Matcher matcher = Pattern.compile("\\$\\$\\d+,(\\d+),.*,(\\d+)##").matcher(sentence);
+ if (matcher.matches()) {
+ String response = "!UDP_ACK," + matcher.group(1) + "," + matcher.group(2);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ if (form != null) {
+ return decodeCustom(channel, remoteAddress, sentence);
+ } else {
+ return decodeFixed(channel, remoteAddress, sentence);
+ }
+ }
+
+
+ private Object decodeCustom(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ String[] keys = form.split(",");
+ String[] values = sentence.replace("$$", "").replace("##", "").split(",");
+
+ if (values.length < keys.length) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ DateBuilder dateBuilder = new DateBuilder();
+
+ for (int i = 0; i < keys.length; i++) {
+ switch (keys[i]) {
+ case "UID":
+ case "IM":
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[i]);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+ break;
+ case "EV":
+ decodeEvent(position, Integer.parseInt(values[i]));
+ break;
+ case "D":
+ String[] date = values[i].split("/");
+ dateBuilder.setMonth(Integer.parseInt(date[0]));
+ dateBuilder.setDay(Integer.parseInt(date[1]));
+ dateBuilder.setYear(Integer.parseInt(date[2]));
+ break;
+ case "T":
+ String[] time = values[i].split(":");
+ dateBuilder.setHour(Integer.parseInt(time[0]));
+ dateBuilder.setMinute(Integer.parseInt(time[1]));
+ dateBuilder.setSecond(Integer.parseInt(time[2]));
+ break;
+ case "LT":
+ position.setLatitude(Double.parseDouble(values[i]));
+ break;
+ case "LN":
+ position.setLongitude(Double.parseDouble(values[i]));
+ break;
+ case "AL":
+ position.setAltitude(Integer.parseInt(values[i]));
+ break;
+ case "GSPT":
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[i])));
+ break;
+ case "HD":
+ if (values[i].contains(".")) {
+ position.setCourse(Double.parseDouble(values[i]));
+ } else {
+ position.setCourse(Integer.parseInt(values[i]) * 0.1);
+ }
+ break;
+ case "SV":
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[i]));
+ break;
+ case "BV":
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[i]));
+ break;
+ case "CQ":
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[i]));
+ break;
+ case "MI":
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(values[i]));
+ break;
+ case "GS":
+ position.setValid(Integer.parseInt(values[i]) == 3);
+ break;
+ case "SI":
+ position.set(Position.KEY_ICCID, values[i]);
+ break;
+ case "IG":
+ int ignition = Integer.parseInt(values[i]);
+ if (ignition > 0) {
+ position.set(Position.KEY_IGNITION, ignition == 1);
+ }
+ break;
+ case "OT":
+ position.set(Position.KEY_OUTPUT, Integer.parseInt(values[i]));
+ break;
+ default:
+ break;
+ }
+ }
+
+ position.setTime(dateBuilder.getDate());
+
+ return position.getDeviceId() > 0 ? position : null;
+ }
+
+ private Object decodeFixed(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser;
+ if (newFormat == null) {
+ parser = new Parser(PATTERN_NEW, sentence);
+ if (parser.matches()) {
+ newFormat = true;
+ } else {
+ parser = new Parser(PATTERN_OLD, sentence);
+ if (parser.matches()) {
+ newFormat = false;
+ } else {
+ return null;
+ }
+ }
+ } else {
+ if (newFormat) {
+ parser = new Parser(PATTERN_NEW, sentence);
+ } else {
+ parser = new Parser(PATTERN_OLD, sentence);
+ }
+ if (!parser.matches()) {
+ return null;
+ }
+ }
+
+ Position position = new Position(getProtocolName());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeEvent(position, parser.nextInt());
+
+ position.setTime(parser.nextDateTime());
+
+ position.setLatitude(parser.nextDouble(0));
+ position.setLongitude(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+ position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+
+ if (newFormat) {
+ position.set(Position.KEY_ODOMETER, UnitsConverter.metersFromMiles(parser.nextDouble(0)));
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
+ }
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble(0));
+ position.set(Position.KEY_RSSI, parser.nextDouble());
+
+ if (!newFormat) {
+ position.set(Position.KEY_ODOMETER, UnitsConverter.metersFromMiles(parser.nextDouble(0)));
+ }
+
+ position.setValid(parser.nextInt(0) == 1);
+
+ if (newFormat && parser.hasNext(13)) {
+ position.set(Position.PREFIX_IN + 1, parser.nextInt());
+ position.set(Position.PREFIX_IN + 2, parser.nextInt());
+ position.set(Position.PREFIX_IN + 3, parser.nextInt());
+ position.set(Position.PREFIX_OUT + 1, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
+ position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(parser.nextInt()));
+ position.set("oilPressure", parser.nextInt());
+ position.set("oilLevel", parser.nextInt());
+ position.set("oilTemp", parser.nextInt());
+ position.set("coolantPressure", parser.nextInt());
+ position.set("coolantLevel", parser.nextInt());
+ position.set("coolantTemp", parser.nextInt());
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/XirgoProtocolEncoder.java b/src/main/java/org/traccar/protocol/XirgoProtocolEncoder.java
new file mode 100644
index 000000000..aa85e9e0e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/XirgoProtocolEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * 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.protocol;
+
+import org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class XirgoProtocolEncoder extends StringProtocolEncoder {
+
+ public XirgoProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_OUTPUT_CONTROL:
+ return String.format("+XT:7005,%d,1", command.getInteger(Command.KEY_DATA) + 1);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Xrb28Protocol.java b/src/main/java/org/traccar/protocol/Xrb28Protocol.java
new file mode 100644
index 000000000..5d8af418b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xrb28Protocol.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 - 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 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.model.Command;
+
+import java.nio.charset.StandardCharsets;
+
+public class Xrb28Protocol extends BaseProtocol {
+
+ public Xrb28Protocol() {
+ 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()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder(StandardCharsets.ISO_8859_1));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new Xrb28ProtocolEncoder(Xrb28Protocol.this));
+ pipeline.addLast(new Xrb28ProtocolDecoder(Xrb28Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
new file mode 100644
index 000000000..69e5b7372
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Command;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
+
+ private String pendingCommand;
+
+ public void setPendingCommand(String pendingCommand) {
+ this.pendingCommand = pendingCommand;
+ }
+
+ public Xrb28ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("*")
+ .expression("....,")
+ .expression("..,") // vendor
+ .number("d{15},") // imei
+ .expression("..,") // type
+ .number("[01],") // reserved
+ .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+),") // satellites
+ .number("(d+.d+),") // hdop
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(-?d+.?d*),") // altitude
+ .expression(".,") // height unit
+ .expression(".#") // mode
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(9, 24));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ String type = sentence.substring(25, 27);
+ if (channel != null) {
+ if (type.matches("L0|L1|W0|E1")) {
+ channel.write(new NetworkMessage(
+ "\u00ff\u00ff*SCOS" + sentence.substring(5, 27) + "#\n",
+ remoteAddress));
+ } else if (type.equals("R0") && pendingCommand != null) {
+ String command = pendingCommand.equals(Command.TYPE_ALARM_ARM) ? "L1," : "L0,";
+ channel.write(new NetworkMessage(
+ "\u00ff\u00ff*SCOS" + sentence.substring(5, 25) + command + sentence.substring(30) + "\n",
+ remoteAddress));
+ pendingCommand = null;
+ }
+ }
+
+ if (!type.startsWith("D")) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ String payload = sentence.substring(25, sentence.length() - 1);
+
+ int index = 0;
+ String[] values = payload.substring(3).split(",");
+
+ switch (type) {
+ case "Q0":
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.01);
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[index++]));
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
+ break;
+ case "H0":
+ position.set(Position.KEY_BLOCKED, Integer.parseInt(values[index++]) > 0);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.01);
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[index++]));
+ break;
+ case "W0":
+ switch (Integer.parseInt(values[index++])) {
+ case 1:
+ position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
+ break;
+ case 2:
+ position.set(Position.KEY_ALARM, Position.ALARM_FALL_DOWN);
+ break;
+ case 3:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ default:
+ break;
+ }
+ break;
+ case "E0":
+ position.set(Position.KEY_ALARM, Position.ALARM_FAULT);
+ position.set("error", Integer.parseInt(values[index++]));
+ break;
+ case "S1":
+ position.set(Position.KEY_EVENT, Integer.parseInt(values[index++]));
+ break;
+ case "R0":
+ case "L0":
+ case "L1":
+ case "S4":
+ case "S5":
+ case "S6":
+ case "S7":
+ case "V0":
+ case "G0":
+ case "K0":
+ case "I0":
+ case "M0":
+ position.set(Position.KEY_RESULT, payload);
+ break;
+ default:
+ break;
+ }
+
+ return !position.getAttributes().isEmpty() ? position : null;
+
+ } else {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ 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.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.setAltitude(parser.nextDouble());
+
+ return position;
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolEncoder.java
new file mode 100644
index 000000000..3e69af329
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolEncoder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 - 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.Protocol;
+
+public class Xrb28ProtocolEncoder extends BaseProtocolEncoder {
+
+ public Xrb28ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private String formatCommand(Command command, String content) {
+ return String.format("\u00ff\u00ff*SCOS,OM,%s,%s#\n", getUniqueId(command.getDeviceId()), content);
+ }
+
+ @Override
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, command.getString(Command.KEY_DATA));
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, "D0");
+ case Command.TYPE_POSITION_PERIODIC:
+ return formatCommand(command, "D1," + command.getInteger(Command.KEY_FREQUENCY));
+ case Command.TYPE_ENGINE_STOP:
+ case Command.TYPE_ALARM_DISARM:
+ if (channel != null) {
+ Xrb28ProtocolDecoder decoder = channel.pipeline().get(Xrb28ProtocolDecoder.class);
+ if (decoder != null) {
+ decoder.setPendingCommand(command.getType());
+ }
+ }
+ return formatCommand(command, "R0,0,20,1234," + System.currentTimeMillis() / 1000);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Xt013Protocol.java b/src/main/java/org/traccar/protocol/Xt013Protocol.java
new file mode 100644
index 000000000..ebb3c123f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xt013Protocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import io.netty.handler.codec.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+
+public class Xt013Protocol extends BaseProtocol {
+
+ public Xt013Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new Xt013ProtocolDecoder(Xt013Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Xt013ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java
index f1214fdb4..f49fb9563 100644
--- a/src/org/traccar/protocol/Xt013ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xt013ProtocolDecoder.java
@@ -15,9 +15,10 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -28,7 +29,7 @@ import java.util.regex.Pattern;
public class Xt013ProtocolDecoder extends BaseProtocolDecoder {
- public Xt013ProtocolDecoder(Xt013Protocol protocol) {
+ public Xt013ProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -65,8 +66,7 @@ public class Xt013ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
diff --git a/src/org/traccar/protocol/Xt2400Protocol.java b/src/main/java/org/traccar/protocol/Xt2400Protocol.java
index 0c5e9cd4c..9427876c8 100644
--- a/src/org/traccar/protocol/Xt2400Protocol.java
+++ b/src/main/java/org/traccar/protocol/Xt2400Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -15,25 +15,17 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.util.List;
-
public class Xt2400Protocol extends BaseProtocol {
public Xt2400Protocol() {
- super("xt2400");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ addServer(new TrackerServer(true, getName()) {
@Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new Xt2400ProtocolDecoder(Xt2400Protocol.this));
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Xt2400ProtocolDecoder(Xt2400Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
index 15e8558be..c132f194b 100644
--- a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,15 +15,16 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
+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.Protocol;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
-import javax.xml.bind.DatatypeConverter;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
@@ -34,7 +35,7 @@ import java.util.regex.Pattern;
public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
- public Xt2400ProtocolDecoder(Xt2400Protocol protocol) {
+ public Xt2400ProtocolDecoder(Protocol protocol) {
super(protocol);
String config = Context.getConfig().getString(getProtocolName() + ".config");
@@ -58,7 +59,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
0x26, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x57, 0x58, 0x59, 0x5a, 0x6b, 0x6f, 0x7A,
0x7B, 0x7C, 0x7d, 0x7E, 0x7F, 0x80, 0x81, 0x82,
- 0x83, 0x84, 0x85, 0x86
+ 0x83, 0x84, 0x85, 0x86, 0xc8
};
int[] l4 = {
0x03, 0x06, 0x07, 0x08, 0x0e, 0x0f, 0x10, 0x11,
@@ -79,12 +80,13 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
TAG_LENGTH_MAP.put(i, 4);
}
TAG_LENGTH_MAP.put(0x95, 24);
+ TAG_LENGTH_MAP.put(0xD0, 21);
}
private static int getTagLength(int tag) {
Integer length = TAG_LENGTH_MAP.get(tag);
if (length == null) {
- throw new IllegalArgumentException("Unknown tag: " + tag);
+ throw new IllegalArgumentException(String.format("Unknown tag: 0x%02X", tag));
}
return length;
}
@@ -92,10 +94,10 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
private Map<Short, byte[]> formats = new HashMap<>();
public void setConfig(String configString) {
- Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+\\] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)");
+ Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)");
Matcher matcher = pattern.matcher(configString);
while (matcher.find()) {
- formats.put(Short.parseShort(matcher.group(1), 16), DatatypeConverter.parseHexBinary(matcher.group(2)));
+ formats.put(Short.parseShort(matcher.group(1), 16), DataConverter.parseHex(matcher.group(2)));
}
}
@@ -103,7 +105,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- ChannelBuffer buf = (ChannelBuffer) msg;
+ ByteBuf buf = (ByteBuf) msg;
byte[] format = null;
if (formats.size() > 1) {
@@ -116,11 +118,11 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
- for (byte tag : format) {
- switch ((int) tag) {
+ for (byte b : format) {
+ int tag = b & 0xFF;
+ switch (tag) {
case 0x03:
DeviceSession deviceSession = getDeviceSession(
channel, remoteAddress, String.valueOf(buf.readUnsignedInt()));
@@ -175,10 +177,18 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
break;
case 0x65:
- position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_VIN, buf.readSlice(17).toString(StandardCharsets.US_ASCII));
+ break;
+ case 0x6C:
+ buf.readUnsignedByte(); // mil
+ int ecuCount = buf.readUnsignedByte();
+ for (int i = 0; i < ecuCount; i++) {
+ buf.readUnsignedByte(); // ecu id
+ buf.skipBytes(buf.readUnsignedByte() * 6);
+ }
break;
case 0x73:
- position.set(Position.KEY_VERSION_FW, buf.readBytes(16).toString(StandardCharsets.US_ASCII).trim());
+ position.set(Position.KEY_VERSION_FW, buf.readSlice(16).toString(StandardCharsets.US_ASCII).trim());
break;
default:
buf.skipBytes(getTagLength(tag));
diff --git a/src/main/java/org/traccar/protocol/YwtProtocol.java b/src/main/java/org/traccar/protocol/YwtProtocol.java
new file mode 100644
index 000000000..c525b75cf
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/YwtProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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;
+
+public class YwtProtocol extends BaseProtocol {
+
+ public YwtProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new YwtProtocolDecoder(YwtProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/YwtProtocolDecoder.java b/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java
index 6a98ab9e7..bf5a23fa7 100644
--- a/src/org/traccar/protocol/YwtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/YwtProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2014 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -15,11 +15,14 @@
*/
package org.traccar.protocol;
-import org.jboss.netty.channel.Channel;
+import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+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;
@@ -27,7 +30,7 @@ import java.util.regex.Pattern;
public class YwtProtocolDecoder extends BaseProtocolDecoder {
- public YwtProtocolDecoder(YwtProtocol protocol) {
+ public YwtProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -67,7 +70,7 @@ public class YwtProtocolDecoder extends BaseProtocolDecoder {
end = sentence.length();
}
- channel.write("%AT+SN=" + sentence.substring(start, end));
+ channel.writeAndFlush(new NetworkMessage("%AT+SN=" + sentence.substring(start, end), remoteAddress));
return null;
}
@@ -76,8 +79,7 @@ public class YwtProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ Position position = new Position(getProtocolName());
String type = parser.next();
@@ -92,11 +94,11 @@ public class YwtProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
position.setAltitude(parser.nextDouble(0));
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
- int satellites = parser.nextInt(0);
- position.setValid(satellites >= 3);
+ int satellites = parser.nextInt();
+ position.setValid(satellites != 0);
position.set(Position.KEY_SATELLITES, satellites);
String reportId = parser.next();
@@ -105,7 +107,7 @@ public class YwtProtocolDecoder extends BaseProtocolDecoder {
// Send response
if ((type.equals("KP") || type.equals("EP")) && channel != null) {
- channel.write("%AT+" + type + "=" + reportId + "\r\n");
+ channel.writeAndFlush(new NetworkMessage("%AT+" + type + "=" + reportId + "\r\n", remoteAddress));
}
return position;
diff --git a/src/org/traccar/reports/Events.java b/src/main/java/org/traccar/reports/Events.java
index a13aeeeb4..66d9e708d 100644
--- a/src/org/traccar/reports/Events.java
+++ b/src/main/java/org/traccar/reports/Events.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ * 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.
@@ -33,6 +33,7 @@ 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 {
@@ -51,7 +52,10 @@ public final class Events {
for (Event event : events) {
if (all || types.contains(event.getType())) {
long geofenceId = event.getGeofenceId();
- if (geofenceId == 0 || Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) {
+ long maintenanceId = event.getMaintenanceId();
+ if ((geofenceId == 0 || Context.getGeofenceManager().checkItemPermission(userId, geofenceId))
+ && (maintenanceId == 0
+ || Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId))) {
result.add(event);
}
}
@@ -67,6 +71,7 @@ public final class Events {
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);
@@ -75,15 +80,25 @@ public final class Events {
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 = (Geofence) Context.getGeofenceManager().getById(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();
@@ -109,6 +124,7 @@ public final class Events {
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/org/traccar/reports/ReportUtils.java b/src/main/java/org/traccar/reports/ReportUtils.java
index f6f386e99..3a631e0d9 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/main/java/org/traccar/reports/ReportUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,7 +26,9 @@ import org.jxls.transform.Transformer;
import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.TransformerFactory;
import org.traccar.Context;
-import org.traccar.events.MotionEventHandler;
+import org.traccar.database.DeviceManager;
+import org.traccar.database.IdentityManager;
+import org.traccar.handler.events.MotionEventHandler;
import org.traccar.model.DeviceState;
import org.traccar.model.Driver;
import org.traccar.model.Event;
@@ -151,8 +153,10 @@ public final class ReportUtils {
return jxlsContext;
}
- public static void processTemplateWithSheets(InputStream templateStream, OutputStream targetStream,
+ 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) {
@@ -166,6 +170,7 @@ public final class ReportUtils {
private static TripReport calculateTrip(
ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
+
Position startTrip = positions.get(startIndex);
Position endTrip = positions.get(endIndex);
@@ -190,13 +195,23 @@ public final class ReportUtils {
trip.setStartLat(startTrip.getLatitude());
trip.setStartLon(startTrip.getLongitude());
trip.setStartTime(startTrip.getFixTime());
- trip.setStartAddress(startTrip.getAddress());
+ String startAddress = startTrip.getAddress();
+ if (startAddress == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ 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());
- trip.setEndAddress(endTrip.getAddress());
+ String endAddress = endTrip.getAddress();
+ if (endAddress == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ endAddress = Context.getGeocoder().getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null);
+ }
+ trip.setEndAddress(endAddress);
trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer));
trip.setDuration(tripDuration);
@@ -207,10 +222,22 @@ public final class ReportUtils {
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) {
+ private static StopReport calculateStop(
+ ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
+
Position startStop = positions.get(startIndex);
Position endStop = positions.get(endIndex);
@@ -224,7 +251,13 @@ public final class ReportUtils {
stop.setLatitude(startStop.getLatitude());
stop.setLongitude(startStop.getLongitude());
stop.setStartTime(startStop.getFixTime());
- stop.setAddress(startStop.getAddress());
+ String address = startStop.getAddress();
+ if (address == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude(), null);
+ }
+ stop.setAddress(address);
+
stop.setEndTime(endStop.getFixTime());
long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime();
@@ -232,24 +265,42 @@ public final class ReportUtils {
stop.setSpentFuel(calculateFuel(startStop, endStop));
long engineHours = 0;
- for (int i = startIndex + 1; i <= endIndex; i++) {
- if (positions.get(i).getBoolean(Position.KEY_IGNITION)
- && positions.get(i - 1).getBoolean(Position.KEY_IGNITION)) {
- engineHours += positions.get(i).getFixTime().getTime() - positions.get(i - 1).getFixTime().getTime();
+ if (startStop.getAttributes().containsKey(Position.KEY_HOURS)
+ && endStop.getAttributes().containsKey(Position.KEY_HOURS)) {
+ engineHours = endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS);
+ } else if (Context.getConfig().getBoolean("processing.engineHours.enable")) {
+ // Temporary fallback for old data, to be removed in May 2019
+ for (int i = startIndex + 1; i <= endIndex; i++) {
+ if (positions.get(i).getBoolean(Position.KEY_IGNITION)
+ && positions.get(i - 1).getBoolean(Position.KEY_IGNITION)) {
+ engineHours += positions.get(i).getFixTime().getTime()
+ - positions.get(i - 1).getFixTime().getTime();
+ }
}
}
stop.setEngineHours(engineHours);
+ 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) {
+ 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);
+ return (T) calculateStop(positions, startIndex, endIndex, ignoreOdometer);
}
}
@@ -273,14 +324,17 @@ public final class ReportUtils {
}
}
- public static <T extends BaseReport> Collection<T> detectTripsAndStops(Collection<Position> positionCollection,
+ 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 != null && !positions.isEmpty()) {
+ if (!positions.isEmpty()) {
boolean trips = reportClass.equals(TripReport.class);
- MotionEventHandler motionHandler = new MotionEventHandler(tripsConfig);
+ MotionEventHandler motionHandler = new MotionEventHandler(identityManager, deviceManager, tripsConfig);
DeviceState deviceState = new DeviceState();
deviceState.setMotionState(isMoving(positions, 0, tripsConfig));
int startEventIndex = trips == deviceState.getMotionState() ? 0 : -1;
@@ -317,6 +371,8 @@ public final class ReportUtils {
ignoreOdometer, reportClass));
}
}
+
return result;
}
+
}
diff --git a/src/org/traccar/reports/Route.java b/src/main/java/org/traccar/reports/Route.java
index 6adb00aae..6adb00aae 100644
--- a/src/org/traccar/reports/Route.java
+++ b/src/main/java/org/traccar/reports/Route.java
diff --git a/src/org/traccar/reports/Stops.java b/src/main/java/org/traccar/reports/Stops.java
index 14b3a2437..2036b0641 100644
--- a/src/org/traccar/reports/Stops.java
+++ b/src/main/java/org/traccar/reports/Stops.java
@@ -28,6 +28,9 @@ 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;
@@ -40,13 +43,14 @@ public final class Stops {
private static Collection<StopReport> detectStops(long deviceId, Date from, Date to) throws SQLException {
boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true);
- Collection<StopReport> result = ReportUtils.detectTripsAndStops(
- Context.getDataManager().getPositions(deviceId, from, to),
- Context.getTripsConfig(), ignoreOdometer, StopReport.class);
+ IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class);
+ DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class);
- return result;
+ return ReportUtils.detectTripsAndStops(
+ identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to),
+ Context.getTripsConfig(), ignoreOdometer, StopReport.class);
}
public static Collection<StopReport> getObjects(
diff --git a/src/org/traccar/reports/Summary.java b/src/main/java/org/traccar/reports/Summary.java
index 366e40421..6d179a873 100644
--- a/src/org/traccar/reports/Summary.java
+++ b/src/main/java/org/traccar/reports/Summary.java
@@ -44,12 +44,15 @@ public final class Summary {
Position firstPosition = null;
Position previousPosition = null;
double speedSum = 0;
+ boolean engineHoursEnabled = Context.getConfig().getBoolean("processing.engineHours.enable");
for (Position position : positions) {
if (firstPosition == null) {
firstPosition = position;
}
- if (previousPosition != null && position.getBoolean(Position.KEY_IGNITION)
+ if (engineHoursEnabled && previousPosition != null
+ && position.getBoolean(Position.KEY_IGNITION)
&& previousPosition.getBoolean(Position.KEY_IGNITION)) {
+ // Temporary fallback for old data, to be removed in May 2019
result.addEngineHours(position.getFixTime().getTime()
- previousPosition.getFixTime().getTime());
}
@@ -58,10 +61,28 @@ public final class Summary {
result.setMaxSpeed(position.getSpeed());
}
boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true);
result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition, !ignoreOdometer));
result.setAverageSpeed(speedSum / positions.size());
result.setSpentFuel(ReportUtils.calculateFuel(firstPosition, previousPosition));
+
+ if (engineHoursEnabled
+ && firstPosition.getAttributes().containsKey(Position.KEY_HOURS)
+ && previousPosition.getAttributes().containsKey(Position.KEY_HOURS)) {
+ result.setEngineHours(
+ previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS));
+ }
+
+ if (!ignoreOdometer
+ && firstPosition.getDouble(Position.KEY_ODOMETER) != 0
+ && previousPosition.getDouble(Position.KEY_ODOMETER) != 0) {
+ result.setStartOdometer(firstPosition.getDouble(Position.KEY_ODOMETER));
+ result.setEndOdometer(previousPosition.getDouble(Position.KEY_ODOMETER));
+ } else {
+ result.setStartOdometer(firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
+ result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+
}
return result;
}
diff --git a/src/org/traccar/reports/Trips.java b/src/main/java/org/traccar/reports/Trips.java
index 696defa94..7c0cd6921 100644
--- a/src/org/traccar/reports/Trips.java
+++ b/src/main/java/org/traccar/reports/Trips.java
@@ -27,6 +27,9 @@ 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;
@@ -39,13 +42,14 @@ public final class Trips {
private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, false, true);
- Collection<TripReport> result = ReportUtils.detectTripsAndStops(
- Context.getDataManager().getPositions(deviceId, from, to),
- Context.getTripsConfig(), ignoreOdometer, TripReport.class);
+ IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class);
+ DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class);
- return result;
+ 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,
diff --git a/src/org/traccar/reports/model/BaseReport.java b/src/main/java/org/traccar/reports/model/BaseReport.java
index 941e2757f..9f2d1188c 100644
--- a/src/org/traccar/reports/model/BaseReport.java
+++ b/src/main/java/org/traccar/reports/model/BaseReport.java
@@ -84,4 +84,23 @@ public class BaseReport {
this.spentFuel = spentFuel;
}
+ private double startOdometer;
+
+ public double getStartOdometer() {
+ return startOdometer;
+ }
+
+ public void setStartOdometer(double startOdometer) {
+ this.startOdometer = startOdometer;
+ }
+ private double endOdometer;
+
+ public double getEndOdometer() {
+ return endOdometer;
+ }
+
+ public void setEndOdometer(double endOdometer) {
+ this.endOdometer = endOdometer;
+ }
+
}
diff --git a/src/org/traccar/reports/model/DeviceReport.java b/src/main/java/org/traccar/reports/model/DeviceReport.java
index 932753d15..932753d15 100644
--- a/src/org/traccar/reports/model/DeviceReport.java
+++ b/src/main/java/org/traccar/reports/model/DeviceReport.java
diff --git a/src/org/traccar/reports/model/StopReport.java b/src/main/java/org/traccar/reports/model/StopReport.java
index 6b2e86299..245292b63 100644
--- a/src/org/traccar/reports/model/StopReport.java
+++ b/src/main/java/org/traccar/reports/model/StopReport.java
@@ -53,37 +53,21 @@ public class StopReport extends BaseReport {
private Date startTime;
public Date getStartTime() {
- if (startTime != null) {
- return new Date(startTime.getTime());
- } else {
- return null;
- }
+ return startTime;
}
public void setStartTime(Date startTime) {
- if (startTime != null) {
- this.startTime = new Date(startTime.getTime());
- } else {
- this.startTime = null;
- }
+ this.startTime = startTime;
}
private Date endTime;
public Date getEndTime() {
- if (endTime != null) {
- return new Date(endTime.getTime());
- } else {
- return null;
- }
+ return endTime;
}
public void setEndTime(Date endTime) {
- if (endTime != null) {
- this.endTime = new Date(endTime.getTime());
- } else {
- this.endTime = null;
- }
+ this.endTime = endTime;
}
private String address;
diff --git a/src/org/traccar/reports/model/SummaryReport.java b/src/main/java/org/traccar/reports/model/SummaryReport.java
index 6f9e9459f..6f9e9459f 100644
--- a/src/org/traccar/reports/model/SummaryReport.java
+++ b/src/main/java/org/traccar/reports/model/SummaryReport.java
diff --git a/src/org/traccar/reports/model/TripReport.java b/src/main/java/org/traccar/reports/model/TripReport.java
index 42a4240b7..3140f3019 100644
--- a/src/org/traccar/reports/model/TripReport.java
+++ b/src/main/java/org/traccar/reports/model/TripReport.java
@@ -83,19 +83,11 @@ public class TripReport extends BaseReport {
private Date startTime;
public Date getStartTime() {
- if (startTime != null) {
- return new Date(startTime.getTime());
- } else {
- return null;
- }
+ return startTime;
}
public void setStartTime(Date startTime) {
- if (startTime != null) {
- this.startTime = new Date(startTime.getTime());
- } else {
- this.startTime = null;
- }
+ this.startTime = startTime;
}
private String startAddress;
@@ -111,19 +103,11 @@ public class TripReport extends BaseReport {
private Date endTime;
public Date getEndTime() {
- if (endTime != null) {
- return new Date(endTime.getTime());
- } else {
- return null;
- }
+ return endTime;
}
public void setEndTime(Date endTime) {
- if (endTime != null) {
- this.endTime = new Date(endTime.getTime());
- } else {
- this.endTime = null;
- }
+ this.endTime = endTime;
}
private String endAddress;
diff --git a/src/org/traccar/reports/model/TripsConfig.java b/src/main/java/org/traccar/reports/model/TripsConfig.java
index 0f0c615d3..0f0c615d3 100644
--- a/src/org/traccar/reports/model/TripsConfig.java
+++ b/src/main/java/org/traccar/reports/model/TripsConfig.java
diff --git a/src/main/java/org/traccar/sms/HttpSmsClient.java b/src/main/java/org/traccar/sms/HttpSmsClient.java
new file mode 100644
index 000000000..8e2b67bf7
--- /dev/null
+++ b/src/main/java/org/traccar/sms/HttpSmsClient.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.sms;
+
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.api.SecurityRequestFilter;
+import org.traccar.helper.DataConverter;
+import org.traccar.notification.MessageException;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+public class HttpSmsClient implements SmsManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HttpSmsClient.class);
+
+ private String url;
+ private String authorizationHeader;
+ private String authorization;
+ private String template;
+ private boolean encode;
+ private MediaType mediaType;
+
+ public HttpSmsClient() {
+ url = Context.getConfig().getString("sms.http.url");
+ authorizationHeader = Context.getConfig().getString("sms.http.authorizationHeader",
+ SecurityRequestFilter.AUTHORIZATION_HEADER);
+ authorization = Context.getConfig().getString("sms.http.authorization");
+ if (authorization == null) {
+ String user = Context.getConfig().getString("sms.http.user");
+ String password = Context.getConfig().getString("sms.http.password");
+ authorization = "Basic "
+ + DataConverter.printBase64((user + ":" + password).getBytes(StandardCharsets.UTF_8));
+ }
+ template = Context.getConfig().getString("sms.http.template").trim();
+ if (template.charAt(0) == '{' || template.charAt(0) == '[') {
+ encode = false;
+ mediaType = MediaType.APPLICATION_JSON_TYPE;
+ } else {
+ encode = true;
+ mediaType = MediaType.APPLICATION_FORM_URLENCODED_TYPE;
+ }
+ }
+
+ private String prepareValue(String value) throws UnsupportedEncodingException {
+ return encode ? URLEncoder.encode(value, StandardCharsets.UTF_8.name()) : value;
+ }
+
+ private String preparePayload(String destAddress, String message) {
+ try {
+ return template
+ .replace("{phone}", prepareValue(destAddress))
+ .replace("{message}", prepareValue(message.trim()));
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Invocation.Builder getRequestBuilder() {
+ return Context.getClient().target(url).request()
+ .header(authorizationHeader, authorization);
+ }
+
+ @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);
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/sms/SmsManager.java b/src/main/java/org/traccar/sms/SmsManager.java
new file mode 100644
index 000000000..3b0cbda7f
--- /dev/null
+++ b/src/main/java/org/traccar/sms/SmsManager.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.sms;
+
+import org.traccar.notification.MessageException;
+
+public interface SmsManager {
+
+ void sendMessageSync(
+ String destAddress, String message, boolean command) throws InterruptedException, MessageException;
+
+ void sendMessageAsync(
+ String destAddress, String message, boolean command);
+
+}
diff --git a/src/org/traccar/smpp/ClientSmppSessionHandler.java b/src/main/java/org/traccar/sms/smpp/ClientSmppSessionHandler.java
index 77f75273e..6b9de3107 100644
--- a/src/org/traccar/smpp/ClientSmppSessionHandler.java
+++ b/src/main/java/org/traccar/sms/smpp/ClientSmppSessionHandler.java
@@ -14,10 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.smpp;
+package org.traccar.sms.smpp;
-import org.traccar.events.TextMessageEventHandler;
-import org.traccar.helper.Log;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.cloudhopper.commons.charset.CharsetUtil;
import com.cloudhopper.smpp.SmppConstants;
@@ -25,9 +25,12 @@ import com.cloudhopper.smpp.impl.DefaultSmppSessionHandler;
import com.cloudhopper.smpp.pdu.DeliverSm;
import com.cloudhopper.smpp.pdu.PduRequest;
import com.cloudhopper.smpp.pdu.PduResponse;
+import com.cloudhopper.smpp.util.SmppUtil;
public class ClientSmppSessionHandler extends DefaultSmppSessionHandler {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ClientSmppSessionHandler.class);
+
private SmppClient smppClient;
public ClientSmppSessionHandler(SmppClient smppClient) {
@@ -36,30 +39,33 @@ public class ClientSmppSessionHandler extends DefaultSmppSessionHandler {
@Override
public void firePduRequestExpired(PduRequest pduRequest) {
- Log.warning("PDU request expired: " + pduRequest);
+ LOGGER.warn("PDU request expired: " + pduRequest);
}
@Override
public PduResponse firePduRequestReceived(PduRequest request) {
- PduResponse response = null;
+ PduResponse response;
try {
if (request instanceof DeliverSm) {
- if (request.getOptionalParameters() != null) {
- Log.debug("SMS Message Delivered: "
- + request.getOptionalParameter(SmppConstants.TAG_RECEIPTED_MSG_ID).getValueAsString()
- + ", State: "
- + request.getOptionalParameter(SmppConstants.TAG_MSG_STATE).getValueAsByte());
+ String sourceAddress = ((DeliverSm) request).getSourceAddress().getAddress();
+ String message = CharsetUtil.decode(((DeliverSm) request).getShortMessage(),
+ smppClient.mapDataCodingToCharset(((DeliverSm) request).getDataCoding()));
+ LOGGER.info("SMS Message Received: " + message.trim() + ", Source Address: " + sourceAddress);
+
+ boolean isDeliveryReceipt;
+ if (smppClient.getDetectDlrByOpts()) {
+ isDeliveryReceipt = request.getOptionalParameters() != null;
} else {
- String sourceAddress = ((DeliverSm) request).getSourceAddress().getAddress();
- String message = CharsetUtil.decode(((DeliverSm) request).getShortMessage(),
- smppClient.mapDataCodingToCharset(((DeliverSm) request).getDataCoding()));
- Log.debug("SMS Message Received: " + message.trim() + ", Source Address: " + sourceAddress);
+ isDeliveryReceipt = SmppUtil.isMessageTypeAnyDeliveryReceipt(((DeliverSm) request).getEsmClass());
+ }
+
+ if (!isDeliveryReceipt) {
TextMessageEventHandler.handleTextMessage(sourceAddress, message);
}
}
response = request.createResponse();
- } catch (Throwable error) {
- Log.warning(error);
+ } catch (Exception error) {
+ LOGGER.warn("SMS receiving error", error);
response = request.createResponse();
response.setResultMessage(error.getMessage());
response.setCommandStatus(SmppConstants.STATUS_UNKNOWNERR);
@@ -69,7 +75,8 @@ public class ClientSmppSessionHandler extends DefaultSmppSessionHandler {
@Override
public void fireChannelUnexpectedlyClosed() {
- Log.warning("SMPP session channel unexpectedly closed");
+ LOGGER.warn("SMPP session channel unexpectedly closed");
smppClient.scheduleReconnect();
}
+
}
diff --git a/src/org/traccar/smpp/EnquireLinkTask.java b/src/main/java/org/traccar/sms/smpp/EnquireLinkTask.java
index affb712b3..7086709d7 100644
--- a/src/org/traccar/smpp/EnquireLinkTask.java
+++ b/src/main/java/org/traccar/sms/smpp/EnquireLinkTask.java
@@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.smpp;
+package org.traccar.sms.smpp;
-import org.traccar.helper.Log;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.cloudhopper.smpp.SmppSession;
import com.cloudhopper.smpp.pdu.EnquireLink;
@@ -27,6 +28,8 @@ import com.cloudhopper.smpp.type.UnrecoverablePduException;
public class EnquireLinkTask implements Runnable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EnquireLinkTask.class);
+
private SmppClient smppClient;
private Integer enquireLinkTimeout;
@@ -43,13 +46,13 @@ public class EnquireLinkTask implements Runnable {
smppSession.enquireLink(new EnquireLink(), enquireLinkTimeout);
} catch (SmppTimeoutException | SmppChannelException
| RecoverablePduException | UnrecoverablePduException error) {
- Log.warning("Enquire link failed, executing reconnect: ", error);
+ LOGGER.warn("Enquire link failed, executing reconnect: ", error);
smppClient.scheduleReconnect();
} catch (InterruptedException error) {
- Log.info("Enquire link interrupted, probably killed by reconnecting");
+ LOGGER.info("Enquire link interrupted, probably killed by reconnecting");
}
} else {
- Log.warning("Enquire link running while session is not connected");
+ LOGGER.warn("Enquire link running while session is not connected");
}
}
diff --git a/src/org/traccar/smpp/ReconnectionTask.java b/src/main/java/org/traccar/sms/smpp/ReconnectionTask.java
index c9d9173ae..c009de8e7 100644
--- a/src/org/traccar/smpp/ReconnectionTask.java
+++ b/src/main/java/org/traccar/sms/smpp/ReconnectionTask.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.smpp;
+package org.traccar.sms.smpp;
public class ReconnectionTask implements Runnable {
@@ -28,4 +28,5 @@ public class ReconnectionTask implements Runnable {
public void run() {
smppClient.reconnect();
}
+
}
diff --git a/src/org/traccar/smpp/SmppClient.java b/src/main/java/org/traccar/sms/smpp/SmppClient.java
index 122bbaea1..874253d36 100644
--- a/src/org/traccar/smpp/SmppClient.java
+++ b/src/main/java/org/traccar/sms/smpp/SmppClient.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
- * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ * Copyright 2017 - 2018 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.
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.smpp;
+package org.traccar.sms.smpp;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -23,8 +23,11 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.Context;
-import org.traccar.helper.Log;
+import org.traccar.notification.MessageException;
+import org.traccar.sms.SmsManager;
import com.cloudhopper.commons.charset.CharsetUtil;
import com.cloudhopper.smpp.SmppBindType;
@@ -35,19 +38,22 @@ import com.cloudhopper.smpp.impl.DefaultSmppClient;
import com.cloudhopper.smpp.impl.DefaultSmppSessionHandler;
import com.cloudhopper.smpp.pdu.SubmitSm;
import com.cloudhopper.smpp.pdu.SubmitSmResp;
+import com.cloudhopper.smpp.tlv.Tlv;
import com.cloudhopper.smpp.type.Address;
import com.cloudhopper.smpp.type.RecoverablePduException;
import com.cloudhopper.smpp.type.SmppChannelException;
import com.cloudhopper.smpp.type.SmppTimeoutException;
import com.cloudhopper.smpp.type.UnrecoverablePduException;
-public class SmppClient {
+public class SmppClient implements SmsManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SmppClient.class);
private SmppSessionConfiguration sessionConfig = new SmppSessionConfiguration();
private SmppSession smppSession;
private DefaultSmppSessionHandler sessionHandler = new ClientSmppSessionHandler(this);
private ExecutorService executorService = Executors.newCachedThreadPool();
- private DefaultSmppClient clientBootstrap = new DefaultSmppClient(executorService, 1);
+ private DefaultSmppClient clientBootstrap = new DefaultSmppClient();
private ScheduledExecutorService enquireLinkExecutor;
private ScheduledFuture<?> enquireLinkTask;
@@ -61,6 +67,8 @@ public class SmppClient {
private String sourceAddress;
private String commandSourceAddress;
private int submitTimeout;
+ private boolean requestDlr;
+ private boolean detectDlrByOpts;
private String notificationsCharsetName;
private byte notificationsDataCoding;
private String commandsCharsetName;
@@ -82,6 +90,7 @@ public class SmppClient {
sessionConfig.setHost(Context.getConfig().getString("sms.smpp.host", "localhost"));
sessionConfig.setPort(Context.getConfig().getInteger("sms.smpp.port", 2775));
sessionConfig.setSystemId(Context.getConfig().getString("sms.smpp.username", "user"));
+ sessionConfig.setSystemType(Context.getConfig().getString("sms.smpp.systemType", null));
sessionConfig.setPassword(Context.getConfig().getString("sms.smpp.password", "password"));
sessionConfig.getLoggingOptions().setLogBytes(false);
sessionConfig.getLoggingOptions().setLogPdu(Context.getConfig().getBoolean("sms.smpp.logPdu"));
@@ -90,6 +99,9 @@ public class SmppClient {
commandSourceAddress = Context.getConfig().getString("sms.smpp.commandSourceAddress", sourceAddress);
submitTimeout = Context.getConfig().getInteger("sms.smpp.submitTimeout", 10000);
+ requestDlr = Context.getConfig().getBoolean("sms.smpp.requestDlr");
+ detectDlrByOpts = Context.getConfig().getBoolean("sms.smpp.detectDlrByOpts");
+
notificationsCharsetName = Context.getConfig().getString("sms.smpp.notificationsCharset",
CharsetUtil.NAME_UCS_2);
notificationsDataCoding = (byte) Context.getConfig().getInteger("sms.smpp.notificationsDataCoding",
@@ -149,16 +161,20 @@ public class SmppClient {
}
}
+ public boolean getDetectDlrByOpts() {
+ return detectDlrByOpts;
+ }
+
protected synchronized void reconnect() {
try {
disconnect();
smppSession = clientBootstrap.bind(sessionConfig, sessionHandler);
stopReconnectionkTask();
runEnquireLinkTask();
- Log.info("SMPP session connected");
+ LOGGER.info("SMPP session connected");
} catch (SmppTimeoutException | SmppChannelException
| UnrecoverablePduException | InterruptedException error) {
- Log.warning("Unable to connect to SMPP server: ", error);
+ LOGGER.warn("Unable to connect to SMPP server: ", error);
}
}
@@ -195,46 +211,62 @@ public class SmppClient {
private void destroySession() {
if (smppSession != null) {
- Log.debug("Cleaning up SMPP session... ");
+ LOGGER.info("Cleaning up SMPP session... ");
smppSession.destroy();
smppSession = null;
}
}
+ @Override
public synchronized void sendMessageSync(String destAddress, String message, boolean command)
- throws RecoverablePduException, UnrecoverablePduException, SmppTimeoutException, SmppChannelException,
- InterruptedException, IllegalStateException {
+ throws MessageException, InterruptedException, IllegalStateException {
if (getSession() != null && getSession().isBound()) {
- SubmitSm submit = new SubmitSm();
- byte[] textBytes;
- textBytes = CharsetUtil.encode(message, command ? commandsCharsetName : notificationsCharsetName);
- submit.setDataCoding(command ? commandsDataCoding : notificationsDataCoding);
- submit.setShortMessage(textBytes);
- submit.setSourceAddress(command ? new Address(commandSourceTon, commandSourceNpi, commandSourceAddress)
- : new Address(sourceTon, sourceNpi, sourceAddress));
- submit.setDestAddress(new Address(destTon, destNpi, destAddress));
- SubmitSmResp submitResponce = getSession().submit(submit, submitTimeout);
- if (submitResponce.getCommandStatus() == SmppConstants.STATUS_OK) {
- Log.debug("SMS submitted, message id: " + submitResponce.getMessageId());
- } else {
- throw new IllegalStateException(submitResponce.getResultMessage());
+ try {
+ SubmitSm submit = new SubmitSm();
+ byte[] textBytes;
+ textBytes = CharsetUtil.encode(message, command ? commandsCharsetName : notificationsCharsetName);
+ submit.setDataCoding(command ? commandsDataCoding : notificationsDataCoding);
+ if (requestDlr) {
+ submit.setRegisteredDelivery(SmppConstants.REGISTERED_DELIVERY_SMSC_RECEIPT_REQUESTED);
+ }
+
+ if (textBytes != null && textBytes.length > 255) {
+ submit.addOptionalParameter(new Tlv(SmppConstants.TAG_MESSAGE_PAYLOAD, textBytes,
+ "message_payload"));
+ } else {
+ submit.setShortMessage(textBytes);
+ }
+
+ submit.setSourceAddress(command ? new Address(commandSourceTon, commandSourceNpi, commandSourceAddress)
+ : new Address(sourceTon, sourceNpi, sourceAddress));
+ submit.setDestAddress(new Address(destTon, destNpi, destAddress));
+ SubmitSmResp submitResponce = getSession().submit(submit, submitTimeout);
+ if (submitResponce.getCommandStatus() == SmppConstants.STATUS_OK) {
+ LOGGER.info("SMS submitted, message id: " + submitResponce.getMessageId());
+ } else {
+ throw new IllegalStateException(submitResponce.getResultMessage());
+ }
+ } catch (SmppChannelException | RecoverablePduException
+ | SmppTimeoutException | UnrecoverablePduException error) {
+ throw new MessageException(error);
}
} else {
- throw new SmppChannelException("SMPP session is not connected");
+ throw new MessageException(new SmppChannelException("SMPP session is not connected"));
}
}
+ @Override
public void sendMessageAsync(final String destAddress, final String message, final boolean command) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
sendMessageSync(destAddress, message, command);
- } catch (InterruptedException | RecoverablePduException | UnrecoverablePduException
- | SmppTimeoutException | SmppChannelException | IllegalStateException error) {
- Log.warning(error);
+ } catch (MessageException | InterruptedException | IllegalStateException error) {
+ LOGGER.warn("SMS sending error", error);
}
}
});
}
+
}
diff --git a/src/org/traccar/events/TextMessageEventHandler.java b/src/main/java/org/traccar/sms/smpp/TextMessageEventHandler.java
index be4a193a7..37c29972d 100644
--- a/src/org/traccar/events/TextMessageEventHandler.java
+++ b/src/main/java/org/traccar/sms/smpp/TextMessageEventHandler.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.events;
+package org.traccar.sms.smpp;
import org.traccar.Context;
import org.traccar.model.Device;
diff --git a/src/org/traccar/web/ConsoleServlet.java b/src/main/java/org/traccar/web/ConsoleServlet.java
index 9b3d8d54b..2b38a935a 100644
--- a/src/org/traccar/web/ConsoleServlet.java
+++ b/src/main/java/org/traccar/web/ConsoleServlet.java
@@ -17,8 +17,9 @@ package org.traccar.web;
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.helper.Log;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -26,6 +27,8 @@ import java.lang.reflect.Method;
public class ConsoleServlet extends WebServlet {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleServlet.class);
+
@Override
public void init() {
super.init();
@@ -51,7 +54,7 @@ public class ConsoleServlet extends WebServlet {
method.invoke(server, true);
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
- Log.warning(e);
+ LOGGER.warn("Console reflection error", e);
}
}
diff --git a/src/org/traccar/web/CsvBuilder.java b/src/main/java/org/traccar/web/CsvBuilder.java
index 31b389873..3fe7e408f 100644
--- a/src/org/traccar/web/CsvBuilder.java
+++ b/src/main/java/org/traccar/web/CsvBuilder.java
@@ -28,17 +28,17 @@ import java.util.SortedSet;
import java.util.TreeSet;
import com.fasterxml.jackson.core.JsonProcessingException;
-import org.joda.time.DateTime;
-import org.joda.time.format.DateTimeFormatter;
-import org.joda.time.format.ISODateTimeFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.traccar.Context;
-import org.traccar.helper.Log;
+import org.traccar.helper.DateUtil;
public class CsvBuilder {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CsvBuilder.class);
+
private static final String LINE_ENDING = "\r\n";
private static final String SEPARATOR = ";";
- private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateTime();
private StringBuilder builder = new StringBuilder();
@@ -51,7 +51,7 @@ public class CsvBuilder {
private SortedSet<Method> getSortedMethods(Object object) {
Method[] methodArray = object.getClass().getMethods();
- SortedSet<Method> methods = new TreeSet<Method>(new Comparator<Method>() {
+ SortedSet<Method> methods = new TreeSet<>(new Comparator<Method>() {
@Override
public int compare(Method m1, Method m2) {
if (m1.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
@@ -75,23 +75,23 @@ public class CsvBuilder {
if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
try {
if (method.getReturnType().equals(boolean.class)) {
- builder.append((Boolean) method.invoke(object));
+ builder.append(method.invoke(object));
addSeparator();
} else if (method.getReturnType().equals(int.class)) {
- builder.append((Integer) method.invoke(object));
+ builder.append(method.invoke(object));
addSeparator();
} else if (method.getReturnType().equals(long.class)) {
- builder.append((Long) method.invoke(object));
+ builder.append(method.invoke(object));
addSeparator();
} else if (method.getReturnType().equals(double.class)) {
- builder.append((Double) method.invoke(object));
+ builder.append(method.invoke(object));
addSeparator();
} else if (method.getReturnType().equals(String.class)) {
builder.append((String) method.invoke(object));
addSeparator();
} else if (method.getReturnType().equals(Date.class)) {
Date value = (Date) method.invoke(object);
- builder.append(DATE_FORMAT.print(new DateTime(value)));
+ builder.append(DateUtil.formatDate(value));
addSeparator();
} else if (method.getReturnType().equals(Map.class)) {
Map value = (Map) method.invoke(object);
@@ -103,12 +103,12 @@ public class CsvBuilder {
builder.append(map);
addSeparator();
} catch (JsonProcessingException e) {
- Log.warning(e);
+ LOGGER.warn("Map JSON formatting error", e);
}
}
}
} catch (IllegalAccessException | InvocationTargetException error) {
- Log.warning(error);
+ LOGGER.warn("Reflection invocation error", error);
}
}
}
@@ -160,4 +160,5 @@ public class CsvBuilder {
public String build() {
return builder.toString();
}
+
}
diff --git a/src/org/traccar/web/GpxBuilder.java b/src/main/java/org/traccar/web/GpxBuilder.java
index bac7182dc..638d100e5 100644
--- a/src/org/traccar/web/GpxBuilder.java
+++ b/src/main/java/org/traccar/web/GpxBuilder.java
@@ -18,9 +18,7 @@ package org.traccar.web;
import java.util.Collection;
-import org.joda.time.DateTime;
-import org.joda.time.format.DateTimeFormatter;
-import org.joda.time.format.ISODateTimeFormat;
+import org.traccar.helper.DateUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -41,8 +39,6 @@ public class GpxBuilder {
+ "</trkpt>%n";
private static final String FOOTER = "</trkseg></trk></gpx>";
- private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateTime();
-
public GpxBuilder() {
builder.append(HEADER);
builder.append("<trkseg>\n");
@@ -55,7 +51,7 @@ public class GpxBuilder {
public void addPosition(Position position) {
builder.append(String.format(POINT, position.getLatitude(), position.getLongitude(),
- DATE_FORMAT.print(new DateTime(position.getFixTime())), position.getAltitude(),
+ DateUtil.formatDate(position.getFixTime()), position.getAltitude(),
position.getCourse(), UnitsConverter.mpsFromKnots(position.getSpeed())));
}
diff --git a/src/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java
index e145ff554..70fef4ed3 100644
--- a/src/org/traccar/web/WebServer.java
+++ b/src/main/java/org/traccar/web/WebServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 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.
@@ -20,45 +20,42 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerList;
-import org.eclipse.jetty.server.handler.ResourceHandler;
-import org.eclipse.jetty.server.session.HashSessionManager;
+import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.webapp.WebAppContext;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
-import org.traccar.Config;
-import org.traccar.Context;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.config.Config;
import org.traccar.api.AsyncSocketServlet;
import org.traccar.api.CorsResponseFilter;
+import org.traccar.api.MediaFilter;
import org.traccar.api.ObjectMapperProvider;
import org.traccar.api.ResourceErrorHandler;
import org.traccar.api.SecurityRequestFilter;
import org.traccar.api.resource.ServerResource;
-import org.traccar.helper.Log;
-import javax.naming.InitialContext;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
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.util.EnumSet;
public class WebServer {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WebServer.class);
+
private Server server;
- private final Config config;
- private final DataSource dataSource;
- private final HandlerList handlers = new HandlerList();
- private final SessionManager sessionManager;
- private void initServer() {
+ private void initServer(Config config) {
String address = config.getString("web.address");
int port = config.getInteger("web.port", 8082);
@@ -69,44 +66,42 @@ public class WebServer {
}
}
- public WebServer(Config config, DataSource dataSource) {
- this.config = config;
- this.dataSource = dataSource;
+ public WebServer(Config config) {
+
+ initServer(config);
+
+ ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
- sessionManager = new HashSessionManager();
int sessionTimeout = config.getInteger("web.sessionTimeout");
- if (sessionTimeout != 0) {
- sessionManager.setMaxInactiveInterval(sessionTimeout);
+ if (sessionTimeout > 0) {
+ servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout);
}
- initServer();
- initApi();
+ initApi(config, servletHandler);
+
if (config.getBoolean("web.console")) {
- initConsole();
- }
- switch (config.getString("web.type", "new")) {
- case "old":
- initOldWebApp();
- break;
- default:
- initWebApp();
- break;
+ servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/console/*");
}
- initClientProxy();
- server.setHandler(handlers);
- server.addBean(new ErrorHandler() {
+ initWebApp(config, 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>");
}
- }, false);
+ });
+
+ HandlerList handlers = new HandlerList();
+ initClientProxy(config, handlers);
+ handlers.addHandler(servletHandler);
+ server.setHandler(handlers);
}
- private void initClientProxy() {
- int port = Context.getConfig().getInteger("osmand.port");
+ private void initClientProxy(Config config, HandlerList handlers) {
+ int port = config.getInteger("osmand.port");
if (port != 0) {
ServletContextHandler servletHandler = new ServletContextHandler() {
@Override
@@ -125,66 +120,45 @@ public class WebServer {
}
}
- private void initWebApp() {
- ResourceHandler resourceHandler = new ResourceHandler();
- resourceHandler.setResourceBase(config.getString("web.path"));
+ private void initWebApp(Config config, ServletContextHandler servletHandler) {
+ ServletHolder servletHolder = new ServletHolder(DefaultServlet.class);
+ servletHolder.setInitParameter("resourceBase", new File(config.getString("web.path")).getAbsolutePath());
if (config.getBoolean("web.debug")) {
- resourceHandler.setWelcomeFiles(new String[] {"debug.html", "index.html"});
- resourceHandler.setMinMemoryMappedContentLength(-1); // avoid locking files on Windows
+ servletHandler.setWelcomeFiles(new String[] {"debug.html", "index.html"});
} else {
String cache = config.getString("web.cacheControl");
if (cache != null && !cache.isEmpty()) {
- resourceHandler.setCacheControl(cache);
+ servletHolder.setInitParameter("cacheControl", cache);
}
- resourceHandler.setWelcomeFiles(new String[] {"release.html", "index.html"});
- }
- handlers.addHandler(resourceHandler);
- }
-
- private void initOldWebApp() {
- try {
- javax.naming.Context context = new InitialContext();
- context.bind("java:/DefaultDS", dataSource);
- } catch (Exception error) {
- Log.warning(error);
+ servletHandler.setWelcomeFiles(new String[] {"release.html", "index.html"});
}
-
- WebAppContext app = new WebAppContext();
- app.setContextPath("/");
- app.getSessionHandler().setSessionManager(sessionManager);
- app.setWar(config.getString("web.application"));
- handlers.addHandler(app);
+ servletHandler.addServlet(servletHolder, "/*");
}
- private void initApi() {
- ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
- servletHandler.setContextPath("/api");
- servletHandler.getSessionHandler().setSessionManager(sessionManager);
+ private void initApi(Config config, ServletContextHandler servletHandler) {
+ servletHandler.addServlet(new ServletHolder(new AsyncSocketServlet()), "/api/socket");
- servletHandler.addServlet(new ServletHolder(new AsyncSocketServlet()), "/socket");
+ if (config.hasKey("media.path")) {
+ ServletHolder servletHolder = new ServletHolder(DefaultServlet.class);
+ servletHolder.setInitParameter("resourceBase", new File(config.getString("media.path")).getAbsolutePath());
+ servletHolder.setInitParameter("dirAllowed", config.getString("media.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);
resourceConfig.registerClasses(SecurityRequestFilter.class, CorsResponseFilter.class);
resourceConfig.packages(ServerResource.class.getPackage().getName());
-
- servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
-
- handlers.addHandler(servletHandler);
- }
-
- private void initConsole() {
- ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
- servletHandler.setContextPath("/console");
- servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/*");
- handlers.addHandler(servletHandler);
+ servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/api/*");
}
public void start() {
try {
server.start();
} catch (Exception error) {
- Log.warning(error);
+ LOGGER.warn("Web server start failed", error);
}
}
@@ -192,7 +166,7 @@ public class WebServer {
try {
server.stop();
} catch (Exception error) {
- Log.warning(error);
+ LOGGER.warn("Web server stop failed", error);
}
}
diff --git a/src/main/proto/OmnicommMessage.proto b/src/main/proto/OmnicommMessage.proto
new file mode 100644
index 000000000..a3b5b6138
--- /dev/null
+++ b/src/main/proto/OmnicommMessage.proto
@@ -0,0 +1,464 @@
+syntax = "proto2";
+
+package org.traccar.protobuf;
+
+message OmnicommMessage {
+
+ repeated uint32 mID = 1 [packed=true]; // идентификатор события (возможно несколько событий в одном сообщении.)
+
+ optional group General = 2 { // Основные параметры
+ optional uint32 Time = 1;//[4] Время/дата события (в OmnicommTime)
+ optional uint32 IDFAS = 2;//[4] Идентификатор бортового оборудования
+ optional bytes IDDrv = 3;//[8] ID iButton (8 байт)
+ optional uint32 FLG = 4;//[1] Общие флаги состояния
+ /* Битовая маска:
+ 0 бит = Положение ключа зажигания
+ 0 – зажигание выключено,
+ 1 – зажигание включено.
+ 1 бит = Наличие связи GSM
+ 0 – Нет связи,
+ 1 – Есть связь.
+ 2 бит = Корректность данных GPS
+ 0 – Данные некорректны,
+ 1 – Данные корректны.
+ 3 бит = Нахождение в роуминге
+ 0 – В домашней сети,
+ 1 – В роуминге.
+ 4 бит = Состояние питания
+ 0 – Внешнее питание,
+ 1 – Резервное питание.
+ 5 бит = Тревожная кнопка (текущее состояние кнопки)
+ 0 – Кнопка отжата,
+ 1 – Кнопка нажата.
+ 6 бит = Вскрытие устройства
+ 0 – Устройство закрыто,
+ 1 – Устройство открыто.
+ 7 бит = Состояние дискретного выхода
+ 0 – Выключен,
+ 1 – Включен.
+ 8 бит = Кнопка голосового вызова
+ 0 - Кнопка отжата
+ 1 - Кнопка нажата
+ 9 бит = глушение GPS
+ 0 – Выключен,
+ 1 – Включен.
+ 10 бит = глушение GSM
+ 0 – Выключен,
+ 1 – Включен.
+ 11 бит = неисправность аккумулятора
+ 0 – Нет,
+ 1 – Да.
+ */
+ optional uint32 Mileage = 5;//[4] Пробег (1LSB = 0.1 метр)
+ optional uint32 VImp = 6;//[2] Скорость по датчику (1LSB = 1км/ч)
+ optional uint32 TImp = 7;//[2] Обороты по датчику (1LSB = 1об./мин.)
+ optional uint32 Uboard = 8;//[4] Напряжение питания (1LSB = 0.1В)
+ optional uint32 BatLife =9;//[4] Уровень заряда аккумулятора (1LSB = 1%)
+ optional sint32 SumAcc = 10;//[4] Корень суммы квадратов ускорений осей x,y,z (1LSB = 0.01g)
+ optional bytes Phone = 11;//[6] Номер вызывающего или вызываемого абонента
+ optional sint32 AmtrX = 12;//[4] Ускорение по оси X
+ optional sint32 AmtrY = 13;//[4] Ускорение по оси Y
+ optional sint32 AmtrZ = 14;//[4] Ускорение по оси Z
+ optional bytes TachoCardID = 15;//[16] ID карты тахографа (16 байт)
+ optional uint32 AccelStatus = 16;//[1] Статус калибровки акселерометра: 0 - не используется, 1 - калибруется, 2 - откалиброван, 3 - Ошибка определения осей
+ optional uint32 HoursKoef = 17;// Моточасы с использование коэффициента
+ optional uint32 GsmSignalQuality = 18;// Уровень сигнала GSM, усл.ед.: 0 - нет сигнала, 1 - плохой, 2 - слабый, 3 - удовлетворительный, 4 - хороший, 5 - отличный
+ optional uint32 WifiSignalQuality = 19;// Уровень сигнала Wi-Fi, усл.ед? 0 - нет сигнала, 1 - плохой, 2 - слабый, 3 - удовлетворительный, 4 - хороший, 5 - отличный
+ }
+
+ optional group Photo = 4 { // Описание блока передачи части изображения
+ optional uint32 POSBLK=1;//[4] Позиция текущего блока (в байтах от начала фотографии)
+ optional uint32 SZPHOTO=2; //[4] Размер всей фотографии (в байтах)
+ optional uint32 SIZEBLK=3;//[4] Размер текущего блока фотографии (в байтах)
+ optional bytes IMGDAT=4;//[2048] Буфер фотографии (данные фотографии)
+ optional uint32 IDPH=5;//[4] Идентификатор фотографии
+ optional uint32 IMGSTAT=6;//[1] Статус изображения, см. Таблицу "Коды состояния фотокамеры".
+ }
+
+ optional group NAV = 5 { // Навигация
+ required sint32 LAT=1;//[4] Широта (1LSB = 0,0000001гр.)
+ required sint32 LON=2;//[4] Долгота (1LSB = 0,0000001гр.)
+ required uint32 GPSVel=3;//[2] Скорость по GPS (1LSB = 0.1км/ч)
+ required uint32 GPSDir=4;//[2] Направление (1LSB = 1гр.)
+ required uint32 GPSNSat=5;//[1] Количество спутников
+ required sint32 GPSAlt=6;//[2] Высота над уровнем моря (1LSB = 0.1м)
+ optional uint32 GPSTime = 7;//[4] Время/дата события по GPS (в OmnicommTime)
+ }
+
+ optional group UniDt = 6 { // Универсальные входы
+ optional sint32 UniVal0 = 1;//[4] Данные универсального входа 1
+ optional sint32 UniVal1 = 2;//[4] Данные универсального входа 2
+ optional sint32 UniVal2 = 3;//[4] Данные универсального входа 3
+ optional sint32 UniVal3 = 4;//[4] Данные универсального входа 4
+ optional sint32 UniVal4 = 5;//[4] Данные универсального входа 5
+ optional sint32 UniVal5 = 6;//[4] Данные универсального входа 6
+ }
+
+ optional group CanDt_J1939 = 7 { // Данные шины CAN (протокол J1939)
+ optional uint32 SPN70 = 70;//[1] Cостояние парковочного тормоза
+ /* Возможные значения
+ 00 - Parking brake not set
+ 01 - Parking brake set
+ 10 - Error
+ 11 - Not available
+ */
+ optional uint32 SPN91 = 91;//[1] Пположение педали акселерометра (0.4%)
+ /* Ошибки:
+ 254 = Error
+ 255 = Not available
+ */
+ optional uint32 SPN96 = 96; // Уровень топлива 1 бак.(0.4 %) Если есть SPN38, то первый бак, если нет, общий уровень
+ /* Ошибки:
+ 251=Error
+ 252= Not available
+ */
+
+ optional uint32 SPN100 = 100;//[1] Давление масла двигателя (1 LSB=4 kPa)
+ /* Ошибки:
+ 254 = Error
+ 255 = Not available
+ */
+ optional uint32 SPN110 = 110;//[1] температура ОЖ двигателя. Cмещение -40 (диапазон данных от -40 до 210°C)
+ /* Ошибки:
+ 254 = Error
+ 255 = Not available
+ */
+ optional uint32 SPN174 = 174;//[1] температура топлива. Cмещение -40 (диапазон данных от -40 до 210°C)
+ /* Ошибки:
+ 254=Error
+ 255= Not available
+ */
+ optional uint32 SPN175 = 175;//[2] температура масла двигателя. Cмещение - 273 (диапазон данных от -273 до 1735 deg C)
+ optional uint32 SPN182 = 182;//[4] суточный расход топлива (0.5 L)
+ /* Ошибки:
+ 4 261 412 864 .. 4 278 190 079 =Error
+ 4 278 190 080 .. 4 294 967 294 = Not available
+ */
+ optional uint32 SPN184 = 184;//[2] мгновенная экономичность (1/512 km/L)
+ /* Ошибки:
+ 65 024 .. 65 279 =Error
+ 65 280 .. 65 535 = Not available
+ */
+ optional uint32 SPN190 = 190;//[2] обороты двигателя (0.125 rpm)
+ /* Ошибки:
+ 65 024 .. 65 279 =Error
+ 65 280 .. 65 535 = Not available
+ */
+ optional uint32 SPN244 = 244;//[4] суточный пробег (0.125 km)
+ /* Ошибки:
+ 4 261 412 864 .. 4 278 190 079 =Error
+ 4 278 190 080 .. 4 294 967 294 = Not available
+ */
+ optional uint32 SPN245 = 245;//[4] общий пробег (0.125 km)
+ /* Ошибки:
+ 4 261 412 864 .. 4 278 190 079 =Error
+ 4 278 190 080 .. 4 294 967 294 = Not available
+ */
+ optional uint32 SPN247 = 247;//[4] общее время работы двигателя (0.05 hr)
+ /* Ошибки:
+ 4 261 412 864 .. 4 278 190 079 =Error
+ 4 278 190 080 .. 4 294 967 294 = Not available
+ */
+ optional uint32 SPN250 = 250;//[4] общий расход топлива за все время (0.5 L)
+ /* Ошибки:
+ 4 261 412 864 .. 4 278 190 079 =Error
+ 4 278 190 080 .. 4 294 967 294 = Not available
+ */
+ optional uint32 SPN521 = 521;//[1] положение педали рабочего тормоза (0.4 %)
+ /* Ошибки:
+ 254=Error
+ 255= Not available
+ */
+ optional uint32 SPN522 = 522;//[1] положение педали сцепления (0.4 %)
+ /* Ошибки:
+ 254=Error
+ 255= Not available
+ */
+ optional uint32 SPN527 = 527;//[1] состояние круиз-контроля
+ /* Возможные значения:
+ 0 = Off/Disabled
+ 1 = Hold
+ 2 = Accelerate
+ 3 = Decelerate
+ 4 = Resume
+ 5 = Set
+ 6 = Accelerator Override
+ 7 = Not available
+ */
+ repeated uint32 SPN582 = 582 [packed=true]; /*PGN 65258 */ //[2*8] Нагрузка на ось
+ optional uint32 SPN597 = 597;//[1] состояние педали рабочего тормоза
+ /* Возможные значения:
+ 0 = Brake pedal released
+ 1 = Brake pedal depressed
+ 2 = Error
+ 3 = Not Available
+ */
+ optional uint32 SPN598 = 598;//[1] состояние педали сцепления
+ /* Возможные значения:
+ 0 = Clutch pedal released
+ 1 = Clutch pedal depressed
+ 2 = Error
+ 3 = Not Available
+ */
+ optional uint32 SPN914 = 914;//[2] пробег до следующего ТО. Cмещение -160635 km (5 km)
+ /* Ошибки:
+ 65 024 .. 65 279 =Error
+ 65 280 .. 65 535 = Not available
+ */
+ optional uint32 SPN916 = 916;//[1] время работы двигателя до следующего ТО. Cмещение -32127 hr (1 hr)
+ /* Ошибки:
+ 65 024 .. 65 279 =Error
+ 65 280 .. 65 535 = Not available
+ */
+ repeated uint32 SPN928 = 928 [packed=true]; /*PGN 65258 */ //[1*8] индекс оси
+ optional uint32 SPN1624 = 1624;//[2] мгновенная скорость (1/256 km/h)
+ // (по CAN это SPN 84, номер не менял чтобы конфликтов с предыдущими прошивками не возникло)
+ /* Ошибки:
+ 65 024 .. 65 279 =Error
+ 65 280 .. 65 535 = Not available
+ */
+ optional uint32 SPN1821 = 1821;//[1] состояние дверей
+ /* Возможные значения:
+ 0 = At least 1 door is open
+ 1 = Closing last door
+ 2 = All doors closed
+ 3..13 = Not defined
+ 14 = Error
+ 15 = Not available
+ */
+ optional uint32 SPN1856 = 1856;//[1] состояние ремней безопасности
+ /* Возможные значения:
+ 0 = NOT Buckled
+ 1 = OK - Seat Belt is buckled
+ 2 = Error - Switch state cannot be determined
+ 3 = Not Available
+ */
+ }
+
+ optional group LLSDt = 8 { // Топливные датчики LLS/LLS-AF
+ optional sint32 TLLS1 = 1; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS1 = 2; //[2] уровень
+ optional sint32 FLLS1 = 3; //[1] код состояния см. Таблицу "Коды состояния LLS".
+
+ optional sint32 TLLS2 = 4; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS2 = 5; //[4] уровень
+ optional sint32 FLLS2 = 6; //[1] код состояния см. Таблицу "Коды состояния LLS".
+
+ optional sint32 TLLS3 = 7; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS3 = 8; //[4] уровень
+ optional sint32 FLLS3 = 9; //[1] код состояния см. Таблицу "Коды состояния LLS".
+
+ optional sint32 TLLS4 = 10; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS4 = 11; //[4] уровень
+ optional sint32 FLLS4 = 12; //[1] код состояния см. Таблицу "Коды состояния LLS".
+
+ optional sint32 TLLS5 = 13; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS5 = 14; //[4] уровень
+ optional sint32 FLLS5 = 15; //[1] код состояния см. "Коды состояния LLS".
+
+ optional sint32 TLLS6 = 16; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS6 = 17; //[4] уровень
+ optional sint32 FLLS6 = 18; //[1] код состояния см. "Коды состояния LLS".
+
+ optional sint32 TLLS7 = 19; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS7 = 20; //[4] уровень
+ optional sint32 FLLS7 = 21; //[1] код состояния см. "Коды состояния LLS".
+
+ optional sint32 TLLS8 = 22; //[1] температура (1LSB = 1гр.C)
+ optional uint32 CLLS8 = 23; //[4] уровень
+ optional sint32 FLLS8 = 24; //[1] код состояния см. "Коды состояния LLS".
+
+ repeated uint32 LLSRefKoef= 25 [packed=true]; //[8] набор из восьми поправочных коэффициентов ДУТ (поз. 0 - ДУТ1, поз. 1 - ДУТ2, ...)
+ }
+
+ optional group Other = 9 { // прочее оборудование
+ optional group OneWire = 1 {
+ // Температурные датчики 1-wire Присутствуют только температуры с настроенных датчиков
+ optional sint32 OneWire1 = 1; //[1] температура c датчика 1
+ optional sint32 OneWire2 = 2; //[1] температура c датчика 2
+ optional sint32 OneWire3 = 3; //[1] температура c датчика 3
+ optional sint32 OneWire4 = 4; //[1] температура c датчика 4
+ optional sint32 OneWire5 = 5; //[1] температура c датчика 5
+ optional sint32 OneWire6 = 6; //[1] температура c датчика 6
+ optional sint32 OneWire7 = 7; //[1] температура c датчика 7
+ optional sint32 OneWire8 = 8; //[1] температура c датчика 8
+ }
+ optional group Pherip = 2 { // Периферия кладется в архив только при данные с только подключенных и настроенных устройств
+ optional uint32 PassengerIn= 1; //[1] Количество вошедших пассажиров
+ optional uint32 PassengerOut= 2; //[1] Количество вышедших пассажиров
+ optional uint32 DoorMask= 3; //[1] маска дверей датчика пасажиропотока
+ optional uint32 DriverStatus= 4; //[1] Статус водителя
+ optional bytes TPMS = 5; //[92] Система контроля давления воздуха в шинах, всего возможно 46 датчика. Массив из 46 структур по 2 байта.
+ optional bytes iQFreeze = 6; //[512] Система контроля рефрижераторами iQFreeze.json - http://wiki.omnicomm.ru/pages/viewpage.action?pageId=11567726
+ }
+ optional group ExCAN = 3 { // Расширенные парметры CAN
+ repeated uint32 SPNid = 1; //[24] Коды SPN
+ repeated uint32 SPNval = 2; //[24] Зачения SPN
+ optional uint64 Adr64TEREX = 10;
+ optional uint64 Adr65TEREX = 11;
+ optional uint64 Adr66TEREX = 12;
+ optional uint64 Adr67TEREX = 13;
+ repeated uint64 Adr11TEREX = 14; //[10] Коды ошибок
+ optional uint64 Adr69TEREX = 15;
+ }
+ optional group Taho_DDD = 4 { // Описание блока передачи части DDD-файла
+ optional uint32 BLKPOS=1;// Позиция текущего блока (в байтах от начала файла)
+ optional bytes FILEDATA=2;//[1024] Данные блока
+ optional bytes CARDID=3;//[16] Идентификатор карты водителя
+ optional uint32 STATUS_DATA=4; // параметр равен 1 если данный блок последний в файле, 2 - если ошибка чтения данных, 0 – если обычный блок данных
+ }
+ optional group APC = 5 { // Данные от ДПП IRMA.
+ optional uint32 APCStatus1 = 1; //[1] Статус ДПП IRMA #1: 0 – ок, 1 – «саботаж», 2 – «дефект», 3 – «датчик не отвечает»
+ optional uint32 APCStatus2 = 2; //[1] Статус ДПП IRMA #2: 0 – ок, 1 – «саботаж», 2 – «дефект», 3 – «датчик не отвечает»
+ optional uint32 APCStatus3 = 3; //[1] Статус ДПП IRMA #3: 0 – ок, 1 – «саботаж», 2 – «дефект», 3 – «датчик не отвечает»
+ optional uint32 APCStatus4 = 4; //[1] Статус ДПП IRMA #4: 0 – ок, 1 – «саботаж», 2 – «дефект», 3 – «датчик не отвечает»
+ optional uint32 APCNumberIn1 = 5; //[2] Количество вошедших пассажиров подсчитанное на ДПП IRMA #1
+ optional uint32 APCNumberOut1 = 6; //[2] Количество вышедших пассажиров подсчитанное на ДПП IRMA #1
+ optional uint32 APCNumberIn2 = 7; //[2] Количество вошедших пассажиров подсчитанное на ДПП IRMA #2
+ optional uint32 APCNumberOut2 = 8; //[2] Количество вышедших пассажиров подсчитанное на ДПП IRMA #2
+ optional uint32 APCNumberIn3 = 9; //[2] Количество вошедших пассажиров подсчитанное на ДПП IRMA #3
+ optional uint32 APCNumberOut31 = 10; //[2] Количество вышедших пассажиров подсчитанное на ДПП IRMA #3
+ optional uint32 APCNumberIn4 = 11; //[2] Количество вошедших пассажиров подсчитанное на ДПП IRMA #4
+ optional uint32 APCNumberOut4 = 12; //[2] Количество вышедших пассажиров подсчитанное на ДПП IRMA #4
+ }
+ optional group MobileEye = 6 { // Данные от MobileEye
+ optional uint32 MobileEyeStatus = 1; //[1] Статус MobileEye (битовая маска событий)
+ repeated bytes CAN700 = 2; // Данные с MobileEye адресс кэн 700
+ repeated bytes CAN727 = 3; // Данные с MobileEye адресс кэн 727
+ repeated bytes CAN760 = 4; // Данные с MobileEye адресс кэн 760
+ }
+ optional group SafeDriving = 7 { // Данные о безопасном вождении
+ optional uint32 EventMask = 1; //[4] Инициатор посылки события (Битовая маска) 0 – Скорость, 1 – Обороты, 2 – Ускорение, 3 – Боковое ускорение, 4 - Ускорение торможения, 5 - Вертикальное ускорение
+ optional uint32 SpeedThreshold = 2; //[4] Порог Скорости, поле присутствует если EventMask/Бит 0 = 1
+ optional uint32 RPMThreshold = 3; //[4] Порог Оборотов, поле присутствует если EventMask/Бит 1 = 1
+ repeated float AccelDangThs = 4 [packed=true]; //[4]
+ }
+ optional group GenComm= 8{ // Данные о GenComm генераторе
+ optional uint32 GEN_OILPRESS1 = 1;
+ optional sint32 GEN_TEMP1 = 2;
+ optional float GEN_VOLT1 = 3;
+ optional uint32 GEN_RPM1 = 4;
+ optional float GEN_UL1_1 = 5;
+ optional float GEN_UL2_1 = 6;
+ optional float GEN_UL3_1 = 7;
+ optional float GEN_IL1_1 = 8;
+ optional float GEN_IL2_1 = 9;
+ optional float GEN_IL3_1 = 10;
+ optional uint32 GEN_STATUS1 = 11;
+ optional uint32 GEN_HOURS1 = 12;
+ repeated bytes GEN_ALARM1 = 13; //[28]
+
+ optional uint32 GEN_OILPRESS2 = 14;
+ optional sint32 GEN_TEMP2 = 15;
+ optional float GEN_VOLT2 = 16;
+ optional uint32 GEN_RPM2 = 17;
+ optional float GEN_UL1_2 = 18;
+ optional float GEN_UL2_2 = 19;
+ optional float GEN_UL3_2 = 20;
+ optional float GEN_IL1_2 = 21;
+ optional float GEN_IL2_2 = 22;
+ optional float GEN_IL3_2 = 23;
+ optional uint32 GEN_STATUS2 = 24;
+ optional uint32 GEN_HOURS2 = 25;
+ repeated bytes GEN_ALARM2 = 26; //[28]
+
+ optional uint32 GEN_OILPRESS3 = 27;
+ optional sint32 GEN_TEMP3 = 28;
+ optional float GEN_VOLT3 = 29;
+ optional uint32 GEN_RPM3 = 30;
+ optional float GEN_UL1_3 = 31;
+ optional float GEN_UL2_3 = 32;
+ optional float GEN_UL3_3 = 33;
+ optional float GEN_IL1_3 = 34;
+ optional float GEN_IL2_3 = 35;
+ optional float GEN_IL3_3 = 36;
+ optional uint32 GEN_STATUS3 = 37;
+ optional uint32 GEN_HOURS3 = 38;
+ repeated bytes GEN_ALARM3 = 39; //[28]
+ optional float GEN_UC = 40; // величина напряжения зарядного генератора
+ optional uint32 GEN_IC = 41; // ток заряда стартерных АКБ
+ optional float GEN_FREQ = 42; // частота выдаваемого напряжения
+ optional float GEN_UL1L2 = 43; // напряжения линейные
+ optional float GEN_UL2L3 = 44; // --
+ optional float GEN_UL1L3 = 45; // --
+ optional uint32 POW_ACT_L1 = 46; // активная мощность
+ optional uint32 POW_ACT_L2 = 47; // --
+ optional uint32 POW_ACT_L3 = 48; // --
+ optional uint32 POW_FULL_L1 = 49; // полная мощность
+ optional uint32 POW_FULL_L2 = 50; // --
+ optional uint32 POW_FULL_L3 = 51; // --
+ optional uint32 POW_REACT_L1 = 52; // реактивная мощность
+ optional uint32 POW_REACT_L2 = 53; // --
+ optional uint32 POW_REACT_L3 = 54; // --
+ optional float POW_KOEF = 55; // коэффициент мощности
+ optional uint32 POW_GEN_TOTAL = 56; // общая выработка электроэнергии
+ optional uint32 FUEL_LEVEL = 57; // текущий объём топлива
+
+ optional uint32 SMS_REGS_0 = 60; // состояние регистра состояния 0 для SMS-оповещений (для отладки)
+ optional uint32 SMS_REGS_1 = 61; // состояние регистра состояния 1 для SMS-оповещений (для отладки)
+ optional uint32 SMS_REGS_2 = 62; // состояние регистра состояния 2 для SMS-оповещений (для отладки)
+ optional uint32 SMS_REGS_3 = 63; // состояние регистра состояния 3 для SMS-оповещений (для отладки)
+ optional uint32 SMS_REGS_4 = 64; // состояние регистра состояния 4 для SMS-оповещений (для отладки)
+ }
+ optional group FuelSensorModbus= 9 { // Данные о сторонних датчиках уровня топлива
+ optional float FuelLevel = 1;
+ optional float MedianLevel = 2;
+ optional uint32 LevelPercent = 3;
+ optional float FuelVolume = 4;
+ optional sint32 FuelTemp = 5;
+ optional float FuelWeight = 6;
+ optional float FuelDensity = 7;
+ optional sint32 VaporTemp = 8;
+ optional float VaporWeight = 9;
+ optional float LiquidWeight = 10;
+ repeated sint32 PointTemp = 11;
+ }
+ optional group WeightControl= 10 { // Данные о весовом контроле
+ repeated float AxelWeight = 1;
+ repeated float AxelLimit = 2;
+ optional float TotalWeight = 3;
+ optional float TotalWeightLimit = 4;
+ }
+ }
+
+ optional group ICONDt = 10 { // данные ICON
+ optional bytes VehicleStatus = 1; //[24] строка статуса ТС. Сейчас от ICON передаётся 20 символов+NULL. [Hex2ASCII]
+ optional uint32 VehicleStatusID = 2; //[1] ID статуса ТС. [Hex2Dec]
+ optional uint32 VehicleStatusGroupID = 3; //[1] ID группы статусов ТС. Резерв. [Hex2Dec]
+ optional uint64 MsgID = 4; //[8] UID сообщения
+ optional uint32 MsgStatus = 5; //[4] флаг статуса сообщения, см CMDPBF.proto
+ optional uint32 StatDate = 6; //[4] Время/дата изменения статуса сообщения (в OmnicommTime)
+ optional bytes VehicleStatusPrev = 7; //[24] строка предыдущего статуса ТС. Сейчас от ICON передаётся 20 символов+NULL. [Hex2ASCII]
+ optional uint32 VehicleStatusFlags = 8; // флаг активности текущего статуса ТС (0x1 - активен, 0x0 - завершен)
+ }
+ optional group OBDDt_J1979 = 11 { // Данные шины CAN (протокол J1979)
+ // в формулах расчёта значений OBD используются обозначения A, B, C, D
+ // uint32 data = [D | C | B | A], где А - младший байт, D - старший байт
+ optional uint32 SID_0x01_PID_0x0D = 1;// Скорость ТС = A (km/h) - Vehicle Speed Sensor
+ optional uint32 SID_0x01_PID_0x31 = 2;// Пробег после сброса ошибок = (256 * A + B), (km) - Distance travelled since DTCs cleared
+ optional uint32 SID_0x01_PID_0x4E = 3;// Время работы двинателя после сброса ошибок = (A*256)+B (минуты) - Engine Run Time since diagnostic trouble codes cleare
+ optional uint32 SID_0x01_PID_0x42 = 4;// Напряжение борта, В = ((A*256) + B)/1000 (В) - Control module voltage
+ optional uint32 SID_0x01_PID_0x0C = 5;// Обороты = (256 * A + B) / 4 (rpm) - Engine RPM
+ optional uint32 SID_0x01_PID_0x2F = 6;// Объема топлива = (100 * A / 255) (%) - Fuel Level Input
+ optional uint32 SID_0x01_PID_0x5E = 7;// Среднее значение мгновенного расхода = (256 * A + B) *0,05 (L/h) - Engine Fuel Rate
+ // Терминал получает несколько значений мгновенного расхода по OBD, рассчитывает и отправляет среднее
+ optional uint32 SID_0x01_PID_0x5C = 8;// Температура масла = (A - 40) (°C)- Engine Oil Temperature
+ optional uint32 SID_0x01_PID_0x05 = 9;// Температура ОЖ = (A - 40) (°C) - Engine Coolant Temperature
+ optional uint32 SID_0x01_PID_0x01 = 10;// Статус неисправности Check Engine - Monitor status since DTCs cleared
+ /* Malfunction Indicator Lamp (MIL) Status:
+ Если A/бит 7 = 0, то ошибки нет
+ Если A/бит 7 = 1, то ошибка есть
+ */
+ optional bytes SID_0x09_PID_0x02 = 11;// [18] VIN ТС = ASCII null-terminated string. Идут значащие символы, потом нулевой байт. Если перый байт нулувой, то строка пустая.
+ optional uint32 SID_0x01_PID_0x5E_quantity = 12; // Кол-во успешно полученных ответов при расчёте среднего значения мгновенного расхода (optional uint32 SID_0x01_PID_0x5E = 7)
+ }
+ optional group LOG = 16 {
+ repeated string Debug = 1; //[1024] Отладочный лог
+ optional uint32 LLSRefNum= 2; // признак наличия подключенных датчиков REF к терминалу – битовая маска. бит 0 соответсвует подключенному 1-ому датчику и т.д.
+ repeated uint32 LLSRefLevelBef= 3 [packed=true];
+ repeated uint32 LLSRefLevelAfter= 4 [packed=true];
+ repeated uint32 LLSRefKoef= 5 [packed=true];
+ repeated uint32 LLSRefSens= 6 [packed=true];
+ repeated uint32 LLSRefTemp= 7 [packed=true];
+ }
+} \ No newline at end of file
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
deleted file mode 100644
index 771ab8acb..000000000
--- a/src/org/traccar/BasePipelineFactory.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright 2012 - 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;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.ChannelEvent;
-import org.jboss.netty.channel.ChannelHandler;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.DownstreamMessageEvent;
-import org.jboss.netty.channel.MessageEvent;
-import org.jboss.netty.channel.SimpleChannelHandler;
-import org.jboss.netty.handler.logging.LoggingHandler;
-import org.jboss.netty.handler.timeout.IdleStateHandler;
-import org.traccar.events.CommandResultEventHandler;
-import org.traccar.events.DriverEventHandler;
-import org.traccar.events.FuelDropEventHandler;
-import org.traccar.events.GeofenceEventHandler;
-import org.traccar.events.IgnitionEventHandler;
-import org.traccar.events.MaintenanceEventHandler;
-import org.traccar.events.MotionEventHandler;
-import org.traccar.events.OverspeedEventHandler;
-import org.traccar.events.AlertEventHandler;
-import org.traccar.helper.Log;
-import org.traccar.processing.ComputedAttributesHandler;
-import org.traccar.processing.CopyAttributesHandler;
-
-import java.net.InetSocketAddress;
-
-public abstract class BasePipelineFactory implements ChannelPipelineFactory {
-
- private final TrackerServer server;
- private int timeout;
-
- private FilterHandler filterHandler;
- private DistanceHandler distanceHandler;
- private RemoteAddressHandler remoteAddressHandler;
- private MotionHandler motionHandler;
- private GeocoderHandler geocoderHandler;
- private GeolocationHandler geolocationHandler;
- private HemisphereHandler hemisphereHandler;
- private CopyAttributesHandler copyAttributesHandler;
- private ComputedAttributesHandler computedAttributesHandler;
-
- private CommandResultEventHandler commandResultEventHandler;
- private OverspeedEventHandler overspeedEventHandler;
- private FuelDropEventHandler fuelDropEventHandler;
- private MotionEventHandler motionEventHandler;
- private GeofenceEventHandler geofenceEventHandler;
- private AlertEventHandler alertEventHandler;
- private IgnitionEventHandler ignitionEventHandler;
- private MaintenanceEventHandler maintenanceEventHandler;
- private DriverEventHandler driverEventHandler;
-
- private static final class OpenChannelHandler extends SimpleChannelHandler {
-
- private final TrackerServer server;
-
- private OpenChannelHandler(TrackerServer server) {
- this.server = server;
- }
-
- @Override
- public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
- server.getChannelGroup().add(e.getChannel());
- }
- }
-
- private static class StandardLoggingHandler extends LoggingHandler {
-
- @Override
- public void log(ChannelEvent e) {
- if (e instanceof MessageEvent) {
- MessageEvent event = (MessageEvent) e;
- StringBuilder msg = new StringBuilder();
-
- msg.append("[").append(String.format("%08X", e.getChannel().getId())).append(": ");
- msg.append(((InetSocketAddress) e.getChannel().getLocalAddress()).getPort());
- if (e instanceof DownstreamMessageEvent) {
- msg.append(" > ");
- } else {
- msg.append(" < ");
- }
-
- if (event.getRemoteAddress() != null) {
- msg.append(((InetSocketAddress) event.getRemoteAddress()).getHostString());
- } else {
- msg.append("null");
- }
- msg.append("]");
-
- if (event.getMessage() instanceof ChannelBuffer) {
- msg.append(" HEX: ");
- msg.append(ChannelBuffers.hexDump((ChannelBuffer) event.getMessage()));
- }
-
- Log.debug(msg.toString());
- }
- }
-
- }
-
- public BasePipelineFactory(TrackerServer server, String protocol) {
- this.server = server;
-
- timeout = Context.getConfig().getInteger(protocol + ".timeout");
- if (timeout == 0) {
- timeout = Context.getConfig().getInteger(protocol + ".resetDelay"); // temporary
- if (timeout == 0) {
- timeout = Context.getConfig().getInteger("server.timeout");
- }
- }
-
- distanceHandler = new DistanceHandler(
- Context.getConfig().getBoolean("coordinates.filter"),
- Context.getConfig().getInteger("coordinates.minError"),
- Context.getConfig().getInteger("coordinates.maxError"));
-
- if (Context.getConfig().getBoolean("processing.remoteAddress.enable")) {
- remoteAddressHandler = new RemoteAddressHandler();
- }
-
- if (Context.getConfig().getBoolean("filter.enable")) {
- filterHandler = new FilterHandler();
- }
-
- if (Context.getGeocoder() != null) {
- geocoderHandler = new GeocoderHandler(
- Context.getGeocoder(),
- Context.getConfig().getBoolean("geocoder.processInvalidPositions"));
- }
-
- if (Context.getGeolocationProvider() != null) {
- geolocationHandler = new GeolocationHandler(
- Context.getGeolocationProvider(),
- Context.getConfig().getBoolean("geolocation.processInvalidPositions"));
- }
-
- motionHandler = new MotionHandler(Context.getTripsConfig().getSpeedThreshold());
-
- if (Context.getConfig().hasKey("location.latitudeHemisphere")
- || Context.getConfig().hasKey("location.longitudeHemisphere")) {
- hemisphereHandler = new HemisphereHandler();
- }
-
- if (Context.getConfig().getBoolean("processing.copyAttributes.enable")) {
- copyAttributesHandler = new CopyAttributesHandler();
- }
-
- if (Context.getConfig().getBoolean("processing.computedAttributes.enable")) {
- computedAttributesHandler = new ComputedAttributesHandler();
- }
-
- if (Context.getConfig().getBoolean("event.enable")) {
- commandResultEventHandler = new CommandResultEventHandler();
- overspeedEventHandler = Context.getOverspeedEventHandler();
- fuelDropEventHandler = new FuelDropEventHandler();
- motionEventHandler = Context.getMotionEventHandler();
- geofenceEventHandler = new GeofenceEventHandler();
- alertEventHandler = new AlertEventHandler();
- ignitionEventHandler = new IgnitionEventHandler();
- maintenanceEventHandler = new MaintenanceEventHandler();
- driverEventHandler = new DriverEventHandler();
- }
- }
-
- protected abstract void addSpecificHandlers(ChannelPipeline pipeline);
-
- @Override
- public ChannelPipeline getPipeline() {
- ChannelPipeline pipeline = Channels.pipeline();
- if (timeout > 0 && !server.isConnectionless()) {
- pipeline.addLast("idleHandler", new IdleStateHandler(GlobalTimer.getTimer(), timeout, 0, 0));
- }
- pipeline.addLast("openHandler", new OpenChannelHandler(server));
- if (Context.isLoggerEnabled()) {
- pipeline.addLast("logger", new StandardLoggingHandler());
- }
-
- addSpecificHandlers(pipeline);
-
- if (geolocationHandler != null) {
- pipeline.addLast("location", geolocationHandler);
- }
- if (hemisphereHandler != null) {
- pipeline.addLast("hemisphere", hemisphereHandler);
- }
-
- if (distanceHandler != null) {
- pipeline.addLast("distance", distanceHandler);
- }
-
- if (remoteAddressHandler != null) {
- pipeline.addLast("remoteAddress", remoteAddressHandler);
- }
-
- addDynamicHandlers(pipeline);
-
- if (filterHandler != null) {
- pipeline.addLast("filter", filterHandler);
- }
-
- if (geocoderHandler != null) {
- pipeline.addLast("geocoder", geocoderHandler);
- }
-
- if (motionHandler != null) {
- pipeline.addLast("motion", motionHandler);
- }
-
- if (copyAttributesHandler != null) {
- pipeline.addLast("copyAttributes", copyAttributesHandler);
- }
-
- if (computedAttributesHandler != null) {
- pipeline.addLast("computedAttributes", computedAttributesHandler);
- }
-
- if (Context.getDataManager() != null) {
- pipeline.addLast("dataHandler", new DefaultDataHandler());
- }
-
- if (Context.getConfig().getBoolean("forward.enable")) {
- pipeline.addLast("webHandler", new WebDataHandler(Context.getConfig().getString("forward.url")));
- }
-
- if (commandResultEventHandler != null) {
- pipeline.addLast("CommandResultEventHandler", commandResultEventHandler);
- }
-
- if (overspeedEventHandler != null) {
- pipeline.addLast("OverspeedEventHandler", overspeedEventHandler);
- }
-
- if (fuelDropEventHandler != null) {
- pipeline.addLast("FuelDropEventHandler", fuelDropEventHandler);
- }
-
- if (motionEventHandler != null) {
- pipeline.addLast("MotionEventHandler", motionEventHandler);
- }
-
- if (geofenceEventHandler != null) {
- pipeline.addLast("GeofenceEventHandler", geofenceEventHandler);
- }
-
- if (alertEventHandler != null) {
- pipeline.addLast("AlertEventHandler", alertEventHandler);
- }
-
- if (ignitionEventHandler != null) {
- pipeline.addLast("IgnitionEventHandler", ignitionEventHandler);
- }
-
- if (maintenanceEventHandler != null) {
- pipeline.addLast("MaintenanceEventHandler", maintenanceEventHandler);
- }
-
- if (driverEventHandler != null) {
- pipeline.addLast("DriverEventHandler", driverEventHandler);
- }
-
- pipeline.addLast("mainHandler", new MainEventHandler());
- return pipeline;
- }
-
- private void addDynamicHandlers(ChannelPipeline pipeline) {
- if (Context.getConfig().hasKey("extra.handlers")) {
- String[] handlers = Context.getConfig().getString("extra.handlers").split(",");
- for (int i = 0; i < handlers.length; i++) {
- try {
- pipeline.addLast("extraHandler." + i, (ChannelHandler) Class.forName(handlers[i]).newInstance());
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException error) {
- Log.warning(error);
- }
- }
- }
- }
-}
diff --git a/src/org/traccar/BaseProtocolEncoder.java b/src/org/traccar/BaseProtocolEncoder.java
deleted file mode 100644
index 2c8a81868..000000000
--- a/src/org/traccar/BaseProtocolEncoder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar;
-
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-import org.traccar.model.Device;
-
-public abstract class BaseProtocolEncoder extends OneToOneEncoder {
-
- protected String getUniqueId(long deviceId) {
- return Context.getIdentityManager().getById(deviceId).getUniqueId();
- }
-
- protected void initDevicePassword(Command command, String defaultPassword) {
- if (!command.getAttributes().containsKey(Command.KEY_DEVICE_PASSWORD)) {
- Device device = Context.getIdentityManager().getById(command.getDeviceId());
- String password = device.getString(Command.KEY_DEVICE_PASSWORD);
- if (password != null) {
- command.set(Command.KEY_DEVICE_PASSWORD, password);
- } else {
- command.set(Command.KEY_DEVICE_PASSWORD, defaultPassword);
- }
- }
- }
-
- @Override
- protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
-
- if (msg instanceof Command) {
- Command command = (Command) msg;
- Object encodedCommand = encodeCommand(command);
-
- // Log command
- StringBuilder s = new StringBuilder();
- s.append(String.format("[%08X] ", channel.getId()));
- s.append("id: ").append(getUniqueId(command.getDeviceId())).append(", ");
- s.append("command type: ").append(command.getType()).append(" ");
- if (encodedCommand != null) {
- s.append("sent");
- } else {
- s.append("not sent");
- }
- Log.info(s.toString());
-
- return encodedCommand;
- }
-
- return msg;
- }
-
- protected abstract Object encodeCommand(Command command);
-
-}
diff --git a/src/org/traccar/Config.java b/src/org/traccar/Config.java
deleted file mode 100644
index 43f4632da..000000000
--- a/src/org/traccar/Config.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2015 - 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;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-public class Config {
-
- private final Properties properties = new Properties();
-
- private boolean useEnvironmentVariables;
-
- void load(String file) throws IOException {
- Properties mainProperties = new Properties();
- try (InputStream inputStream = new FileInputStream(file)) {
- mainProperties.loadFromXML(inputStream);
- }
-
- String defaultConfigFile = mainProperties.getProperty("config.default");
- if (defaultConfigFile != null) {
- try (InputStream inputStream = new FileInputStream(defaultConfigFile)) {
- properties.loadFromXML(inputStream);
- }
- }
-
- properties.putAll(mainProperties); // override defaults
-
- useEnvironmentVariables = Boolean.parseBoolean(System.getenv("CONFIG_USE_ENVIRONMENT_VARIABLES"))
- || Boolean.parseBoolean(properties.getProperty("config.useEnvironmentVariables"));
- }
-
- public boolean hasKey(String key) {
- return useEnvironmentVariables && System.getenv().containsKey(getEnvironmentVariableName(key))
- || properties.containsKey(key);
- }
-
- public String getString(String key) {
- if (useEnvironmentVariables) {
- String value = System.getenv(getEnvironmentVariableName(key));
- if (value != null && !value.isEmpty()) {
- return value;
- }
- }
- return properties.getProperty(key);
- }
-
- public String getString(String key, String defaultValue) {
- return hasKey(key) ? getString(key) : defaultValue;
- }
-
- public boolean getBoolean(String key) {
- return Boolean.parseBoolean(getString(key));
- }
-
- public int getInteger(String key) {
- return getInteger(key, 0);
- }
-
- public int getInteger(String key, int defaultValue) {
- return hasKey(key) ? Integer.parseInt(getString(key)) : defaultValue;
- }
-
- public long getLong(String key) {
- return getLong(key, 0);
- }
-
- public long getLong(String key, long defaultValue) {
- return hasKey(key) ? Long.parseLong(getString(key)) : defaultValue;
- }
-
- public double getDouble(String key) {
- return getDouble(key, 0.0);
- }
-
- public double getDouble(String key, double defaultValue) {
- return hasKey(key) ? Double.parseDouble(getString(key)) : defaultValue;
- }
-
- public static String getEnvironmentVariableName(String key) {
- return key.replaceAll("\\.", "_").replaceAll("(\\p{Lu})", "_$1").toUpperCase();
- }
-
- public void setString(String key, String value) {
- properties.put(key, value);
- }
-
-}
diff --git a/src/org/traccar/GeocoderHandler.java b/src/org/traccar/GeocoderHandler.java
deleted file mode 100644
index a211d1a23..000000000
--- a/src/org/traccar/GeocoderHandler.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2012 - 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;
-
-import org.jboss.netty.channel.ChannelEvent;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelUpstreamHandler;
-import org.jboss.netty.channel.Channels;
-import org.jboss.netty.channel.MessageEvent;
-import org.traccar.geocoder.AddressFormat;
-import org.traccar.geocoder.Geocoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Position;
-
-public class GeocoderHandler implements ChannelUpstreamHandler {
-
- private final Geocoder geocoder;
- private final boolean processInvalidPositions;
- private final AddressFormat addressFormat;
- private final int geocoderReuseDistance;
-
- public GeocoderHandler(Geocoder geocoder, boolean processInvalidPositions) {
- this.geocoder = geocoder;
- this.processInvalidPositions = processInvalidPositions;
-
- String formatString = Context.getConfig().getString("geocoder.format");
- if (formatString != null) {
- addressFormat = new AddressFormat(formatString);
- } else {
- addressFormat = new AddressFormat();
- }
-
- geocoderReuseDistance = Context.getConfig().getInteger("geocoder.reuseDistance", 0);
- }
-
- @Override
- public void handleUpstream(final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
- if (!(evt instanceof MessageEvent)) {
- ctx.sendUpstream(evt);
- return;
- }
-
- final MessageEvent event = (MessageEvent) evt;
- Object message = event.getMessage();
- if (message instanceof Position) {
- final Position position = (Position) message;
- if (processInvalidPositions || position.getValid()) {
- if (geocoderReuseDistance != 0) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
- if (lastPosition != null && lastPosition.getAddress() != null
- && position.getDouble(Position.KEY_DISTANCE) <= geocoderReuseDistance) {
- position.setAddress(lastPosition.getAddress());
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
- return;
- }
- }
-
- Context.getStatisticsManager().registerGeocoderRequest();
-
- geocoder.getAddress(addressFormat, position.getLatitude(), position.getLongitude(),
- new Geocoder.ReverseGeocoderCallback() {
- @Override
- public void onSuccess(String address) {
- position.setAddress(address);
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
- }
-
- @Override
- public void onFailure(Throwable e) {
- Log.warning("Geocoding failed", e);
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
- }
- });
- } else {
- Channels.fireMessageReceived(ctx, position, event.getRemoteAddress());
- }
- } else {
- Channels.fireMessageReceived(ctx, message, event.getRemoteAddress());
- }
- }
-
-}
diff --git a/src/org/traccar/GlobalChannelFactory.java b/src/org/traccar/GlobalChannelFactory.java
deleted file mode 100644
index c5cd1a697..000000000
--- a/src/org/traccar/GlobalChannelFactory.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2012 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar;
-
-import org.jboss.netty.channel.ChannelFactory;
-import org.jboss.netty.channel.socket.DatagramChannelFactory;
-import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory;
-import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
-
-public final class GlobalChannelFactory {
-
- private static ChannelFactory channelFactory = null;
- private static DatagramChannelFactory datagramChannelFactory = null;
-
- private GlobalChannelFactory() {
- }
-
- public static void release() {
- if (channelFactory != null) {
- channelFactory.releaseExternalResources();
- }
- if (datagramChannelFactory != null) {
- datagramChannelFactory.releaseExternalResources();
- }
- channelFactory = null;
- datagramChannelFactory = null;
- }
-
- public static ChannelFactory getFactory() {
- if (channelFactory == null) {
- channelFactory = new NioServerSocketChannelFactory();
- }
- return channelFactory;
- }
-
- public static DatagramChannelFactory getDatagramFactory() {
- if (datagramChannelFactory == null) {
- datagramChannelFactory = new NioDatagramChannelFactory();
- }
- return datagramChannelFactory;
- }
-
-}
diff --git a/src/org/traccar/Main.java b/src/org/traccar/Main.java
deleted file mode 100644
index 1e2db2693..000000000
--- a/src/org/traccar/Main.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 org.traccar.helper.Log;
-
-import java.sql.SQLException;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.Locale;
-
-public final class Main {
-
- private static final long CLEAN_PERIOD = 24 * 60 * 60 * 1000;
-
- private Main() {
- }
-
- public static void main(String[] args) throws Exception {
- Locale.setDefault(Locale.ENGLISH);
-
- Context.init(args);
- Log.info("Starting server...");
-
- Context.getServerManager().start();
- if (Context.getWebServer() != null) {
- Context.getWebServer().start();
- }
-
- new Timer().scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- try {
- Context.getDataManager().clearHistory();
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }, 0, CLEAN_PERIOD);
-
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- Log.info("Shutting down server...");
-
- if (Context.getWebServer() != null) {
- Context.getWebServer().stop();
- }
- Context.getServerManager().stop();
- }
- });
- }
-
-}
diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java
deleted file mode 100644
index 8e88e15b9..000000000
--- a/src/org/traccar/MainEventHandler.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.channel.ChannelStateEvent;
-import org.jboss.netty.channel.ExceptionEvent;
-import org.jboss.netty.channel.MessageEvent;
-import org.jboss.netty.channel.socket.DatagramChannel;
-import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
-import org.jboss.netty.handler.timeout.IdleStateEvent;
-import org.traccar.helper.Log;
-import org.traccar.model.Position;
-
-import java.sql.SQLException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-public class MainEventHandler extends IdleStateAwareChannelHandler {
-
- private final Set<String> connectionlessProtocols = new HashSet<>();
-
- public MainEventHandler() {
- String connectionlessProtocolList = Context.getConfig().getString("status.ignoreOffline");
- if (connectionlessProtocolList != null) {
- connectionlessProtocols.addAll(Arrays.asList(connectionlessProtocolList.split(",")));
- }
- }
-
- @Override
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
-
- if (e.getMessage() != null && e.getMessage() instanceof Position) {
-
- Position position = (Position) e.getMessage();
- try {
- Context.getDeviceManager().updateLatestPosition(position);
- } catch (SQLException error) {
- Log.warning(error);
- }
-
- String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId();
-
- // Log position
- StringBuilder s = new StringBuilder();
- s.append(formatChannel(e.getChannel())).append(" ");
- s.append("id: ").append(uniqueId).append(", ");
- s.append("time: ").append(
- new SimpleDateFormat(Log.DATE_FORMAT).format(position.getFixTime())).append(", ");
- s.append("lat: ").append(String.format("%.5f", position.getLatitude())).append(", ");
- s.append("lon: ").append(String.format("%.5f", position.getLongitude())).append(", ");
- s.append("speed: ").append(String.format("%.1f", position.getSpeed())).append(", ");
- s.append("course: ").append(String.format("%.1f", position.getCourse()));
- Object cmdResult = position.getAttributes().get(Position.KEY_RESULT);
- if (cmdResult != null) {
- s.append(", result: ").append(cmdResult);
- }
- Log.info(s.toString());
-
- Context.getStatisticsManager().registerMessageStored(position.getDeviceId());
- }
- }
-
- private static String formatChannel(Channel channel) {
- return String.format("[%08X]", channel.getId());
- }
-
- @Override
- public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
- Log.info(formatChannel(e.getChannel()) + " connected");
- }
-
- @Override
- public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
- Log.info(formatChannel(e.getChannel()) + " disconnected");
- closeChannel(e.getChannel());
-
- BaseProtocolDecoder protocolDecoder = (BaseProtocolDecoder) ctx.getPipeline().get("objectDecoder");
- if (ctx.getPipeline().get("httpDecoder") == null
- && !connectionlessProtocols.contains(protocolDecoder.getProtocolName())) {
- Context.getConnectionManager().removeActiveDevice(e.getChannel());
- }
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
- Log.warning(formatChannel(e.getChannel()) + " error", e.getCause());
- closeChannel(e.getChannel());
- }
-
- @Override
- public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
- Log.info(formatChannel(e.getChannel()) + " timed out");
- closeChannel(e.getChannel());
- }
-
- private void closeChannel(Channel channel) {
- if (!(channel instanceof DatagramChannel)) {
- channel.close();
- }
- }
-
-}
diff --git a/src/org/traccar/Protocol.java b/src/org/traccar/Protocol.java
deleted file mode 100644
index 87ac05298..000000000
--- a/src/org/traccar/Protocol.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.traccar;
-
-import org.traccar.database.ActiveDevice;
-import org.traccar.model.Command;
-
-import java.util.Collection;
-import java.util.List;
-
-public interface Protocol {
-
- String getName();
-
- Collection<String> getSupportedDataCommands();
-
- void sendDataCommand(ActiveDevice activeDevice, Command command);
-
- void initTrackerServers(List<TrackerServer> serverList);
-
- Collection<String> getSupportedTextCommands();
-
- void sendTextCommand(String destAddress, Command command) throws Exception;
-
-}
diff --git a/src/org/traccar/TrackerServer.java b/src/org/traccar/TrackerServer.java
deleted file mode 100644
index 7e0a6f0c6..000000000
--- a/src/org/traccar/TrackerServer.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2012 - 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;
-
-import org.jboss.netty.bootstrap.Bootstrap;
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.buffer.HeapChannelBufferFactory;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.channel.ChannelPipelineFactory;
-import org.jboss.netty.channel.group.ChannelGroup;
-import org.jboss.netty.channel.group.ChannelGroupFuture;
-import org.jboss.netty.channel.group.DefaultChannelGroup;
-
-import java.net.InetSocketAddress;
-import java.nio.ByteOrder;
-
-public abstract class TrackerServer {
-
- private final Bootstrap bootstrap;
- private final String protocol;
-
- public boolean isConnectionless() {
- return bootstrap instanceof ConnectionlessBootstrap;
- }
-
- public String getProtocol() {
- return protocol;
- }
-
- public TrackerServer(Bootstrap bootstrap, String protocol) {
- this.bootstrap = bootstrap;
- this.protocol = protocol;
-
- if (bootstrap instanceof ServerBootstrap) {
- bootstrap.setFactory(GlobalChannelFactory.getFactory());
- } else if (bootstrap instanceof ConnectionlessBootstrap) {
- bootstrap.setFactory(GlobalChannelFactory.getDatagramFactory());
- }
-
- address = Context.getConfig().getString(protocol + ".address");
- port = Context.getConfig().getInteger(protocol + ".port");
-
- bootstrap.setPipelineFactory(new BasePipelineFactory(this, protocol) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- TrackerServer.this.addSpecificHandlers(pipeline);
- }
- });
- }
-
- protected abstract void addSpecificHandlers(ChannelPipeline pipeline);
-
- private int port;
-
- 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;
- }
-
- public void setEndianness(ByteOrder byteOrder) {
- bootstrap.setOption("bufferFactory", new HeapChannelBufferFactory(byteOrder));
- bootstrap.setOption("child.bufferFactory", new HeapChannelBufferFactory(byteOrder));
- }
-
- private final ChannelGroup allChannels = new DefaultChannelGroup();
-
- public ChannelGroup getChannelGroup() {
- return allChannels;
- }
-
- public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) {
- bootstrap.setPipelineFactory(pipelineFactory);
- }
-
- public ChannelPipelineFactory getPipelineFactory() {
- return bootstrap.getPipelineFactory();
- }
-
- public void start() {
- InetSocketAddress endpoint;
- if (address == null) {
- endpoint = new InetSocketAddress(port);
- } else {
- endpoint = new InetSocketAddress(address, port);
- }
-
- Channel channel = null;
- if (bootstrap instanceof ServerBootstrap) {
- channel = ((ServerBootstrap) bootstrap).bind(endpoint);
- } else if (bootstrap instanceof ConnectionlessBootstrap) {
- channel = ((ConnectionlessBootstrap) bootstrap).bind(endpoint);
- }
-
- if (channel != null) {
- getChannelGroup().add(channel);
- }
- }
-
- public void stop() {
- ChannelGroupFuture future = getChannelGroup().close();
- future.awaitUninterruptibly();
- }
-
-}
diff --git a/src/org/traccar/database/IdentityManager.java b/src/org/traccar/database/IdentityManager.java
deleted file mode 100644
index 82d905963..000000000
--- a/src/org/traccar/database/IdentityManager.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 {
-
- Device getById(long id);
-
- Device getByUniqueId(String uniqueId) throws Exception;
-
- Position getLastPosition(long deviceId);
-
- boolean isLatestPosition(Position position);
-
- boolean lookupAttributeBoolean(long deviceId, String attributeName, boolean defaultValue, boolean lookupConfig);
-
- String lookupAttributeString(long deviceId, String attributeName, String defaultValue, boolean lookupConfig);
-
- int lookupAttributeInteger(long deviceId, String attributeName, int defaultValue, boolean lookupConfig);
-
- long lookupAttributeLong(long deviceId, String attributeName, long defaultValue, boolean lookupConfig);
-
-}
diff --git a/src/org/traccar/events/AlertEventHandler.java b/src/org/traccar/events/AlertEventHandler.java
deleted file mode 100644
index 003ccb662..000000000
--- a/src/org/traccar/events/AlertEventHandler.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.events;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.traccar.BaseEventHandler;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-
-public class AlertEventHandler extends BaseEventHandler {
-
- @Override
- protected Map<Event, Position> analyzePosition(Position position) {
- Object alarm = position.getAttributes().get(Position.KEY_ALARM);
- if (alarm != null) {
- Event event = new Event(Event.TYPE_ALARM, position.getDeviceId(), position.getId());
- event.set(Position.KEY_ALARM, (String) alarm);
- return Collections.singletonMap(event, position);
- }
- return null;
- }
-
-}
diff --git a/src/org/traccar/events/MaintenanceEventHandler.java b/src/org/traccar/events/MaintenanceEventHandler.java
deleted file mode 100644
index 86abf7c17..000000000
--- a/src/org/traccar/events/MaintenanceEventHandler.java
+++ /dev/null
@@ -1,69 +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.events;
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.traccar.BaseEventHandler;
-import org.traccar.Context;
-import org.traccar.model.Device;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-
-public class MaintenanceEventHandler extends BaseEventHandler {
-
- public static final String ATTRIBUTE_MAINTENANCE_START = "maintenance.start";
- public static final String ATTRIBUTE_MAINTENANCE_INTERVAL = "maintenance.interval";
-
- @Override
- protected Map<Event, Position> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getById(position.getDeviceId());
- if (device == null || !Context.getIdentityManager().isLatestPosition(position)) {
- return null;
- }
-
- double maintenanceInterval = Context.getDeviceManager()
- .lookupAttributeDouble(device.getId(), ATTRIBUTE_MAINTENANCE_INTERVAL, 0, false);
- if (maintenanceInterval == 0) {
- return null;
- }
- double maintenanceStart = Context.getDeviceManager()
- .lookupAttributeDouble(device.getId(), ATTRIBUTE_MAINTENANCE_START, 0, false);
-
- double oldTotalDistance = 0.0;
- double newTotalDistance = 0.0;
-
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
- if (lastPosition != null) {
- oldTotalDistance = lastPosition.getDouble(Position.KEY_TOTAL_DISTANCE);
- }
- newTotalDistance = position.getDouble(Position.KEY_TOTAL_DISTANCE);
-
- oldTotalDistance -= maintenanceStart;
- newTotalDistance -= maintenanceStart;
-
- if ((long) (oldTotalDistance / maintenanceInterval) < (long) (newTotalDistance / maintenanceInterval)) {
- Event event = new Event(Event.TYPE_MAINTENANCE, position.getDeviceId(), position.getId());
- event.set(Position.KEY_TOTAL_DISTANCE, newTotalDistance);
- return Collections.singletonMap(event, position);
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/geocoder/JsonGeocoder.java b/src/org/traccar/geocoder/JsonGeocoder.java
deleted file mode 100644
index 6d1380729..000000000
--- a/src/org/traccar/geocoder/JsonGeocoder.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 com.ning.http.client.AsyncCompletionHandler;
-import com.ning.http.client.Response;
-import org.traccar.Context;
-
-import javax.json.Json;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-import java.util.AbstractMap;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-public abstract class JsonGeocoder implements Geocoder {
-
- private final String url;
-
- private Map<Map.Entry<Double, Double>, String> cache;
-
- public JsonGeocoder(String url, final int cacheSize) {
- this.url = url;
- if (cacheSize > 0) {
- this.cache = Collections.synchronizedMap(new LinkedHashMap<Map.Entry<Double, Double>, String>() {
- @Override
- protected boolean removeEldestEntry(Map.Entry eldest) {
- return size() > cacheSize;
- }
- });
- }
- }
-
- @Override
- public void getAddress(
- final AddressFormat format, final double latitude,
- final double longitude, final ReverseGeocoderCallback callback) {
-
- if (cache != null) {
- String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude));
- if (cachedAddress != null) {
- callback.onSuccess(cachedAddress);
- return;
- }
- }
-
- Context.getAsyncHttpClient().prepareGet(String.format(url, latitude, longitude))
- .execute(new AsyncCompletionHandler() {
- @Override
- public Object onCompleted(Response response) throws Exception {
- try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
- Address address = parseAddress(reader.readObject());
- if (address != null) {
- String formattedAddress = format.format(address);
- if (cache != null) {
- cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress);
- }
- callback.onSuccess(formattedAddress);
- } else {
- callback.onFailure(new GeocoderException("Empty address"));
- }
- }
- return null;
- }
-
- @Override
- public void onThrowable(Throwable t) {
- callback.onFailure(t);
- }
- });
- }
-
- public abstract Address parseAddress(JsonObject json);
-
-}
diff --git a/src/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/org/traccar/geolocation/UniversalGeolocationProvider.java
deleted file mode 100644
index 6416b2633..000000000
--- a/src/org/traccar/geolocation/UniversalGeolocationProvider.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.geolocation;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.ning.http.client.AsyncCompletionHandler;
-import com.ning.http.client.Response;
-import org.traccar.Context;
-import org.traccar.model.Network;
-
-import javax.json.Json;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-
-public class UniversalGeolocationProvider implements GeolocationProvider {
-
- private String url;
-
- public UniversalGeolocationProvider(String url, String key) {
- this.url = url + "?key=" + key;
- }
-
- @Override
- public void getLocation(Network network, final LocationProviderCallback callback) {
- try {
- String request = Context.getObjectMapper().writeValueAsString(network);
- Context.getAsyncHttpClient().preparePost(url)
- .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
- .setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(request.length()))
- .setBody(request).execute(new AsyncCompletionHandler() {
- @Override
- public Object onCompleted(Response response) throws Exception {
- try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
- JsonObject json = reader.readObject();
- if (json.containsKey("error")) {
- callback.onFailure(
- new GeolocationException(json.getJsonObject("error").getString("message")));
- } else {
- JsonObject location = json.getJsonObject("location");
- callback.onSuccess(
- location.getJsonNumber("lat").doubleValue(),
- location.getJsonNumber("lng").doubleValue(),
- json.getJsonNumber("accuracy").doubleValue());
- }
- }
- return null;
- }
-
- @Override
- public void onThrowable(Throwable t) {
- callback.onFailure(t);
- }
- });
- } catch (JsonProcessingException e) {
- callback.onFailure(e);
- }
- }
-
-}
diff --git a/src/org/traccar/helper/Checksum.java b/src/org/traccar/helper/Checksum.java
deleted file mode 100644
index 44cef9635..000000000
--- a/src/org/traccar/helper/Checksum.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.zip.CRC32;
-
-public final class Checksum {
-
- private Checksum() {
- }
-
- private static final int[] CRC16_CCITT_TABLE_REVERSE = {
- 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
- 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
- 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
- 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
- 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
- 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
- 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
- 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
- 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
- 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
- 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
- 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
- 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
- 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
- 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
- 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
- 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
- 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
- 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
- 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
- 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
- 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
- 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
- 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
- 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
- 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
- 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
- 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
- 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
- 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
- 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
- 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
- };
-
- private static final int[] CRC16_CCITT_TABLE = {
- 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
- 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
- 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
- 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
- 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
- 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
- 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
- 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
- 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
- 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
- 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
- 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
- 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
- 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
- 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
- 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
- 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
- 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
- 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
- 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
- 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
- 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
- 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
- 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
- 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
- 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
- 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
- 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
- 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
- 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
- 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
- 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
- };
-
- private static final int[] CRC16_IBM_TABLE = {
- 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
- 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
- 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
- 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
- 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
- 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
- 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
- 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
- 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
- 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
- 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
- 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
- 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
- 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
- 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
- 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
- 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
- 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
- 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
- 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
- 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
- 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
- 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
- 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
- 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
- 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
- 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
- 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
- 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
- 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
- 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
- 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
- };
-
- // More info: http://reveng.sourceforge.net/crc-catalogue/16.htm
- public static final String CRC16_IBM = "IBM";
- public static final String CRC16_MODBUS = "MODBUS";
- public static final String CRC16_X25 = "X-25";
- public static final String CRC16_CCITT_FALSE = "CCITT-FALSE";
- public static final String CRC16_KERMIT = "KERMIT";
- public static final String CRC16_XMODEM = "XMODEM";
- public static final String CRC16_AUG_CCITT = "AUG-CCITT";
- public static final String CRC16_GENIBUS = "GENIBUS";
- public static final String CRC16_MCRF4XX = "MCRF4XX";
-
- private static int crc16Unreflected(ByteBuffer buf, int crcIn, int[] table) {
- int crc16 = crcIn;
- while (buf.hasRemaining()) {
- crc16 = table[((crc16 >> 8) ^ buf.get()) & 0xff] ^ (crc16 << 8);
- }
- return crc16 & 0xFFFF;
- }
-
- private static int crc16Reflected(ByteBuffer buf, int crcIn, int[] table) {
- int crc16 = crcIn;
- while (buf.hasRemaining()) {
- crc16 = table[(crc16 ^ buf.get()) & 0xff] ^ (crc16 >> 8);
- }
- return crc16 & 0xFFFF;
- }
-
- public static int crc16(String type, ByteBuffer buf) {
- switch (type) {
- case CRC16_IBM:
- return crc16Reflected(buf, 0, CRC16_IBM_TABLE);
- case CRC16_MODBUS:
- return crc16Reflected(buf, 0xFFFF, CRC16_IBM_TABLE);
- case CRC16_X25:
- return crc16Reflected(buf, 0xFFFF, CRC16_CCITT_TABLE_REVERSE) ^ 0xFFFF;
- case CRC16_CCITT_FALSE:
- return crc16Unreflected(buf, 0xFFFF, CRC16_CCITT_TABLE);
- case CRC16_KERMIT:
- return crc16Reflected(buf, 0, CRC16_CCITT_TABLE_REVERSE);
- case CRC16_XMODEM:
- return crc16Unreflected(buf, 0, CRC16_CCITT_TABLE);
- case CRC16_AUG_CCITT:
- return crc16Unreflected(buf, 0x1d0f, CRC16_CCITT_TABLE);
- case CRC16_GENIBUS:
- return crc16Unreflected(buf, 0xFFFF, CRC16_CCITT_TABLE) ^ 0xFFFF;
- case CRC16_MCRF4XX:
- return crc16Reflected(buf, 0xFFFF, CRC16_CCITT_TABLE_REVERSE);
- default:
- throw new UnsupportedOperationException(type);
- }
- }
-
- public static int crc32(ByteBuffer buf) {
- CRC32 checksum = new CRC32();
- while (buf.hasRemaining()) {
- checksum.update(buf.get());
- }
- return (int) checksum.getValue();
- }
-
- public static int xor(ByteBuffer buf) {
- int checksum = 0;
- while (buf.hasRemaining()) {
- checksum ^= buf.get();
- }
- return checksum;
- }
-
- public static int xor(String string) {
- byte checksum = 0;
- for (byte b : string.getBytes(StandardCharsets.US_ASCII)) {
- checksum ^= b;
- }
- return checksum;
- }
-
- public static String nmea(String msg) {
- int checksum = 0;
- byte[] bytes = msg.getBytes(StandardCharsets.US_ASCII);
- for (int i = 1; i < bytes.length; i++) {
- checksum ^= bytes[i];
- }
- return String.format("*%02x", checksum).toUpperCase();
- }
-
- public static String sum(String msg) {
- byte checksum = 0;
- for (byte b : msg.getBytes(StandardCharsets.US_ASCII)) {
- checksum += b;
- }
- return String.format("%02X", checksum).toUpperCase();
- }
-
- public static long luhn(long imei) {
- long checksum = 0;
- long remain = imei;
-
- for (int i = 0; remain != 0; i++) {
- long digit = remain % 10;
-
- if (i % 2 == 0) {
- digit *= 2;
- if (digit >= 10) {
- digit = 1 + (digit % 10);
- }
- }
-
- checksum += digit;
- remain /= 10;
- }
-
- return (10 - (checksum % 10)) % 10;
- }
-
- public static int modulo256(byte[] bytes) {
- int sum = 0;
- for (byte b : bytes) {
- sum = (sum + b) & 0xFF;
- }
- return sum;
- }
-
-}
diff --git a/src/org/traccar/helper/Log.java b/src/org/traccar/helper/Log.java
deleted file mode 100644
index d74246a64..000000000
--- a/src/org/traccar/helper/Log.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.helper;
-
-import org.apache.log4j.Appender;
-import org.apache.log4j.DailyRollingFileAppender;
-import org.apache.log4j.Layout;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
-import org.apache.log4j.Logger;
-import org.apache.log4j.PatternLayout;
-import org.apache.log4j.varia.NullAppender;
-import org.jboss.netty.logging.AbstractInternalLogger;
-import org.jboss.netty.logging.InternalLogger;
-import org.jboss.netty.logging.InternalLoggerFactory;
-import org.traccar.Config;
-
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryMXBean;
-import java.lang.management.OperatingSystemMXBean;
-import java.lang.management.RuntimeMXBean;
-import java.nio.charset.Charset;
-
-public final class Log {
-
- private Log() {
- }
-
- public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
-
- private static final String LOGGER_NAME = "traccar";
-
- private static final String STACK_PACKAGE = "org.traccar";
- private static final int STACK_LIMIT = 3;
-
- private static Logger logger = null;
-
- public static String getAppVersion() {
- return Log.class.getPackage().getImplementationVersion();
- }
-
- public static void setupLogger(Config config) throws IOException {
-
- Layout layout = new PatternLayout("%d{" + DATE_FORMAT + "} %5p: %m%n");
-
- Appender appender = new DailyRollingFileAppender(
- layout, config.getString("logger.file"), "'.'yyyyMMdd");
-
- LogManager.resetConfiguration();
- LogManager.getRootLogger().addAppender(new NullAppender());
-
- logger = Logger.getLogger(LOGGER_NAME);
- logger.addAppender(appender);
- logger.setLevel(Level.toLevel(config.getString("logger.level"), Level.ALL));
-
- // Workaround for "Bug 745866 - (EDG-45) Possible netty logging config problem"
- InternalLoggerFactory.setDefaultFactory(new InternalLoggerFactory() {
- @Override
- public InternalLogger newInstance(String string) {
- return new NettyInternalLogger();
- }
- });
-
- Log.logSystemInfo();
- Log.info("Version: " + getAppVersion());
- }
-
- public static Logger getLogger() {
- if (logger == null) {
- logger = Logger.getLogger(LOGGER_NAME);
- logger.setLevel(Level.OFF);
- }
- return logger;
- }
-
- public static void logSystemInfo() {
- try {
- OperatingSystemMXBean operatingSystemBean = ManagementFactory.getOperatingSystemMXBean();
- Log.info("Operating system"
- + " name: " + operatingSystemBean.getName()
- + " version: " + operatingSystemBean.getVersion()
- + " architecture: " + operatingSystemBean.getArch());
-
- RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
- Log.info("Java runtime"
- + " name: " + runtimeBean.getVmName()
- + " vendor: " + runtimeBean.getVmVendor()
- + " version: " + runtimeBean.getVmVersion());
-
- MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
- Log.info("Memory limit"
- + " heap: " + memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024) + "mb"
- + " non-heap: " + memoryBean.getNonHeapMemoryUsage().getMax() / (1024 * 1024) + "mb");
-
- Log.info("Character encoding: "
- + System.getProperty("file.encoding") + " charset: " + Charset.defaultCharset());
-
- } catch (Exception error) {
- Log.warning("Failed to get system info");
- }
- }
-
- public static void error(String msg) {
- getLogger().error(msg);
- }
-
- public static void warning(String msg) {
- getLogger().warn(msg);
- }
-
- public static void warning(Throwable exception) {
- warning(null, exception);
- }
-
- public static void warning(String msg, Throwable exception) {
- StringBuilder s = new StringBuilder();
- if (msg != null) {
- s.append(msg);
- }
- if (exception != null) {
- if (msg != null) {
- s.append(" - ");
- }
- s.append(exceptionStack(exception));
- }
- getLogger().warn(s.toString());
- }
-
- public static void info(String msg) {
- getLogger().info(msg);
- }
-
- public static void debug(String msg) {
- getLogger().debug(msg);
- }
-
- public static String exceptionStack(Throwable exception) {
- StringBuilder s = new StringBuilder();
- String exceptionMsg = exception.getMessage();
- if (exceptionMsg != null) {
- s.append(exceptionMsg);
- s.append(" - ");
- }
- s.append(exception.getClass().getSimpleName());
- StackTraceElement[] stack = exception.getStackTrace();
-
- if (stack.length > 0) {
- int count = STACK_LIMIT;
- boolean first = true;
- boolean skip = false;
- String file = "";
- s.append(" (");
- for (StackTraceElement element : stack) {
- if (count > 0 && element.getClassName().startsWith(STACK_PACKAGE)) {
- if (!first) {
- s.append(" < ");
- } else {
- first = false;
- }
-
- if (skip) {
- s.append("... < ");
- skip = false;
- }
-
- if (file.equals(element.getFileName())) {
- s.append("*");
- } else {
- file = element.getFileName();
- s.append(file.substring(0, file.length() - 5)); // remove ".java"
- count -= 1;
- }
- s.append(":").append(element.getLineNumber());
- } else {
- skip = true;
- }
- }
- if (skip) {
- if (!first) {
- s.append(" < ");
- }
- s.append("...");
- }
- s.append(")");
- }
- return s.toString();
- }
-
- /**
- * Netty logger implementation
- */
- private static class NettyInternalLogger extends AbstractInternalLogger {
-
- @Override
- public boolean isDebugEnabled() {
- return false;
- }
-
- @Override
- public boolean isInfoEnabled() {
- return false;
- }
-
- @Override
- public boolean isWarnEnabled() {
- return true;
- }
-
- @Override
- public boolean isErrorEnabled() {
- return true;
- }
-
- @Override
- public void debug(String string) {
- debug(string, null);
- }
-
- @Override
- public void debug(String string, Throwable thrwbl) {
- }
-
- @Override
- public void info(String string) {
- info(string, null);
- }
-
- @Override
- public void info(String string, Throwable thrwbl) {
- }
-
- @Override
- public void warn(String string) {
- warn(string, null);
- }
-
- @Override
- public void warn(String string, Throwable thrwbl) {
- getLogger().warn("netty warning: " + string);
- }
-
- @Override
- public void error(String string) {
- error(string, null);
- }
-
- @Override
- public void error(String string, Throwable thrwbl) {
- getLogger().error("netty error: " + string);
- }
-
- }
-
-}
diff --git a/src/org/traccar/helper/StringFinder.java b/src/org/traccar/helper/StringFinder.java
deleted file mode 100644
index 2fa0aa9a4..000000000
--- a/src/org/traccar/helper/StringFinder.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBufferIndexFinder;
-
-import java.nio.charset.StandardCharsets;
-
-public class StringFinder implements ChannelBufferIndexFinder {
-
- private String string;
-
- public StringFinder(String string) {
- this.string = string;
- }
-
- @Override
- public boolean find(ChannelBuffer buffer, int guessedIndex) {
-
- if (buffer.writerIndex() - guessedIndex < string.length()) {
- return false;
- }
-
- return string.equals(buffer.toString(guessedIndex, string.length(), StandardCharsets.US_ASCII));
- }
-
-}
diff --git a/src/org/traccar/notification/EventForwarder.java b/src/org/traccar/notification/EventForwarder.java
deleted file mode 100644
index ac37f980c..000000000
--- a/src/org/traccar/notification/EventForwarder.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.notification;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
-import org.traccar.Context;
-import org.traccar.helper.Log;
-import org.traccar.model.Device;
-import org.traccar.model.Event;
-import org.traccar.model.Geofence;
-import org.traccar.model.Position;
-
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-
-public final class EventForwarder {
-
- private String url;
- private String header;
-
- public EventForwarder() {
- url = Context.getConfig().getString("event.forward.url", "http://localhost/");
- header = Context.getConfig().getString("event.forward.header", "");
- }
-
- 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";
-
- public void forwardEvent(Event event, Position position) {
-
- BoundRequestBuilder requestBuilder = Context.getAsyncHttpClient().preparePost(url);
-
- requestBuilder.addHeader("Content-Type", "application/json; charset=utf-8");
- if (!header.equals("")) {
- String[] headerLines = header.split("\\r?\\n");
- for (String headerLine: headerLines) {
- String[] splitedLine = headerLine.split(":", 2);
- if (splitedLine.length == 2) {
- requestBuilder.setHeader(splitedLine[0].trim(), splitedLine[1].trim());
- }
- }
- }
-
- requestBuilder.setBody(preparePayload(event, position));
- requestBuilder.execute();
- }
-
- private byte[] preparePayload(Event event, Position position) {
- Map<String, Object> data = new HashMap<>();
- data.put(KEY_EVENT, event);
- if (position != null) {
- data.put(KEY_POSITION, position);
- }
- if (event.getDeviceId() != 0) {
- Device device = Context.getIdentityManager().getById(event.getDeviceId());
- if (device != null) {
- data.put(KEY_DEVICE, device);
- }
- }
- if (event.getGeofenceId() != 0) {
- Geofence geofence = (Geofence) Context.getGeofenceManager().getById(event.getGeofenceId());
- if (geofence != null) {
- data.put(KEY_GEOFENCE, geofence);
- }
- }
- try {
- return Context.getObjectMapper().writeValueAsString(data).getBytes(StandardCharsets.UTF_8);
- } catch (JsonProcessingException e) {
- Log.warning(e);
- return null;
- }
- }
-
-}
diff --git a/src/org/traccar/notification/NotificationSms.java b/src/org/traccar/notification/NotificationSms.java
deleted file mode 100644
index 8c0265af4..000000000
--- a/src/org/traccar/notification/NotificationSms.java
+++ /dev/null
@@ -1,52 +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.notification;
-
-import org.traccar.Context;
-import org.traccar.model.Event;
-import org.traccar.model.Position;
-import org.traccar.model.User;
-
-import com.cloudhopper.smpp.type.RecoverablePduException;
-import com.cloudhopper.smpp.type.SmppChannelException;
-import com.cloudhopper.smpp.type.SmppTimeoutException;
-import com.cloudhopper.smpp.type.UnrecoverablePduException;
-
-public final class NotificationSms {
-
- private NotificationSms() {
- }
-
- public static void sendSmsAsync(long userId, Event event, Position position) {
- User user = Context.getPermissionsManager().getUser(userId);
- if (Context.getSmppManager() != null && user.getPhone() != null) {
- Context.getStatisticsManager().registerSms();
- Context.getSmppManager().sendMessageAsync(user.getPhone(),
- NotificationFormatter.formatSmsMessage(userId, event, position), false);
- }
- }
-
- public static void sendSmsSync(long userId, Event event, Position position) throws RecoverablePduException,
- UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException {
- User user = Context.getPermissionsManager().getUser(userId);
- if (Context.getSmppManager() != null && user.getPhone() != null) {
- Context.getStatisticsManager().registerSms();
- Context.getSmppManager().sendMessageSync(user.getPhone(),
- NotificationFormatter.formatSmsMessage(userId, event, position), false);
- }
- }
-}
diff --git a/src/org/traccar/protocol/AdmProtocol.java b/src/org/traccar/protocol/AdmProtocol.java
deleted file mode 100644
index 4d2cbe7b3..000000000
--- a/src/org/traccar/protocol/AdmProtocol.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class AdmProtocol extends BaseProtocol {
-
- public AdmProtocol() {
- super("adm");
- setSupportedDataCommands(
- Command.TYPE_GET_DEVICE_STATUS,
- Command.TYPE_CUSTOM);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 1, -3, 0));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new AdmProtocolEncoder());
- pipeline.addLast("objectDecoder", new AdmProtocolDecoder(AdmProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/AlematicsProtocol.java b/src/org/traccar/protocol/AlematicsProtocol.java
deleted file mode 100644
index 241ce8bf9..000000000
--- a/src/org/traccar/protocol/AlematicsProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class AlematicsProtocol extends BaseProtocol {
-
- public AlematicsProtocol() {
- super("alematics");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new AlematicsFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new AlematicsProtocolDecoder(AlematicsProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ApelProtocol.java b/src/org/traccar/protocol/ApelProtocol.java
deleted file mode 100644
index 690b5ed99..000000000
--- a/src/org/traccar/protocol/ApelProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class ApelProtocol extends BaseProtocol {
-
- public ApelProtocol() {
- super("apel");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, 4, 0));
- pipeline.addLast("objectDecoder", new ApelProtocolDecoder(ApelProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/AppelloProtocol.java b/src/org/traccar/protocol/AppelloProtocol.java
deleted file mode 100644
index 8581850a1..000000000
--- a/src/org/traccar/protocol/AppelloProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class AppelloProtocol extends BaseProtocol {
-
- public AppelloProtocol() {
- super("appello");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new AppelloProtocolDecoder(AppelloProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/AquilaProtocol.java b/src/org/traccar/protocol/AquilaProtocol.java
deleted file mode 100644
index c1de71cd3..000000000
--- a/src/org/traccar/protocol/AquilaProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class AquilaProtocol extends BaseProtocol {
-
- public AquilaProtocol() {
- super("aquila");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new AquilaProtocolDecoder(AquilaProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Ardi01Protocol.java b/src/org/traccar/protocol/Ardi01Protocol.java
deleted file mode 100644
index 446592bd0..000000000
--- a/src/org/traccar/protocol/Ardi01Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Ardi01Protocol extends BaseProtocol {
-
- public Ardi01Protocol() {
- super("ardi01");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Ardi01ProtocolDecoder(Ardi01Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ArknavProtocol.java b/src/org/traccar/protocol/ArknavProtocol.java
deleted file mode 100644
index c22e5f443..000000000
--- a/src/org/traccar/protocol/ArknavProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ArknavProtocol extends BaseProtocol {
-
- public ArknavProtocol() {
- super("arknav");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new ArknavProtocolDecoder(ArknavProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ArknavX8Protocol.java b/src/org/traccar/protocol/ArknavX8Protocol.java
deleted file mode 100644
index e759b5294..000000000
--- a/src/org/traccar/protocol/ArknavX8Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ArknavX8Protocol extends BaseProtocol {
-
- public ArknavX8Protocol() {
- super("arknavx8");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new ArknavX8ProtocolDecoder(ArknavX8Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ArnaviProtocol.java b/src/org/traccar/protocol/ArnaviProtocol.java
deleted file mode 100644
index 956f2329a..000000000
--- a/src/org/traccar/protocol/ArnaviProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ArnaviProtocol extends BaseProtocol {
-
- public ArnaviProtocol() {
- super("arnavi");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new ArnaviProtocolDecoder(ArnaviProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/AstraProtocol.java b/src/org/traccar/protocol/AstraProtocol.java
deleted file mode 100644
index 87c562a9d..000000000
--- a/src/org/traccar/protocol/AstraProtocol.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class AstraProtocol extends BaseProtocol {
-
- public AstraProtocol() {
- super("astra");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 1, 2, -3, 0));
- pipeline.addLast("objectDecoder", new AstraProtocolDecoder(AstraProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new AstraProtocolDecoder(AstraProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/At2000Protocol.java b/src/org/traccar/protocol/At2000Protocol.java
deleted file mode 100644
index 35aa0b469..000000000
--- a/src/org/traccar/protocol/At2000Protocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class At2000Protocol extends BaseProtocol {
-
- public At2000Protocol() {
- super("at2000");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new At2000FrameDecoder());
- pipeline.addLast("objectDecoder", new At2000ProtocolDecoder(At2000Protocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/AtrackFrameDecoder.java b/src/org/traccar/protocol/AtrackFrameDecoder.java
deleted file mode 100644
index ce4a9a65f..000000000
--- a/src/org/traccar/protocol/AtrackFrameDecoder.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-import org.traccar.helper.StringFinder;
-
-public class AtrackFrameDecoder extends FrameDecoder {
-
- private static final int KEEPALIVE_LENGTH = 12;
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
-
- if (buf.readableBytes() >= 2) {
-
- if (buf.getUnsignedShort(buf.readerIndex()) == 0xfe02) {
-
- if (buf.readableBytes() >= KEEPALIVE_LENGTH) {
- return buf.readBytes(KEEPALIVE_LENGTH);
- }
-
- } else if (buf.getUnsignedShort(buf.readerIndex()) == 0x4050) {
-
- if (buf.readableBytes() > 6) {
- int length = buf.getUnsignedShort(buf.readerIndex() + 4) + 4 + 2;
- if (buf.readableBytes() >= length) {
- return buf.readBytes(length);
- }
- }
-
- } else {
-
- int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("\r\n"));
- if (endIndex > 0) {
- return buf.readBytes(endIndex - buf.readerIndex() + 2);
- }
-
- }
-
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/AtrackProtocol.java b/src/org/traccar/protocol/AtrackProtocol.java
deleted file mode 100644
index 5104e5587..000000000
--- a/src/org/traccar/protocol/AtrackProtocol.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.util.List;
-
-public class AtrackProtocol extends BaseProtocol {
-
- public AtrackProtocol() {
- super("atrack");
- setSupportedDataCommands(
- Command.TYPE_CUSTOM);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new AtrackFrameDecoder());
- pipeline.addLast("objectEncoder", new AtrackProtocolEncoder());
- pipeline.addLast("objectDecoder", new AtrackProtocolDecoder(AtrackProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectEncoder", new AtrackProtocolEncoder());
- pipeline.addLast("objectDecoder", new AtrackProtocolDecoder(AtrackProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java
deleted file mode 100644
index 23cb67e15..000000000
--- a/src/org/traccar/protocol/AtrackProtocolDecoder.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright 2013 - 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-public class AtrackProtocolDecoder extends BaseProtocolDecoder {
-
- private static final int MIN_DATA_LENGTH = 40;
-
- private boolean longDate;
- private boolean custom;
- private String form;
-
- private final Map<Integer, String> alarmMap = new HashMap<>();
-
- public AtrackProtocolDecoder(AtrackProtocol protocol) {
- super(protocol);
-
- longDate = Context.getConfig().getBoolean(getProtocolName() + ".longDate");
-
- custom = Context.getConfig().getBoolean(getProtocolName() + ".custom");
- form = Context.getConfig().getString(getProtocolName() + ".form");
- if (form != null) {
- custom = true;
- }
-
- for (String pair : Context.getConfig().getString(getProtocolName() + ".alarmMap", "").split(",")) {
- if (!pair.isEmpty()) {
- alarmMap.put(
- Integer.parseInt(pair.substring(0, pair.indexOf('='))), pair.substring(pair.indexOf('=') + 1));
- }
- }
- }
-
- public void setLongDate(boolean longDate) {
- this.longDate = longDate;
- }
-
- public void setCustom(boolean custom) {
- this.custom = custom;
- }
-
- private static void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) {
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(12);
- response.writeShort(0xfe02);
- response.writeLong(rawId);
- response.writeShort(index);
- channel.write(response, remoteAddress);
- }
- }
-
- private static String readString(ChannelBuffer buf) {
- String result = null;
- int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
- if (index > buf.readerIndex()) {
- result = buf.readBytes(index - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
- }
- buf.readByte();
- return result;
- }
-
- private void readCustomData(Position position, ChannelBuffer buf, String form) {
- CellTower cellTower = new CellTower();
- String[] keys = form.substring(1).split("%");
- for (String key : keys) {
- switch (key) {
- case "SA":
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- break;
- case "MV":
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
- break;
- case "BV":
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
- break;
- case "GQ":
- cellTower.setSignalStrength((int) buf.readUnsignedByte());
- break;
- case "CE":
- cellTower.setCellId(buf.readUnsignedInt());
- break;
- case "LC":
- cellTower.setLocationAreaCode(buf.readUnsignedShort());
- break;
- case "CN":
- int combinedMobileCodes = (int) (buf.readUnsignedInt() % 100000); // cccnn
- cellTower.setMobileCountryCode(combinedMobileCodes / 100);
- cellTower.setMobileNetworkCode(combinedMobileCodes % 100);
- break;
- case "RL":
- buf.readUnsignedByte(); // rxlev
- break;
- case "PC":
- position.set(Position.PREFIX_COUNT + 1, buf.readUnsignedInt());
- break;
- case "AT":
- position.setAltitude(buf.readUnsignedInt());
- break;
- case "RP":
- position.set(Position.KEY_RPM, buf.readUnsignedShort());
- break;
- case "GS":
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- break;
- case "DT":
- position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() == 1);
- break;
- case "VN":
- position.set(Position.KEY_VIN, readString(buf));
- break;
- case "MF":
- buf.readUnsignedShort(); // mass air flow rate
- break;
- case "EL":
- buf.readUnsignedByte(); // engine load
- break;
- case "TR":
- position.set(Position.KEY_THROTTLE, buf.readUnsignedByte());
- break;
- case "ET":
- position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort());
- break;
- case "FL":
- position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
- break;
- case "ML":
- buf.readUnsignedByte(); // mil status
- break;
- case "FC":
- position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
- break;
- case "CI":
- readString(buf); // format string
- break;
- case "AV1":
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- break;
- case "NC":
- readString(buf); // gsm neighbor cell info
- break;
- case "SM":
- buf.readUnsignedShort(); // max speed between reports
- break;
- case "GL":
- readString(buf); // google link
- break;
- case "MA":
- readString(buf); // mac address
- break;
- default:
- break;
- }
- }
-
- if (cellTower.getMobileCountryCode() != null
- && cellTower.getMobileNetworkCode() != null
- && cellTower.getCellId() != null
- && cellTower.getLocationAreaCode() != null) {
- position.setNetwork(new Network(cellTower));
- } else if (cellTower.getSignalStrength() != null) {
- position.set(Position.KEY_RSSI, cellTower.getSignalStrength());
- }
- }
-
- private static final Pattern PATTERN_INFO = new PatternBuilder()
- .text("$INFO=")
- .number("(d+),") // unit id
- .expression("([^,]+),") // model
- .expression("([^,]+),") // firmware version
- .number("d+,") // imei
- .number("d+,") // imsi
- .number("d+,") // sim card id
- .number("(d+),") // power
- .number("(d+),") // battery
- .number("(d+),") // satellites
- .number("d+,") // gsm status
- .number("(d+),") // rssi
- .number("d+,") // connection status
- .number("d+") // antenna status
- .any()
- .compile();
-
- private Position decodeString(Channel channel, SocketAddress remoteAddress, String sentence) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- getLastLocation(position, null);
-
- DeviceSession deviceSession;
-
- if (sentence.startsWith("$INFO")) {
-
- Parser parser = new Parser(PATTERN_INFO, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
-
- position.set("model", parser.next());
- position.set(Position.KEY_VERSION_FW, parser.next());
- position.set(Position.KEY_POWER, parser.nextInt() * 0.1);
- position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1);
- position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.set(Position.KEY_RSSI, parser.nextInt());
-
- } else {
-
- deviceSession = getDeviceSession(channel, remoteAddress);
-
- position.set(Position.KEY_RESULT, sentence);
-
- }
-
- if (deviceSession == null) {
- return null;
- } else {
- position.setDeviceId(deviceSession.getDeviceId());
- return position;
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- if (buf.getUnsignedShort(buf.readerIndex()) == 0xfe02) {
- if (channel != null) {
- channel.write(buf, remoteAddress); // keep-alive message
- }
- return null;
- } else if (buf.getByte(buf.readerIndex()) == '$') {
- return decodeString(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim());
- }
-
- buf.skipBytes(2); // prefix
- buf.readUnsignedShort(); // checksum
- buf.readUnsignedShort(); // length
- int index = buf.readUnsignedShort();
-
- long id = buf.readLong();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
- if (deviceSession == null) {
- return null;
- }
-
- sendResponse(channel, remoteAddress, id, index);
-
- List<Position> positions = new LinkedList<>();
-
- while (buf.readableBytes() >= MIN_DATA_LENGTH) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- if (longDate) {
-
- DateBuilder dateBuilder = new DateBuilder()
- .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
- position.setTime(dateBuilder.getDate());
-
- buf.skipBytes(7 + 7);
-
-
- } else {
-
- position.setFixTime(new Date(buf.readUnsignedInt() * 1000));
- position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
- buf.readUnsignedInt(); // send time
- }
-
- position.setValid(true);
- position.setLongitude(buf.readInt() * 0.000001);
- position.setLatitude(buf.readInt() * 0.000001);
- position.setCourse(buf.readUnsignedShort());
-
- int type = buf.readUnsignedByte();
- position.set(Position.KEY_TYPE, type);
- position.set(Position.KEY_ALARM, alarmMap.get(type));
-
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
- position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
- position.set(Position.KEY_INPUT, buf.readUnsignedByte());
-
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
-
- position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001);
-
- position.set(Position.KEY_DRIVER_UNIQUE_ID, readString(buf));
-
- position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1);
- position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1);
-
- position.set("message", readString(buf));
-
- if (custom) {
- String form = this.form;
- if (form == null) {
- form = readString(buf).substring("%CI".length());
- }
- readCustomData(position, buf, form);
- }
-
- positions.add(position);
-
- }
-
- return positions;
- }
-
-}
diff --git a/src/org/traccar/protocol/AuroProtocol.java b/src/org/traccar/protocol/AuroProtocol.java
deleted file mode 100644
index e1b23478f..000000000
--- a/src/org/traccar/protocol/AuroProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class AuroProtocol extends BaseProtocol {
-
- public AuroProtocol() {
- super("auro");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new AuroProtocolDecoder(AuroProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/AutoGradeProtocol.java b/src/org/traccar/protocol/AutoGradeProtocol.java
deleted file mode 100644
index 41bb8d9ad..000000000
--- a/src/org/traccar/protocol/AutoGradeProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class AutoGradeProtocol extends BaseProtocol {
-
- public AutoGradeProtocol() {
- super("autograde");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new AutoGradeProtocolDecoder(AutoGradeProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Avl301Protocol.java b/src/org/traccar/protocol/Avl301Protocol.java
deleted file mode 100644
index 4a217bad6..000000000
--- a/src/org/traccar/protocol/Avl301Protocol.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Avl301Protocol extends BaseProtocol {
-
- public Avl301Protocol() {
- super("avl301");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(256, 2, 1, -3, 0));
- pipeline.addLast("objectDecoder", new Avl301ProtocolDecoder(Avl301Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/BceProtocol.java b/src/org/traccar/protocol/BceProtocol.java
deleted file mode 100644
index 5374fd0e1..000000000
--- a/src/org/traccar/protocol/BceProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class BceProtocol extends BaseProtocol {
-
- public BceProtocol() {
- super("bce");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new BceFrameDecoder());
- pipeline.addLast("objectDecoder", new BceProtocolDecoder(BceProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/BceProtocolDecoder.java b/src/org/traccar/protocol/BceProtocolDecoder.java
deleted file mode 100644
index b472ac6c9..000000000
--- a/src/org/traccar/protocol/BceProtocolDecoder.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.ByteOrder;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-public class BceProtocolDecoder extends BaseProtocolDecoder {
-
- public BceProtocolDecoder(BceProtocol protocol) {
- super(protocol);
- }
-
- private static final int DATA_TYPE = 7;
-
- public static final int MSG_ASYNC_STACK = 0xA5;
- public static final int MSG_STACK_COFIRM = 0x19;
- public static final int MSG_TIME_TRIGGERED = 0xA0;
- public static final int MSG_OUTPUT_CONTROL = 0x41;
- public static final int MSG_OUTPUT_CONTROL_ACK = 0xC1;
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- String imei = String.format("%015d", buf.readLong());
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession == null) {
- return null;
- }
-
- List<Position> positions = new LinkedList<>();
-
- while (buf.readableBytes() > 1) {
-
- int dataEnd = buf.readUnsignedShort() + buf.readerIndex();
- int type = buf.readUnsignedByte();
- int confirmKey = buf.readUnsignedByte() & 0x7F;
-
- while (buf.readerIndex() < dataEnd) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- int structEnd = buf.readUnsignedByte() + buf.readerIndex();
-
- long time = buf.readUnsignedInt();
- if ((time & 0x0f) == DATA_TYPE) {
-
- time = time >> 4 << 1;
- time += 0x47798280; // 01/01/2008
- position.setTime(new Date(time * 1000));
-
- // Read masks
- int mask;
- List<Integer> masks = new LinkedList<>();
- do {
- mask = buf.readUnsignedShort();
- masks.add(mask);
- } while (BitUtil.check(mask, 15));
-
- mask = masks.get(0);
-
- if (BitUtil.check(mask, 0)) {
- position.setValid(true);
- position.setLongitude(buf.readFloat());
- position.setLatitude(buf.readFloat());
- position.setSpeed(buf.readUnsignedByte());
-
- int gps = buf.readUnsignedByte();
- position.set(Position.KEY_SATELLITES, gps & 0xf);
- position.set(Position.KEY_HDOP, gps >> 4);
-
- position.setCourse(buf.readUnsignedByte());
- position.setAltitude(buf.readUnsignedShort());
-
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
-
- if (BitUtil.check(mask, 1)) {
- position.set(Position.KEY_INPUT, buf.readUnsignedShort());
- }
-
- for (int i = 1; i <= 8; i++) {
- if (BitUtil.check(mask, i + 1)) {
- position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort());
- }
- }
-
- if (BitUtil.check(mask, 10)) {
- buf.skipBytes(4);
- }
- if (BitUtil.check(mask, 11)) {
- buf.skipBytes(4);
- }
- if (BitUtil.check(mask, 12)) {
- buf.skipBytes(2);
- }
- if (BitUtil.check(mask, 13)) {
- buf.skipBytes(2);
- }
-
- if (BitUtil.check(mask, 14)) {
- position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShort(), buf.readUnsignedByte(),
- buf.readUnsignedShort(), buf.readUnsignedShort(),
- buf.readUnsignedByte())));
- buf.readUnsignedByte();
- }
-
- if (BitUtil.check(mask, 0)) {
- positions.add(position);
- }
- }
-
- buf.readerIndex(structEnd);
- }
-
- // Send response
- if (type == MSG_ASYNC_STACK && channel != null) {
- ChannelBuffer response = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, 8 + 2 + 2 + 1);
- response.writeLong(Long.parseLong(imei));
- response.writeShort(2);
- response.writeByte(MSG_STACK_COFIRM);
- response.writeByte(confirmKey);
-
- int checksum = 0;
- for (int i = 0; i < response.writerIndex(); i++) {
- checksum += response.getUnsignedByte(i);
- }
- response.writeByte(checksum);
-
- channel.write(response);
- }
- }
-
- return positions;
- }
-
-}
diff --git a/src/org/traccar/protocol/BlackKiteProtocol.java b/src/org/traccar/protocol/BlackKiteProtocol.java
deleted file mode 100644
index db32f5328..000000000
--- a/src/org/traccar/protocol/BlackKiteProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Vijay Kumar (vijaykumar@zilogic.com)
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class BlackKiteProtocol extends BaseProtocol {
-
- public BlackKiteProtocol() {
- super("blackkite");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new GalileoFrameDecoder());
- pipeline.addLast("objectDecoder", new BlackKiteProtocolDecoder(BlackKiteProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/BoxProtocol.java b/src/org/traccar/protocol/BoxProtocol.java
deleted file mode 100644
index 36e7790f0..000000000
--- a/src/org/traccar/protocol/BoxProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class BoxProtocol extends BaseProtocol {
-
- public BoxProtocol() {
- super("box");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new BoxProtocolDecoder(BoxProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/CarTrackProtocol.java b/src/org/traccar/protocol/CarTrackProtocol.java
deleted file mode 100644
index d235c92be..000000000
--- a/src/org/traccar/protocol/CarTrackProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class CarTrackProtocol extends BaseProtocol {
-
- public CarTrackProtocol() {
- super("cartrack");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "##"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new CarTrackProtocolDecoder(CarTrackProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/CarscopProtocol.java b/src/org/traccar/protocol/CarscopProtocol.java
deleted file mode 100644
index 01a754027..000000000
--- a/src/org/traccar/protocol/CarscopProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class CarscopProtocol extends BaseProtocol {
-
- public CarscopProtocol() {
- super("carscop");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '^'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new CarscopProtocolDecoder(CarscopProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/CastelProtocol.java b/src/org/traccar/protocol/CastelProtocol.java
deleted file mode 100644
index db9df0674..000000000
--- a/src/org/traccar/protocol/CastelProtocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class CastelProtocol extends BaseProtocol {
-
- public CastelProtocol() {
- super("castel");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
- pipeline.addLast("objectDecoder", new CastelProtocolDecoder(CastelProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
-
- server = new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new CastelProtocolDecoder(CastelProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java
deleted file mode 100644
index 3a0ccea78..000000000
--- a/src/org/traccar/protocol/CastelProtocolDecoder.java
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.Checksum;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.ObdDecoder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-public class CastelProtocolDecoder extends BaseProtocolDecoder {
-
- private static final Map<Integer, Integer> PID_LENGTH_MAP = new HashMap<>();
-
- static {
- int[] l1 = {
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0b, 0x0d,
- 0x0e, 0x0f, 0x11, 0x12, 0x13, 0x1c, 0x1d, 0x1e, 0x2c,
- 0x2d, 0x2e, 0x2f, 0x30, 0x33, 0x43, 0x45, 0x46,
- 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x51, 0x52,
- 0x5a
- };
- int[] l2 = {
- 0x02, 0x03, 0x0a, 0x0c, 0x10, 0x14, 0x15, 0x16,
- 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1f, 0x21, 0x22,
- 0x23, 0x31, 0x32, 0x3c, 0x3d, 0x3e, 0x3f, 0x42,
- 0x44, 0x4d, 0x4e, 0x50, 0x53, 0x54, 0x55, 0x56,
- 0x57, 0x58, 0x59
- };
- int[] l4 = {
- 0x00, 0x01, 0x20, 0x24, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x2b, 0x34, 0x35, 0x36, 0x37, 0x38,
- 0x39, 0x3a, 0x3b, 0x40, 0x41, 0x4f
- };
- for (int i : l1) {
- PID_LENGTH_MAP.put(i, 1);
- }
- for (int i : l2) {
- PID_LENGTH_MAP.put(i, 2);
- }
- for (int i : l4) {
- PID_LENGTH_MAP.put(i, 4);
- }
- }
-
- public CastelProtocolDecoder(CastelProtocol protocol) {
- super(protocol);
- }
-
- public static final short MSG_SC_LOGIN = 0x1001;
- public static final short MSG_SC_LOGIN_RESPONSE = (short) 0x9001;
- public static final short MSG_SC_LOGOUT = 0x1002;
- public static final short MSG_SC_HEARTBEAT = 0x1003;
- public static final short MSG_SC_HEARTBEAT_RESPONSE = (short) 0x9003;
- public static final short MSG_SC_GPS = 0x4001;
- public static final short MSG_SC_PID_DATA = 0x4002;
- public static final short MSG_SC_SUPPORTED_PID = 0x4004;
- public static final short MSG_SC_OBD_DATA = 0x4005;
- public static final short MSG_SC_DTCS_PASSENGER = 0x4006;
- public static final short MSG_SC_DTCS_COMMERCIAL = 0x400B;
- public static final short MSG_SC_ALARM = 0x4007;
- public static final short MSG_SC_CELL = 0x4008;
- public static final short MSG_SC_GPS_SLEEP = 0x4009;
- public static final short MSG_SC_AGPS_REQUEST = 0x5101;
- public static final short MSG_SC_CURRENT_LOCATION = (short) 0xB001;
-
- public static final short MSG_CC_LOGIN = 0x4001;
- public static final short MSG_CC_LOGIN_RESPONSE = (short) 0x8001;
- public static final short MSG_CC_HEARTBEAT = 0x4206;
- public static final short MSG_CC_HEARTBEAT_RESPONSE = (short) 0x8206;
-
- private Position readPosition(DeviceSession deviceSession, ChannelBuffer buf) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- DateBuilder dateBuilder = new DateBuilder()
- .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
- position.setTime(dateBuilder.getDate());
-
- double lat = buf.readUnsignedInt() / 3600000.0;
- double lon = buf.readUnsignedInt() / 3600000.0;
- position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShort()));
- position.setCourse(buf.readUnsignedShort() * 0.1);
-
- int flags = buf.readUnsignedByte();
- if ((flags & 0x02) == 0) {
- lat = -lat;
- }
- if ((flags & 0x01) == 0) {
- lon = -lon;
- }
- position.setLatitude(lat);
- position.setLongitude(lon);
- position.setValid((flags & 0x0C) > 0);
- position.set(Position.KEY_SATELLITES, flags >> 4);
-
- return position;
- }
-
- private Position createPosition(DeviceSession deviceSession) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- getLastLocation(position, null);
-
- return position;
- }
-
- private void decodeObd(Position position, ChannelBuffer buf, boolean groups) {
-
- int count = buf.readUnsignedByte();
-
- int[] pids = new int[count];
- for (int i = 0; i < count; i++) {
- pids[i] = buf.readUnsignedShort() & 0xff;
- }
-
- if (groups) {
- buf.readUnsignedByte(); // group count
- buf.readUnsignedByte(); // group size
- }
-
- for (int i = 0; i < count; i++) {
- int value;
- switch (PID_LENGTH_MAP.get(pids[i])) {
- case 1:
- value = buf.readUnsignedByte();
- break;
- case 2:
- value = buf.readUnsignedShort();
- break;
- case 4:
- value = buf.readInt();
- break;
- default:
- value = 0;
- break;
- }
- position.add(ObdDecoder.decodeData(pids[i], value, false));
- }
- }
-
- private void decodeStat(Position position, ChannelBuffer buf) {
-
- buf.readUnsignedInt(); // ACC ON time
- buf.readUnsignedInt(); // UTC time
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
- position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
- buf.readUnsignedShort(); // current fuel consumption
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
- buf.skipBytes(8);
- }
-
- private void sendResponse(
- Channel channel, SocketAddress remoteAddress,
- int version, ChannelBuffer id, short type, ChannelBuffer content) {
-
- if (channel != null) {
- int length = 2 + 2 + 1 + id.readableBytes() + 2 + 2 + 2;
- if (content != null) {
- length += content.readableBytes();
- }
-
- ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length);
- response.writeByte('@'); response.writeByte('@');
- response.writeShort(length);
- response.writeByte(version);
- response.writeBytes(id);
- response.writeShort(ChannelBuffers.swapShort(type));
- if (content != null) {
- response.writeBytes(content);
- }
- response.writeShort(
- Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex())));
- response.writeByte(0x0D); response.writeByte(0x0A);
- channel.write(response, remoteAddress);
- }
- }
-
- private void sendResponse(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer id, short type) {
-
- if (channel != null) {
- int length = 2 + 2 + id.readableBytes() + 2 + 4 + 8 + 2 + 2;
-
- ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length);
- response.writeByte('@'); response.writeByte('@');
- response.writeShort(length);
- response.writeBytes(id);
- response.writeShort(ChannelBuffers.swapShort(type));
- response.writeInt(0);
- for (int i = 0; i < 8; i++) {
- response.writeByte(0xff);
- }
- response.writeShort(
- Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex())));
- response.writeByte(0x0D); response.writeByte(0x0A);
- channel.write(response, remoteAddress);
- }
- }
-
- private Object decodeSc(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer buf,
- int version, ChannelBuffer id, int type, DeviceSession deviceSession) {
-
- if (type == MSG_SC_HEARTBEAT) {
-
- sendResponse(channel, remoteAddress, version, id, MSG_SC_HEARTBEAT_RESPONSE, null);
-
- } else if (type == MSG_SC_LOGIN || type == MSG_SC_LOGOUT || type == MSG_SC_GPS
- || type == MSG_SC_ALARM || type == MSG_SC_CURRENT_LOCATION) {
-
- if (type == MSG_SC_LOGIN) {
- ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 10);
- response.writeInt(0xFFFFFFFF);
- response.writeShort(0);
- response.writeInt((int) (System.currentTimeMillis() / 1000));
- sendResponse(channel, remoteAddress, version, id, MSG_SC_LOGIN_RESPONSE, response);
- }
-
- if (type == MSG_SC_GPS) {
- buf.readUnsignedByte(); // historical
- } else if (type == MSG_SC_ALARM) {
- buf.readUnsignedInt(); // alarm
- } else if (type == MSG_SC_CURRENT_LOCATION) {
- buf.readUnsignedShort();
- }
-
- buf.readUnsignedInt(); // ACC ON time
- buf.readUnsignedInt(); // UTC time
- long odometer = buf.readUnsignedInt();
- long tripOdometer = buf.readUnsignedInt();
- long fuelConsumption = buf.readUnsignedInt();
- buf.readUnsignedShort(); // current fuel consumption
- long status = buf.readUnsignedInt();
- buf.skipBytes(8);
-
- int count = buf.readUnsignedByte();
-
- List<Position> positions = new LinkedList<>();
-
- for (int i = 0; i < count; i++) {
- Position position = readPosition(deviceSession, buf);
- position.set(Position.KEY_ODOMETER, odometer);
- position.set(Position.KEY_ODOMETER_TRIP, tripOdometer);
- position.set(Position.KEY_FUEL_CONSUMPTION, fuelConsumption);
- position.set(Position.KEY_STATUS, status);
- positions.add(position);
- }
-
- if (!positions.isEmpty()) {
- return positions;
- }
-
- } else if (type == MSG_SC_GPS_SLEEP) {
-
- buf.readUnsignedInt(); // device time
-
- return readPosition(deviceSession, buf);
-
- } else if (type == MSG_SC_AGPS_REQUEST) {
-
- return readPosition(deviceSession, buf);
-
- } else if (type == MSG_SC_PID_DATA) {
-
- Position position = createPosition(deviceSession);
-
- decodeStat(position, buf);
-
- buf.readUnsignedShort(); // sample rate
- decodeObd(position, buf, true);
-
- return position;
-
- } else if (type == MSG_SC_DTCS_PASSENGER) {
-
- Position position = createPosition(deviceSession);
-
- decodeStat(position, buf);
-
- buf.readUnsignedByte(); // flag
- position.add(ObdDecoder.decodeCodes(ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedByte()))));
-
- return position;
-
- } else if (type == MSG_SC_OBD_DATA) {
-
- Position position = createPosition(deviceSession);
-
- decodeStat(position, buf);
-
- buf.readUnsignedByte(); // flag
- decodeObd(position, buf, false);
-
- return position;
-
- } else if (type == MSG_SC_CELL) {
-
- Position position = createPosition(deviceSession);
-
- decodeStat(position, buf);
-
- position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
-
- return position;
-
- }
-
- return null;
- }
-
-
- private Object decodeCc(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer buf,
- int version, ChannelBuffer id, int type, DeviceSession deviceSession) {
-
- if (type == MSG_CC_HEARTBEAT) {
-
- sendResponse(channel, remoteAddress, version, id, MSG_CC_HEARTBEAT_RESPONSE, null);
-
- buf.readUnsignedByte(); // 0x01 for history
- int count = buf.readUnsignedByte();
-
- List<Position> positions = new LinkedList<>();
-
- for (int i = 0; i < count; i++) {
- Position position = readPosition(deviceSession, buf);
-
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
-
- buf.readUnsignedByte(); // geo-fencing id
- buf.readUnsignedByte(); // geo-fencing flags
- buf.readUnsignedByte(); // additional flags
-
- position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
-
- positions.add(position);
- }
-
- return positions;
-
- } else if (type == MSG_CC_LOGIN) {
-
- sendResponse(channel, remoteAddress, version, id, MSG_CC_LOGIN_RESPONSE, null);
-
- Position position = readPosition(deviceSession, buf);
-
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
-
- buf.readUnsignedByte(); // geo-fencing id
- buf.readUnsignedByte(); // geo-fencing flags
- buf.readUnsignedByte(); // additional flags
-
- // GSM_CELL_CODE
- // STR_Z - firmware version
- // STR_Z - hardware version
-
- return position;
-
- }
-
- return null;
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- int header = buf.readUnsignedShort();
- buf.readUnsignedShort(); // length
-
- int version = -1;
- if (header == 0x4040) {
- version = buf.readUnsignedByte();
- }
-
- ChannelBuffer id = buf.readBytes(20);
- int type = ChannelBuffers.swapShort(buf.readShort());
-
- DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, id.toString(StandardCharsets.US_ASCII).trim());
- if (deviceSession == null) {
- return null;
- }
-
- if (version == -1) {
-
- if (type == 0x2001) {
-
- sendResponse(channel, remoteAddress, id, (short) 0x1001);
-
- buf.readUnsignedInt(); // index
- buf.readUnsignedInt(); // unix time
- buf.readUnsignedByte();
-
- return readPosition(deviceSession, buf);
-
- }
-
- } else if (version == 3 || version == 4) {
-
- return decodeSc(channel, remoteAddress, buf, version, id, type, deviceSession);
-
- } else {
-
- return decodeCc(channel, remoteAddress, buf, version, id, type, deviceSession);
-
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/CellocatorProtocol.java b/src/org/traccar/protocol/CellocatorProtocol.java
deleted file mode 100644
index 4a20bc977..000000000
--- a/src/org/traccar/protocol/CellocatorProtocol.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class CellocatorProtocol extends BaseProtocol {
-
- public CellocatorProtocol() {
- super("cellocator");
- setSupportedDataCommands(
- Command.TYPE_OUTPUT_CONTROL);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CellocatorFrameDecoder());
- pipeline.addLast("objectEncoder", new CellocatorProtocolEncoder());
- pipeline.addLast("objectDecoder", new CellocatorProtocolDecoder(CellocatorProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
-
- server = new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectEncoder", new CellocatorProtocolEncoder());
- pipeline.addLast("objectDecoder", new CellocatorProtocolDecoder(CellocatorProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/org/traccar/protocol/CellocatorProtocolDecoder.java
deleted file mode 100644
index 7ef013e28..000000000
--- a/src/org/traccar/protocol/CellocatorProtocolDecoder.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2013 - 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.ByteOrder;
-
-public class CellocatorProtocolDecoder extends BaseProtocolDecoder {
-
- public CellocatorProtocolDecoder(CellocatorProtocol protocol) {
- super(protocol);
- }
-
- static final int MSG_CLIENT_STATUS = 0;
- static final int MSG_CLIENT_PROGRAMMING = 3;
- static final int MSG_CLIENT_SERIAL_LOG = 7;
- static final int MSG_CLIENT_SERIAL = 8;
- static final int MSG_CLIENT_MODULAR = 9;
-
- public static final int MSG_SERVER_ACKNOWLEDGE = 4;
-
- private byte commandCount;
-
- private void sendReply(Channel channel, SocketAddress remoteAddress, long deviceId, byte packetNumber) {
- ChannelBuffer reply = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 28);
- reply.writeByte('M');
- reply.writeByte('C');
- reply.writeByte('G');
- reply.writeByte('P');
- reply.writeByte(MSG_SERVER_ACKNOWLEDGE);
- reply.writeInt((int) deviceId);
- reply.writeByte(commandCount++);
- reply.writeInt(0); // authentication code
- reply.writeByte(0);
- reply.writeByte(packetNumber);
- reply.writeZero(11);
-
- byte checksum = 0;
- for (int i = 4; i < 27; i++) {
- checksum += reply.getByte(i);
- }
- reply.writeByte(checksum);
-
- if (channel != null) {
- channel.write(reply, remoteAddress);
- }
- }
-
- private String decodeAlarm(short reason) {
- switch (reason) {
- case 70:
- return Position.ALARM_SOS;
- case 80:
- return Position.ALARM_POWER_CUT;
- case 81:
- return Position.ALARM_LOW_POWER;
- default:
- return null;
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.skipBytes(4); // system code
- int type = buf.readUnsignedByte();
- long deviceUniqueId = buf.readUnsignedInt();
-
- if (type != MSG_CLIENT_SERIAL) {
- buf.readUnsignedShort(); // communication control
- }
- byte packetNumber = buf.readByte();
-
- sendReply(channel, remoteAddress, deviceUniqueId, packetNumber);
-
- if (type == MSG_CLIENT_STATUS) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceUniqueId));
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_VERSION_HW, buf.readUnsignedByte());
- position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
- position.set("protocolVersion", buf.readUnsignedByte());
-
- position.set(Position.KEY_STATUS, buf.getUnsignedByte(buf.readerIndex()) & 0x0f);
-
- int operator = (buf.readUnsignedByte() & 0xf0) << 4;
- operator += buf.readUnsignedByte();
-
- buf.readUnsignedByte(); // reason data
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
-
- position.set("mode", buf.readUnsignedByte());
- position.set(Position.PREFIX_IO + 1, buf.readUnsignedInt());
-
- operator <<= 8;
- operator += buf.readUnsignedByte();
- position.set(Position.KEY_OPERATOR, operator);
-
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedInt());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
- buf.skipBytes(6); // multi-purpose data
-
- position.set(Position.KEY_GPS, buf.readUnsignedShort());
- position.set("locationStatus", buf.readUnsignedByte());
- position.set("mode1", buf.readUnsignedByte());
- position.set("mode2", buf.readUnsignedByte());
-
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
-
- position.setValid(true);
- position.setLongitude(buf.readInt() / Math.PI * 180 / 100000000);
- position.setLatitude(buf.readInt() / Math.PI * 180 / 100000000.0);
- position.setAltitude(buf.readInt() * 0.01);
- position.setSpeed(UnitsConverter.knotsFromMps(buf.readInt() * 0.01));
- position.setCourse(buf.readUnsignedShort() / Math.PI * 180.0 / 1000.0);
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTimeReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedShort());
- position.setTime(dateBuilder.getDate());
-
- return position;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/CguardProtocol.java b/src/org/traccar/protocol/CguardProtocol.java
deleted file mode 100644
index 460bd331f..000000000
--- a/src/org/traccar/protocol/CguardProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class CguardProtocol extends BaseProtocol {
-
- public CguardProtocol() {
- super("cguard");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new CguardProtocolDecoder(CguardProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/CradlepointProtocol.java b/src/org/traccar/protocol/CradlepointProtocol.java
deleted file mode 100644
index 6ed54aa17..000000000
--- a/src/org/traccar/protocol/CradlepointProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class CradlepointProtocol extends BaseProtocol {
-
- public CradlepointProtocol() {
- super("cradlepoint");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new CradlepointProtocolDecoder(CradlepointProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/DishaProtocol.java b/src/org/traccar/protocol/DishaProtocol.java
deleted file mode 100644
index 53ba36004..000000000
--- a/src/org/traccar/protocol/DishaProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class DishaProtocol extends BaseProtocol {
-
- public DishaProtocol() {
- super("disha");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new DishaProtocolDecoder(DishaProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/DmtHttpProtocol.java b/src/org/traccar/protocol/DmtHttpProtocol.java
deleted file mode 100644
index 2378441ba..000000000
--- a/src/org/traccar/protocol/DmtHttpProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class DmtHttpProtocol extends BaseProtocol {
-
- public DmtHttpProtocol() {
- super("dmthttp");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("httpAggregator", new HttpChunkAggregator(65535));
- pipeline.addLast("objectDecoder", new DmtHttpProtocolDecoder(DmtHttpProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/DmtProtocol.java b/src/org/traccar/protocol/DmtProtocol.java
deleted file mode 100644
index 18bb1524a..000000000
--- a/src/org/traccar/protocol/DmtProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class DmtProtocol extends BaseProtocol {
-
- public DmtProtocol() {
- super("dmt");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2));
- pipeline.addLast("objectDecoder", new DmtProtocolDecoder(DmtProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/DmtProtocolDecoder.java b/src/org/traccar/protocol/DmtProtocolDecoder.java
deleted file mode 100644
index 39462a469..000000000
--- a/src/org/traccar/protocol/DmtProtocolDecoder.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-
-public class DmtProtocolDecoder extends BaseProtocolDecoder {
-
- public DmtProtocolDecoder(DmtProtocol protocol) {
- super(protocol);
- }
-
- public static final int MSG_HELLO = 0x00;
- public static final int MSG_HELLO_RESPONSE = 0x01;
- public static final int MSG_DATA_RECORD = 0x04;
- public static final int MSG_COMMIT = 0x05;
- public static final int MSG_COMMIT_RESPONSE = 0x06;
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.skipBytes(2); // header
-
- int type = buf.readUnsignedByte();
-
- buf.readUnsignedShort(); // length
-
- if (type == MSG_HELLO) {
-
- buf.readUnsignedInt(); // device serial number
-
- DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
-
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
- response.writeByte(0x02); response.writeByte(0x55); // header
- response.writeByte(MSG_HELLO_RESPONSE);
- response.writeShort(4 + 4);
- response.writeInt((int) (System.currentTimeMillis() / 1000));
- response.writeInt(deviceSession != null ? 0 : 1); // flags
- channel.write(response);
- }
-
- } else if (type == MSG_COMMIT) {
-
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
- response.writeByte(0x02); response.writeByte(0x55); // header
- response.writeByte(MSG_COMMIT_RESPONSE);
- response.writeShort(1);
- response.writeByte(1); // flags (success)
- channel.write(response);
- }
-
- } else if (type == MSG_DATA_RECORD) {
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null) {
- return null;
- }
-
- List<Position> positions = new LinkedList<>();
-
- while (buf.readable()) {
-
- int recordEnd = buf.readerIndex() + buf.readUnsignedShort();
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_INDEX, buf.readUnsignedInt());
-
- position.setDeviceTime(new Date(1356998400000L + buf.readUnsignedInt() * 1000)); // since 1 Jan 2013
-
- position.set(Position.KEY_EVENT, buf.readUnsignedByte());
-
- while (buf.readerIndex() < recordEnd) {
-
- int fieldId = buf.readUnsignedByte();
- int fieldLength = buf.readUnsignedByte();
- int fieldEnd = buf.readerIndex() + (fieldLength == 255 ? buf.readUnsignedShort() : fieldLength);
-
- if (fieldId == 0) {
-
- position.setFixTime(new Date(1356998400000L + buf.readUnsignedInt() * 1000));
- position.setLatitude(buf.readInt() * 0.0000001);
- position.setLongitude(buf.readInt() * 0.0000001);
- position.setAltitude(buf.readShort());
- position.setSpeed(UnitsConverter.knotsFromCps(buf.readUnsignedShort()));
-
- buf.readUnsignedByte(); // speed accuracy
-
- position.setCourse(buf.readUnsignedByte() * 2);
-
- position.set(Position.KEY_PDOP, buf.readUnsignedByte() * 0.1);
-
- position.setAccuracy(buf.readUnsignedByte());
- position.setValid(buf.readUnsignedByte() != 0);
-
- } else if (fieldId == 2) {
-
- int input = buf.readInt();
- int output = buf.readUnsignedShort();
- int status = buf.readUnsignedShort();
-
- 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);
-
- } else if (fieldId == 6) {
-
- while (buf.readerIndex() < fieldEnd) {
- switch (buf.readUnsignedByte()) {
- case 1:
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
- break;
- case 2:
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
- break;
- case 3:
- position.set(Position.KEY_DEVICE_TEMP, buf.readShort() * 0.01);
- break;
- case 4:
- position.set(Position.KEY_RSSI, buf.readUnsignedShort());
- break;
- case 5:
- position.set("solarPower", buf.readUnsignedShort() * 0.001);
- break;
- default:
- break;
- }
- }
-
- }
-
- buf.readerIndex(fieldEnd);
-
- }
-
- if (position.getFixTime() == null) {
- getLastLocation(position, position.getDeviceTime());
- }
-
- positions.add(position);
-
- }
-
- return positions;
-
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/DwayProtocol.java b/src/org/traccar/protocol/DwayProtocol.java
deleted file mode 100644
index 151d3fe01..000000000
--- a/src/org/traccar/protocol/DwayProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class DwayProtocol extends BaseProtocol {
-
- public DwayProtocol() {
- super("dway");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new DwayProtocolDecoder(DwayProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/EasyTrackProtocol.java b/src/org/traccar/protocol/EasyTrackProtocol.java
deleted file mode 100644
index eeed07129..000000000
--- a/src/org/traccar/protocol/EasyTrackProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class EasyTrackProtocol extends BaseProtocol {
-
- public EasyTrackProtocol() {
- super("easytrack");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "#", "\r\n"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new EasyTrackProtocolDecoder(EasyTrackProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/EelinkProtocolEncoder.java b/src/org/traccar/protocol/EelinkProtocolEncoder.java
deleted file mode 100644
index 5a28733f7..000000000
--- a/src/org/traccar/protocol/EelinkProtocolEncoder.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-
-import java.nio.charset.StandardCharsets;
-
-public class EelinkProtocolEncoder extends BaseProtocolEncoder {
-
- private ChannelBuffer encodeContent(String content) {
-
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
-
- buf.writeByte(0x67);
- buf.writeByte(0x67);
- buf.writeByte(EelinkProtocolDecoder.MSG_DOWNLINK);
- buf.writeShort(2 + 1 + 4 + content.length()); // length
- buf.writeShort(0); // index
-
- buf.writeByte(0x01); // command
- buf.writeInt(0); // server id
- buf.writeBytes(content.getBytes(StandardCharsets.UTF_8));
-
- return buf;
- }
-
- @Override
- protected Object encodeCommand(Command command) {
-
- switch (command.getType()) {
- case Command.TYPE_CUSTOM:
- return encodeContent(command.getString(Command.KEY_DATA));
- case Command.TYPE_ENGINE_STOP:
- return encodeContent("RELAY,1#");
- case Command.TYPE_ENGINE_RESUME:
- return encodeContent("RELAY,0#");
- case Command.TYPE_REBOOT_DEVICE:
- return encodeContent("RESET#");
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/EskyProtocol.java b/src/org/traccar/protocol/EskyProtocol.java
deleted file mode 100644
index 4c1d11f7d..000000000
--- a/src/org/traccar/protocol/EskyProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class EskyProtocol extends BaseProtocol {
-
- public EskyProtocol() {
- super("esky");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new EskyFrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new EskyProtocolDecoder(EskyProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ExtremTracProtocol.java b/src/org/traccar/protocol/ExtremTracProtocol.java
deleted file mode 100644
index d9b178e23..000000000
--- a/src/org/traccar/protocol/ExtremTracProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ExtremTracProtocol extends BaseProtocol {
-
- public ExtremTracProtocol() {
- super("extremtrac");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new ExtremTracProtocolDecoder(ExtremTracProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/org/traccar/protocol/FifotrackProtocolDecoder.java
deleted file mode 100644
index 304f6a2c3..000000000
--- a/src/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.util.regex.Pattern;
-
-public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
-
- public FifotrackProtocolDecoder(FifotrackProtocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("$$")
- .number("d+,") // length
- .number("(d+),") // imei
- .number("x+,") // index
- .expression("[^,]+,") // type
- .number("(d+)?,") // alarm
- .number("(dd)(dd)(dd)") // date (yymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("([AV]),") // validity
- .number("(-?d+.d+),") // latitude
- .number("(-?d+.d+),") // longitude
- .number("(d+),") // speed
- .number("(d+),") // course
- .number("(-?d+),") // altitude
- .number("(d+),") // odometer
- .number("d+,") // runtime
- .number("(xxxx),") // status
- .number("(x+)?,") // input
- .number("(x+)?,") // output
- .number("(d+)|") // mcc
- .number("(d+)|") // mnc
- .number("(x+)|") // lac
- .number("(x+),") // cid
- .number("([x|]+)") // adc
- .expression(",([^,]+)") // rfid
- .expression(",([^*]+)").optional(2) // sensors
- .any()
- .compile();
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- Parser parser = new Parser(PATTERN, (String) msg);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_ALARM, parser.next());
-
- position.setTime(parser.nextDateTime());
-
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt(0)));
- position.setCourse(parser.nextInt(0));
- position.setAltitude(parser.nextInt(0));
-
- position.set(Position.KEY_ODOMETER, parser.nextLong(0));
- position.set(Position.KEY_STATUS, parser.nextHexInt(0));
- if (parser.hasNext()) {
- position.set(Position.KEY_INPUT, parser.nextHexInt(0));
- }
- if (parser.hasNext()) {
- position.set(Position.KEY_OUTPUT, parser.nextHexInt(0));
- }
-
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
-
- String[] adc = parser.next().split("\\|");
- for (int i = 0; i < adc.length; i++) {
- position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16));
- }
-
- position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
-
- if (parser.hasNext()) {
- String[] sensors = parser.next().split("\\|");
- for (int i = 0; i < sensors.length; i++) {
- position.set(Position.PREFIX_IO + (i + 1), sensors[i]);
- }
- }
-
- return position;
- }
-
-}
diff --git a/src/org/traccar/protocol/FlespiProtocol.java b/src/org/traccar/protocol/FlespiProtocol.java
deleted file mode 100644
index d22bd7ae0..000000000
--- a/src/org/traccar/protocol/FlespiProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class FlespiProtocol extends BaseProtocol {
-
- public FlespiProtocol() {
- super("flespi");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("httpAggregator", new HttpChunkAggregator(Integer.MAX_VALUE));
- pipeline.addLast("objectDecoder", new FlespiProtocolDecoder(FlespiProtocol.this));
- }
- });
- }
-}
diff --git a/src/org/traccar/protocol/FlespiProtocolDecoder.java b/src/org/traccar/protocol/FlespiProtocolDecoder.java
deleted file mode 100644
index 799d78ecb..000000000
--- a/src/org/traccar/protocol/FlespiProtocolDecoder.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
-import org.jboss.netty.handler.codec.http.HttpHeaders;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.model.Position;
-
-import javax.json.Json;
-import javax.json.JsonArray;
-import javax.json.JsonNumber;
-import javax.json.JsonObject;
-import javax.json.JsonString;
-import java.io.StringReader;
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.Date;
-
-public class FlespiProtocolDecoder extends BaseProtocolDecoder {
-
- public FlespiProtocolDecoder(FlespiProtocol protocol) {
- super(protocol);
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- HttpRequest request = (HttpRequest) msg;
- JsonArray result = Json.createReader(new StringReader(request.getContent().toString(StandardCharsets.UTF_8)))
- .readArray();
- 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) {
- continue;
- }
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ident.getString());
- if (deviceSession == null) {
- continue;
- }
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- decodePosition(message, position);
- positions.add(position);
- }
-
- sendResponse(channel, HttpResponseStatus.OK);
- return positions;
- }
-
- private void sendResponse(Channel channel, HttpResponseStatus status) {
- if (channel != null) {
- HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
- response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0);
- channel.write(response);
- }
- }
-
- private void decodePosition(JsonObject object, Position position) {
- position.setProtocol(getProtocolName());
-
- Date deviceTime = new Date((long) object.getJsonNumber("timestamp").doubleValue() * 1000);
- position.setTime(deviceTime);
- JsonNumber lat = object.getJsonNumber("position.latitude");
- JsonNumber lon = object.getJsonNumber("position.longitude");
- if (lat != null && lon != null) {
- position.setLatitude(lat.doubleValue());
- position.setLongitude(lon.doubleValue());
- } else {
- getLastLocation(position, deviceTime);
- }
- JsonNumber speed = object.getJsonNumber("position.speed");
- if (speed != null) {
- position.setSpeed(speed.doubleValue());
- }
- JsonNumber course = object.getJsonNumber("position.direction");
- if (course != null) {
- position.setCourse(course.doubleValue());
- }
- JsonNumber altitude = object.getJsonNumber("position.altitude");
- if (altitude != null) {
- position.setAltitude(altitude.doubleValue());
- }
-
- position.setValid(object.getBoolean("position.valid", true));
- position.set(Position.KEY_SATELLITES, object.getInt("position.satellites", 0));
-
- if (object.getBoolean("alarm.event.trigger", false)) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
- }
-}
diff --git a/src/org/traccar/protocol/FlexCommProtocol.java b/src/org/traccar/protocol/FlexCommProtocol.java
deleted file mode 100644
index 5dba4421a..000000000
--- a/src/org/traccar/protocol/FlexCommProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.FixedLengthFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class FlexCommProtocol extends BaseProtocol {
-
- public FlexCommProtocol() {
- super("flexcomm");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new FixedLengthFrameDecoder(2 + 2 + 101 + 5));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new FlexCommProtocolDecoder(FlexCommProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/FlextrackProtocol.java b/src/org/traccar/protocol/FlextrackProtocol.java
deleted file mode 100644
index 77e316d82..000000000
--- a/src/org/traccar/protocol/FlextrackProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class FlextrackProtocol extends BaseProtocol {
-
- public FlextrackProtocol() {
- super("flextrack");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r"));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new FlextrackProtocolDecoder(FlextrackProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/FoxProtocol.java b/src/org/traccar/protocol/FoxProtocol.java
deleted file mode 100644
index 501bff4c4..000000000
--- a/src/org/traccar/protocol/FoxProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class FoxProtocol extends BaseProtocol {
-
- public FoxProtocol() {
- super("fox");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "</fox>"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new FoxProtocolDecoder(FoxProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/FreedomProtocol.java b/src/org/traccar/protocol/FreedomProtocol.java
deleted file mode 100644
index 5b4bf22ff..000000000
--- a/src/org/traccar/protocol/FreedomProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class FreedomProtocol extends BaseProtocol {
-
- public FreedomProtocol() {
- super("freedom");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new FreedomProtocolDecoder(FreedomProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GatorProtocol.java b/src/org/traccar/protocol/GatorProtocol.java
deleted file mode 100644
index 7fa4854d3..000000000
--- a/src/org/traccar/protocol/GatorProtocol.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GatorProtocol extends BaseProtocol {
-
- public GatorProtocol() {
- super("gator");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2));
- pipeline.addLast("objectDecoder", new GatorProtocolDecoder(GatorProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new GatorProtocolDecoder(GatorProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GenxProtocol.java b/src/org/traccar/protocol/GenxProtocol.java
deleted file mode 100644
index 2b5b1a43d..000000000
--- a/src/org/traccar/protocol/GenxProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GenxProtocol extends BaseProtocol {
-
- public GenxProtocol() {
- super("genx");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new GenxProtocolDecoder(GenxProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Gl100Protocol.java b/src/org/traccar/protocol/Gl100Protocol.java
deleted file mode 100644
index 0fd18d44f..000000000
--- a/src/org/traccar/protocol/Gl100Protocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Gl100Protocol extends BaseProtocol {
-
- public Gl100Protocol() {
- super("gl100");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\0'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Gl100ProtocolDecoder(Gl100Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Gl100ProtocolDecoder(Gl100Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Gl200Protocol.java b/src/org/traccar/protocol/Gl200Protocol.java
deleted file mode 100644
index 799d7fe36..000000000
--- a/src/org/traccar/protocol/Gl200Protocol.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.util.List;
-
-public class Gl200Protocol extends BaseProtocol {
-
- public Gl200Protocol() {
- super("gl200");
- setSupportedDataCommands(
- Command.TYPE_POSITION_SINGLE,
- Command.TYPE_ENGINE_STOP,
- Command.TYPE_ENGINE_RESUME,
- Command.TYPE_IDENTIFICATION,
- Command.TYPE_REBOOT_DEVICE);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Gl200FrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GlobalSatProtocol.java b/src/org/traccar/protocol/GlobalSatProtocol.java
deleted file mode 100644
index f3d07fc96..000000000
--- a/src/org/traccar/protocol/GlobalSatProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GlobalSatProtocol extends BaseProtocol {
-
- public GlobalSatProtocol() {
- super("globalsat");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '!'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new GlobalSatProtocolDecoder(GlobalSatProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GnxProtocol.java b/src/org/traccar/protocol/GnxProtocol.java
deleted file mode 100644
index 84af24000..000000000
--- a/src/org/traccar/protocol/GnxProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GnxProtocol extends BaseProtocol {
-
- public GnxProtocol() {
- super("gnx");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\n\r"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new GnxProtocolDecoder(GnxProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GoSafeProtocol.java b/src/org/traccar/protocol/GoSafeProtocol.java
deleted file mode 100644
index bfd473df9..000000000
--- a/src/org/traccar/protocol/GoSafeProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GoSafeProtocol extends BaseProtocol {
-
- public GoSafeProtocol() {
- super("gosafe");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new GoSafeProtocolDecoder(GoSafeProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/org/traccar/protocol/GoSafeProtocolDecoder.java
deleted file mode 100644
index f9aaae0ce..000000000
--- a/src/org/traccar/protocol/GoSafeProtocolDecoder.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Pattern;
-
-public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
-
- public GoSafeProtocolDecoder(GoSafeProtocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("*GS") // header
- .number("d+,") // protocol version
- .number("(d+),") // imei
- .number("(dd)(dd)(dd)") // time (hhmmss)
- .number("(dd)(dd)(dd),") // date (ddmmyy)
- .expression("(.*)#?") // data
- .compile();
-
- private static final Pattern PATTERN_ITEM = new PatternBuilder()
- .number("(x+)?,").optional() // event
- .groupBegin()
- .text("SYS:")
- .expression("[^,]*,")
- .groupEnd("?")
- .groupBegin()
- .text("GPS:")
- .expression("([AV]);") // validity
- .number("(d+);") // satellites
- .number("([NS])(d+.d+);") // latitude
- .number("([EW])(d+.d+);") // longitude
- .number("(d+)?;") // speed
- .number("(d+);") // course
- .number("(d+);") // altitude
- .number("(d+.d+)") // hdop
- .number(";(d+.d+)").optional() // vdop
- .expression(",?")
- .groupEnd()
- .groupBegin()
- .text("GSM:")
- .number("d*;") // registration
- .number("d*;") // gsm signal
- .number("(d+);") // mcc
- .number("(d+);") // mnc
- .number("(x+);") // lac
- .number("(x+);") // cid
- .number("(-d+)") // rssi
- .expression("[^,]*,?")
- .groupEnd("?")
- .groupBegin()
- .text("COT:")
- .number("(d+)") // odometer
- .number("(?:;d+:d+:d+)?") // engine hours
- .expression(",?")
- .groupEnd("?")
- .groupBegin()
- .text("ADC:")
- .number("(d+.d+)") // power
- .number("(?:;(d+.d+))?,?") // battery
- .groupEnd("?")
- .groupBegin()
- .text("DTT:")
- .number("(x+);") // status
- .number("(x+)?;") // io
- .number("(x+);") // geo-fence 0-119
- .number("(x+);") // geo-fence 120-155
- .number("(x+)") // event status
- .number("(?:;(x+))?,?") // packet type
- .groupEnd("?")
- .groupBegin()
- .text("ETD:").expression("([^,]+),?")
- .groupEnd("?")
- .groupBegin()
- .text("OBD:")
- .number("(x+),?")
- .groupEnd("?")
- .groupBegin()
- .text("FUL:").expression("[^,]*,?")
- .groupEnd("?")
- .groupBegin()
- .text("TRU:").expression("[^,]*,?")
- .groupEnd("?")
- .groupBegin()
- .text("TAG:").expression("([^,]+),?")
- .groupEnd("?")
- .compile();
-
- private static final Pattern PATTERN_OLD = new PatternBuilder()
- .text("*GS") // header
- .number("d+,") // protocol version
- .number("(d+),") // imei
- .text("GPS:")
- .number("(dd)(dd)(dd);") // time (hhmmss)
- .number("d;").optional() // fix type
- .expression("([AV]);") // validity
- .number("([NS])(d+.d+);") // latitude
- .number("([EW])(d+.d+);") // longitude
- .number("(d+)?;") // speed
- .number("(d+);") // course
- .number("(d+.?d*)").optional() // hdop
- .number("(dd)(dd)(dd)") // date (ddmmyy)
- .any()
- .compile();
-
- private Position decodePosition(DeviceSession deviceSession, Parser parser, Date time) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- if (time != null) {
- position.setTime(time);
- }
-
- position.set(Position.KEY_EVENT, parser.next());
-
- position.setValid(parser.next().equals("A"));
- position.set(Position.KEY_SATELLITES, parser.nextInt());
-
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- position.set(Position.KEY_HDOP, parser.nextDouble(0));
- position.set(Position.KEY_VDOP, parser.nextDouble(0));
-
- if (parser.hasNext(5)) {
- position.setNetwork(new Network(CellTower.from(parser.nextInt(0), parser.nextInt(0),
- parser.nextHexInt(0), parser.nextHexInt(0), parser.nextInt(0))));
- }
- if (parser.hasNext()) {
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
- }
- position.set(Position.KEY_POWER, parser.nextDouble());
- position.set(Position.KEY_BATTERY, parser.nextDouble());
-
- if (parser.hasNext(6)) {
- long status = parser.nextLong(16, 0);
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 13));
- position.set(Position.KEY_STATUS, status);
- position.set("ioStatus", parser.next());
- position.set(Position.KEY_GEOFENCE, parser.next() + parser.next());
- position.set("eventStatus", parser.next());
- position.set("packetType", parser.next());
- }
-
- if (parser.hasNext()) {
- position.set("eventData", parser.next());
- }
-
- if (parser.hasNext()) {
- position.set("obd", parser.next());
- }
-
- if (parser.hasNext()) {
- position.set("tagData", parser.next());
- }
-
- return position;
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- if (channel != null) {
- channel.write("1234");
- }
-
- String sentence = (String) msg;
- Pattern pattern = PATTERN;
- if (sentence.startsWith("*GS02")) {
- pattern = PATTERN_OLD;
- }
-
- Parser parser = new Parser(pattern, (String) msg);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- if (pattern == PATTERN_OLD) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
-
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
-
- position.set(Position.KEY_HDOP, parser.next());
-
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
-
- return position;
-
- } else {
-
- Date time = null;
- if (parser.hasNext(6)) {
- time = parser.nextDateTime(Parser.DateTimeFormat.HMS_DMY);
- }
-
- List<Position> positions = new LinkedList<>();
- Parser itemParser = new Parser(PATTERN_ITEM, parser.next());
- while (itemParser.find()) {
- positions.add(decodePosition(deviceSession, itemParser, time));
- }
-
- return positions;
-
- }
- }
-
-}
diff --git a/src/org/traccar/protocol/GotopProtocol.java b/src/org/traccar/protocol/GotopProtocol.java
deleted file mode 100644
index 5d522adf5..000000000
--- a/src/org/traccar/protocol/GotopProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GotopProtocol extends BaseProtocol {
-
- public GotopProtocol() {
- super("gotop");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new GotopProtocolDecoder(GotopProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Gps103Protocol.java b/src/org/traccar/protocol/Gps103Protocol.java
deleted file mode 100644
index a5bd32261..000000000
--- a/src/org/traccar/protocol/Gps103Protocol.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.util.List;
-
-public class Gps103Protocol extends BaseProtocol {
-
- public Gps103Protocol() {
- super("gps103");
- setSupportedDataCommands(
- Command.TYPE_CUSTOM,
- Command.TYPE_POSITION_SINGLE,
- Command.TYPE_POSITION_PERIODIC,
- Command.TYPE_POSITION_STOP,
- Command.TYPE_ENGINE_STOP,
- Command.TYPE_ENGINE_RESUME,
- Command.TYPE_ALARM_ARM,
- Command.TYPE_ALARM_DISARM,
- Command.TYPE_REQUEST_PHOTO);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(2048, "\r\n", "\n", ";"));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new Gps103ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new Gps103ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
deleted file mode 100644
index 099047aa0..000000000
--- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright 2012 - 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.util.regex.Pattern;
-
-public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
-
- public Gps103ProtocolDecoder(Gps103Protocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("imei:")
- .number("(d+),") // imei
- .expression("([^,]+),") // alarm
- .number("(dd)/?(dd)/?(dd) ?") // local date (yymmdd)
- .number("(dd):?(dd)(?:dd)?,") // local time (hhmmss)
- .expression("([^,]+)?,") // rfid
- .expression("[FL],") // full / low
- .groupBegin()
- .number("(dd)(dd)(dd).d+") // time utc (hhmmss)
- .or()
- .number("(?:d{1,5}.d+)?")
- .groupEnd()
- .text(",")
- .expression("([AV]),") // validity
- .expression("([NS]),").optional()
- .number("(d+)(dd.d+),") // latitude (ddmm.mmmm)
- .expression("([NS]),").optional()
- .expression("([EW]),").optional()
- .number("(d+)(dd.d+),") // longitude (dddmm.mmmm)
- .expression("([EW])?,").optional()
- .number("(d+.?d*)?,?") // speed
- .number("(d+.?d*)?,?") // course
- .number("(d+.?d*)?,?") // altitude
- .expression("([^,;]+)?,?")
- .expression("([^,;]+)?,?")
- .expression("([^,;]+)?,?")
- .expression("([^,;]+)?,?")
- .expression("([^,;]+)?,?")
- .any()
- .compile();
-
- private static final Pattern PATTERN_NETWORK = new PatternBuilder()
- .text("imei:")
- .number("(d+),") // imei
- .expression("[^,]+,") // alarm
- .number("d*,,")
- .text("L,,,")
- .number("(x+),,") // lac
- .number("(x+),,,") // cid
- .any()
- .compile();
-
- private static final Pattern PATTERN_HANDSHAKE = new PatternBuilder()
- .number("##,imei:(d+),A")
- .compile();
-
- private static final Pattern PATTERN_OBD = new PatternBuilder()
- .text("imei:")
- .number("(d+),") // imei
- .expression("OBD,") // type
- .number("(dd)(dd)(dd)") // date (yymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(d+),") // odometer
- .number("(d+.d+)?,") // fuel instant
- .number("(d+.d+)?,") // fuel average
- .number("(d+)?,") // hours
- .number("(d+),") // speed
- .number("(d+.?d*%),") // power load
- .number("(?:([-+]?d+)|[-+]?),") // temperature
- .number("(d+.?d*%),") // throttle
- .number("(d+),") // rpm
- .number("(d+.d+),") // battery
- .number("([^;]*)") // dtcs
- .any()
- .compile();
-
- private String decodeAlarm(String value) {
- if (value.startsWith("T:")) {
- return Position.ALARM_TEMPERATURE;
- } else if (value.startsWith("oil")) {
- return Position.ALARM_OIL_LEAK;
- }
- switch (value) {
- case "tracker":
- return null;
- case "help me":
- return Position.ALARM_SOS;
- case "low battery":
- return Position.ALARM_LOW_BATTERY;
- case "stockade":
- return Position.ALARM_GEOFENCE;
- case "move":
- return Position.ALARM_MOVEMENT;
- case "speed":
- return Position.ALARM_OVERSPEED;
- case "acc on":
- return Position.ALARM_POWER_ON;
- case "acc off":
- return Position.ALARM_POWER_OFF;
- case "door alarm":
- return Position.ALARM_DOOR;
- case "ac alarm":
- return Position.ALARM_POWER_CUT;
- case "accident alarm":
- return Position.ALARM_ACCIDENT;
- case "sensor alarm":
- return Position.ALARM_SHOCK;
- case "bonnet alarm":
- return Position.ALARM_BONNET;
- case "footbrake alarm":
- return Position.ALARM_FOOT_BRAKE;
- case "DTC":
- return Position.ALARM_FAULT;
- default:
- return null;
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- String sentence = (String) msg;
-
- // Send response #1
- if (sentence.contains("##")) {
- if (channel != null) {
- channel.write("LOAD", remoteAddress);
- Parser handshakeParser = new Parser(PATTERN_HANDSHAKE, sentence);
- if (handshakeParser.matches()) {
- getDeviceSession(channel, remoteAddress, handshakeParser.next());
- }
- }
- return null;
- }
-
- // Send response #2
- if (!sentence.isEmpty() && Character.isDigit(sentence.charAt(0))) {
- if (channel != null) {
- channel.write("ON", remoteAddress);
- }
- int start = sentence.indexOf("imei:");
- if (start >= 0) {
- sentence = sentence.substring(start);
- } else {
- return null;
- }
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- Parser parser = new Parser(PATTERN_NETWORK, sentence);
- if (parser.matches()) {
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- getLastLocation(position, null);
-
- position.setNetwork(new Network(
- CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0))));
-
- return position;
-
- }
-
- parser = new Parser(PATTERN_OBD, sentence);
- if (parser.matches()) {
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- getLastLocation(position, parser.nextDateTime());
-
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
- parser.nextDouble(0); // instant fuel consumption
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble(0));
- position.set(Position.KEY_HOURS, parser.nextInt());
- position.set(Position.KEY_OBD_SPEED, parser.nextInt(0));
- position.set(Position.KEY_ENGINE_LOAD, parser.next());
- position.set(Position.KEY_COOLANT_TEMP, parser.nextInt());
- position.set(Position.KEY_THROTTLE, parser.next());
- position.set(Position.KEY_RPM, parser.nextInt(0));
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
- position.set(Position.KEY_DTCS, parser.next().replace(',', ' ').trim());
-
- return position;
-
- }
-
- parser = new Parser(PATTERN, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- String imei = parser.next();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- String alarm = parser.next();
- position.set(Position.KEY_ALARM, decodeAlarm(alarm));
- if (alarm.equals("help me")) {
- if (channel != null) {
- channel.write("**,imei:" + imei + ",E;", remoteAddress);
- }
- } else if (alarm.equals("acc on")) {
- position.set(Position.KEY_IGNITION, true);
- } else if (alarm.equals("acc off")) {
- position.set(Position.KEY_IGNITION, false);
- } else if (alarm.startsWith("T:")) {
- position.set(Position.PREFIX_TEMP + 1, alarm.substring(2));
- } else if (alarm.startsWith("oil ")) {
- position.set("oil", alarm.substring(4));
- } else if (!position.getAttributes().containsKey(Position.KEY_ALARM) && !alarm.equals("tracker")) {
- position.set(Position.KEY_EVENT, alarm);
- }
-
- DateBuilder dateBuilder = new DateBuilder()
- .setDate(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
-
- int localHours = parser.nextInt(0);
- int localMinutes = parser.nextInt(0);
-
- String rfid = parser.next();
- if (alarm.equals("rfid")) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
- }
-
- String utcHours = parser.next();
- String utcMinutes = parser.next();
-
- dateBuilder.setTime(localHours, localMinutes, parser.nextInt(0));
-
- // Timezone calculation
- if (utcHours != null && utcMinutes != null) {
- int deltaMinutes = (localHours - Integer.parseInt(utcHours)) * 60;
- deltaMinutes += localMinutes - Integer.parseInt(utcMinutes);
- if (deltaMinutes <= -12 * 60) {
- deltaMinutes += 24 * 60;
- } else if (deltaMinutes > 12 * 60) {
- deltaMinutes -= 24 * 60;
- }
- dateBuilder.addMinute(-deltaMinutes);
- }
- position.setTime(dateBuilder.getDate());
-
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_HEM));
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- for (int i = 1; i <= 5; i++) {
- position.set(Position.PREFIX_IO + i, parser.next());
- }
-
- return position;
- }
-
-}
diff --git a/src/org/traccar/protocol/GpsGateProtocol.java b/src/org/traccar/protocol/GpsGateProtocol.java
deleted file mode 100644
index c7dc2c4f3..000000000
--- a/src/org/traccar/protocol/GpsGateProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GpsGateProtocol extends BaseProtocol {
-
- public GpsGateProtocol() {
- super("gpsgate");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\0", "\n", "\r\n"));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new GpsGateProtocolDecoder(GpsGateProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GpsMarkerProtocol.java b/src/org/traccar/protocol/GpsMarkerProtocol.java
deleted file mode 100644
index 5c64d16b2..000000000
--- a/src/org/traccar/protocol/GpsMarkerProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GpsMarkerProtocol extends BaseProtocol {
-
- public GpsMarkerProtocol() {
- super("gpsmarker");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new GpsMarkerProtocolDecoder(GpsMarkerProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/GpsmtaProtocol.java b/src/org/traccar/protocol/GpsmtaProtocol.java
deleted file mode 100644
index 2d1181bec..000000000
--- a/src/org/traccar/protocol/GpsmtaProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class GpsmtaProtocol extends BaseProtocol {
-
- public GpsmtaProtocol() {
- super("gpsmta");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new GpsmtaProtocolDecoder(GpsmtaProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Gt02Protocol.java b/src/org/traccar/protocol/Gt02Protocol.java
deleted file mode 100644
index e484b66cd..000000000
--- a/src/org/traccar/protocol/Gt02Protocol.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Gt02Protocol extends BaseProtocol {
-
- public Gt02Protocol() {
- super("gt02");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0));
- pipeline.addLast("objectDecoder", new Gt02ProtocolDecoder(Gt02Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
deleted file mode 100644
index 177c0b653..000000000
--- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * Copyright 2012 - 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BcdUtil;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.Checksum;
-import org.traccar.helper.DateBuilder;
-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;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.regex.Pattern;
-
-public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
-
- private boolean forceTimeZone = false;
- private final TimeZone timeZone = TimeZone.getTimeZone("UTC");
-
- private int serverIndex;
-
- private final Map<Integer, ChannelBuffer> photos = new HashMap<>();
-
- public Gt06ProtocolDecoder(Gt06Protocol protocol) {
- super(protocol);
-
- if (Context.getConfig().hasKey(getProtocolName() + ".timezone")) {
- forceTimeZone = true;
- timeZone.setRawOffset(Context.getConfig().getInteger(getProtocolName() + ".timezone") * 1000);
- }
- }
-
- public static final int MSG_LOGIN = 0x01;
- public static final int MSG_GPS = 0x10;
- public static final int MSG_LBS = 0x11;
- public static final int MSG_GPS_LBS_1 = 0x12;
- public static final int MSG_GPS_LBS_2 = 0x22;
- public static final int MSG_STATUS = 0x13;
- public static final int MSG_SATELLITE = 0x14;
- public static final int MSG_STRING = 0x15;
- public static final int MSG_GPS_LBS_STATUS_1 = 0x16;
- public static final int MSG_WIFI = 0x17;
- public static final int MSG_GPS_LBS_STATUS_2 = 0x26;
- public static final int MSG_GPS_LBS_STATUS_3 = 0x27;
- public static final int MSG_LBS_MULTIPLE = 0x28;
- public static final int MSG_LBS_WIFI = 0x2C;
- public static final int MSG_LBS_PHONE = 0x17;
- 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_AZ735_GPS = 0x32;
- public static final int MSG_AZ735_ALARM = 0x33;
- 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;
- public static final int MSG_WIFI_2 = 0x69;
- 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_INFO = 0x94;
- public static final int MSG_STRING_INFO = 0x21;
-
- private static boolean isSupported(int type) {
- return hasGps(type) || hasLbs(type) || hasStatus(type);
- }
-
- private static boolean hasGps(int type) {
- return type == MSG_GPS || type == MSG_GPS_LBS_1 || type == MSG_GPS_LBS_2
- || type == MSG_GPS_LBS_STATUS_1 || type == MSG_GPS_LBS_STATUS_2 || type == MSG_GPS_LBS_STATUS_3
- || type == MSG_GPS_PHONE || type == MSG_GPS_LBS_EXTEND;
- }
-
- private static boolean hasLbs(int type) {
- return type == MSG_LBS || type == MSG_LBS_STATUS || type == MSG_GPS_LBS_1 || type == MSG_GPS_LBS_2
- || type == MSG_GPS_LBS_STATUS_1 || type == MSG_GPS_LBS_STATUS_2 || type == MSG_GPS_LBS_STATUS_3;
- }
-
- private static boolean hasStatus(int type) {
- return type == MSG_STATUS || type == MSG_LBS_STATUS
- || type == MSG_GPS_LBS_STATUS_1 || type == MSG_GPS_LBS_STATUS_2 || type == MSG_GPS_LBS_STATUS_3;
- }
-
- private void sendResponse(Channel channel, boolean extended, int type, ChannelBuffer content) {
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
- int length = 5 + (content != null ? content.readableBytes() : 0);
- if (extended) {
- response.writeShort(0x7979);
- response.writeShort(length);
- } else {
- response.writeShort(0x7878);
- response.writeByte(length);
- }
- response.writeByte(type);
- if (content != null) {
- response.writeBytes(content);
- }
- response.writeShort(++serverIndex);
- response.writeShort(Checksum.crc16(Checksum.CRC16_X25,
- response.toByteBuffer(2, response.writerIndex() - 2)));
- response.writeByte('\r'); response.writeByte('\n'); // ending
- channel.write(response);
- }
- }
-
- private void sendPhotoRequest(Channel channel, int pictureId) {
- ChannelBuffer photo = photos.get(pictureId);
- ChannelBuffer content = ChannelBuffers.dynamicBuffer();
- content.writeInt(pictureId);
- content.writeInt(photo.writerIndex());
- content.writeShort(Math.min(photo.writableBytes(), 1024));
- sendResponse(channel, false, MSG_X1_PHOTO_DATA, content);
- }
-
- private boolean decodeGps(Position position, ChannelBuffer buf, boolean hasLength) {
-
- DateBuilder dateBuilder = new DateBuilder(timeZone)
- .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
- position.setTime(dateBuilder.getDate());
-
- if (hasLength && buf.readUnsignedByte() == 0) {
- return false;
- }
-
- position.set(Position.KEY_SATELLITES, BitUtil.to(buf.readUnsignedByte(), 4));
-
- 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);
-
- if (BitUtil.check(flags, 14)) {
- position.set(Position.KEY_IGNITION, BitUtil.check(flags, 15));
- }
-
- return true;
- }
-
- private boolean decodeLbs(Position position, ChannelBuffer buf, boolean hasLength) {
-
- int length = 0;
- if (hasLength) {
- length = buf.readUnsignedByte();
- if (length == 0) {
- return false;
- }
- }
-
- position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedShort(), buf.readUnsignedMedium())));
-
- if (length > 0) {
- buf.skipBytes(length - (hasLength ? 9 : 8));
- }
-
- return true;
- }
-
- private boolean decodeStatus(Position position, ChannelBuffer buf) {
-
- int status = buf.readUnsignedByte();
-
- position.set(Position.KEY_STATUS, status);
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 1));
- position.set(Position.KEY_CHARGE, BitUtil.check(status, 2));
- position.set(Position.KEY_BLOCKED, BitUtil.check(status, 7));
-
- switch (BitUtil.between(status, 3, 6)) {
- case 1:
- position.set(Position.KEY_ALARM, Position.ALARM_SHOCK);
- break;
- case 2:
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
- break;
- case 3:
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
- break;
- case 4:
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
- break;
- default:
- break;
- }
-
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte());
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
-
- return true;
- }
-
- private String decodeAlarm(short value) {
- switch (value) {
- case 0x01:
- return Position.ALARM_SOS;
- case 0x02:
- return Position.ALARM_POWER_CUT;
- case 0x03:
- case 0x09:
- return Position.ALARM_VIBRATION;
- case 0x04:
- return Position.ALARM_GEOFENCE_ENTER;
- case 0x05:
- return Position.ALARM_GEOFENCE_EXIT;
- case 0x06:
- return Position.ALARM_OVERSPEED;
- case 0x0E:
- case 0x0F:
- return Position.ALARM_LOW_BATTERY;
- case 0x11:
- return Position.ALARM_POWER_OFF;
- 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, ChannelBuffer buf) throws Exception {
-
- int length = buf.readUnsignedByte();
- int dataLength = length - 5;
- int type = buf.readUnsignedByte();
-
- DeviceSession deviceSession = null;
- if (type != MSG_LOGIN) {
- deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null) {
- return null;
- }
- }
-
- if (type == MSG_LOGIN) {
-
- String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
- buf.readUnsignedShort(); // type
-
- if (dataLength > 10) {
- int extensionBits = buf.readUnsignedShort();
- int hours = (extensionBits >> 4) / 100;
- int minutes = (extensionBits >> 4) % 100;
- int offset = (hours * 60 + minutes) * 60;
- if ((extensionBits & 0x8) != 0) {
- offset = -offset;
- }
- if (!forceTimeZone) {
- timeZone.setRawOffset(offset * 1000);
- }
- }
-
- if (getDeviceSession(channel, remoteAddress, imei) != null) {
- sendResponse(channel, false, type, null);
- }
-
- } else if (type == MSG_X1_GPS) {
-
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
-
- buf.readUnsignedInt(); // data and alarm
-
- decodeGps(position, buf, false);
-
- buf.readUnsignedShort(); // terminal info
-
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
-
- position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShort(), buf.readUnsignedByte(),
- buf.readUnsignedShort(), buf.readUnsignedInt())));
-
- return position;
-
- } else if (type == MSG_X1_PHOTO_INFO) {
-
- buf.skipBytes(6); // time
- buf.readUnsignedByte(); // fix status
- buf.readUnsignedInt(); // latitude
- buf.readUnsignedInt(); // longitude
- buf.readUnsignedByte(); // camera id
- buf.readUnsignedByte(); // photo source
- buf.readUnsignedByte(); // picture format
-
- ChannelBuffer photo = ChannelBuffers.buffer(buf.readInt());
- int pictureId = buf.readInt();
- photos.put(pictureId, photo);
- sendPhotoRequest(channel, pictureId);
-
- } else if (type == MSG_WIFI || type == MSG_WIFI_2) {
-
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
-
- DateBuilder dateBuilder = new DateBuilder()
- .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));
- getLastLocation(position, dateBuilder.getDate());
-
- Network network = new Network();
-
- int wifiCount = buf.getByte(2);
- for (int i = 0; i < wifiCount; i++) {
- String mac = String.format("%02x:%02x:%02x:%02x:%02x:%02x",
- buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(),
- buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
- network.addWifiAccessPoint(WifiAccessPoint.from(mac, 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()));
- }
-
- position.setNetwork(network);
-
- return position;
-
- } else {
-
- return decodeBasicOther(channel, buf, deviceSession, type, dataLength);
-
- }
-
- return null;
- }
-
- private Object decodeBasicOther(Channel channel, ChannelBuffer buf,
- DeviceSession deviceSession, int type, int dataLength) throws Exception {
-
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
-
- if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI) {
-
- DateBuilder dateBuilder = new DateBuilder(timeZone)
- .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
-
- getLastLocation(position, dateBuilder.getDate());
-
- int mcc = buf.readUnsignedShort();
- int mnc = buf.readUnsignedByte();
- Network network = new Network();
- for (int i = 0; i < 7; i++) {
- int lac = buf.readUnsignedShort();
- int cid = buf.readUnsignedMedium();
- int rssi = -buf.readUnsignedByte();
- if (lac > 0) {
- network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi));
- }
- }
-
- buf.readUnsignedByte(); // time leads
-
- if (type != MSG_LBS_MULTIPLE) {
- int wifiCount = buf.readUnsignedByte();
- for (int i = 0; i < wifiCount; i++) {
- String mac = ChannelBuffers.hexDump(buf.readBytes(6)).replaceAll("(..)", "$1:");
- network.addWifiAccessPoint(WifiAccessPoint.from(
- mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
- }
- }
-
- position.setNetwork(network);
-
- } else if (type == MSG_STRING) {
-
- getLastLocation(position, null);
-
- int commandLength = buf.readUnsignedByte();
-
- if (commandLength > 0) {
- buf.readUnsignedByte(); // server flag (reserved)
- position.set(Position.KEY_RESULT,
- buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII));
- }
-
- } else if (isSupported(type)) {
-
- if (hasGps(type)) {
- decodeGps(position, buf, false);
- } else {
- getLastLocation(position, null);
- }
-
- if (hasLbs(type)) {
- decodeLbs(position, buf, hasStatus(type));
- }
-
- if (hasStatus(type)) {
- decodeStatus(position, buf);
- }
-
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() >= 4 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
-
- if (type == MSG_GPS_LBS_2 && 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);
- }
-
- } else {
-
- buf.skipBytes(dataLength);
- if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) {
- sendResponse(channel, false, type, null);
- }
- return null;
-
- }
-
- sendResponse(channel, false, type, null);
-
- return position;
- }
-
- private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) throws Exception {
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
-
- buf.readUnsignedShort(); // length
- int type = buf.readUnsignedByte();
-
- if (type == MSG_STRING_INFO) {
-
- buf.readUnsignedInt(); // server flag
- String data;
- if (buf.readUnsignedByte() == 1) {
- data = buf.readBytes(buf.readableBytes() - 6).toString(StandardCharsets.US_ASCII);
- } else {
- data = buf.readBytes(buf.readableBytes() - 6).toString(StandardCharsets.UTF_16BE);
- }
-
- if (decodeLocationString(position, data) == null) {
- getLastLocation(position, null);
- position.set(Position.KEY_RESULT, data);
- }
-
- return position;
-
- } else if (type == MSG_INFO) {
-
- int subType = buf.readUnsignedByte();
-
- getLastLocation(position, null);
-
- if (subType == 0x00) {
-
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
- return position;
-
- } else if (subType == 0x05) {
-
- 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 == 0x0d) {
-
- buf.skipBytes(6);
- return decodeFuelData(position, buf.toString(
- buf.readerIndex(), buf.readableBytes() - 4 - 2, StandardCharsets.US_ASCII));
-
- }
-
- } else if (type == MSG_X1_PHOTO_DATA) {
-
- int pictureId = buf.readInt();
-
- ChannelBuffer photo = photos.get(pictureId);
-
- buf.readUnsignedInt(); // offset
- buf.readBytes(photo, buf.readUnsignedShort());
-
- if (photo.writableBytes() > 0) {
- sendPhotoRequest(channel, pictureId);
- } else {
- Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId());
- Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg");
- photos.remove(pictureId);
- }
-
- } else if (type == MSG_AZ735_GPS || type == MSG_AZ735_ALARM) {
-
- if (!decodeGps(position, buf, true)) {
- getLastLocation(position, position.getDeviceTime());
- }
-
- if (decodeLbs(position, buf, true)) {
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- }
-
- buf.skipBytes(buf.readUnsignedByte()); // additional cell towers
- buf.skipBytes(buf.readUnsignedByte()); // wifi access point
-
- int status = buf.readUnsignedByte();
- position.set(Position.KEY_STATUS, status);
-
- if (type == MSG_AZ735_ALARM) {
- switch (status) {
- case 0xA0:
- position.set(Position.KEY_ARMED, true);
- break;
- case 0xA1:
- position.set(Position.KEY_ARMED, false);
- break;
- case 0xA2:
- case 0xA3:
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
- break;
- case 0xA4:
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- break;
- case 0xA5:
- position.set(Position.KEY_ALARM, Position.ALARM_DOOR);
- break;
- default:
- break;
- }
- }
-
- buf.skipBytes(buf.readUnsignedByte()); // reserved extension
-
- sendResponse(channel, true, type, null);
-
- return position;
-
- }
-
- return null;
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- int header = buf.readShort();
-
- if (header == 0x7878) {
- return decodeBasic(channel, remoteAddress, buf);
- } else if (header == 0x7979) {
- return decodeExtended(channel, remoteAddress, buf);
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/Gt30Protocol.java b/src/org/traccar/protocol/Gt30Protocol.java
deleted file mode 100644
index 186002a35..000000000
--- a/src/org/traccar/protocol/Gt30Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Gt30Protocol extends BaseProtocol {
-
- public Gt30Protocol() {
- super("gt30");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Gt30ProtocolDecoder(Gt30Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/HaicomProtocol.java b/src/org/traccar/protocol/HaicomProtocol.java
deleted file mode 100644
index 4380dd2cc..000000000
--- a/src/org/traccar/protocol/HaicomProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class HaicomProtocol extends BaseProtocol {
-
- public HaicomProtocol() {
- super("haicom");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '*'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new HaicomProtocolDecoder(HaicomProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/HomtecsProtocol.java b/src/org/traccar/protocol/HomtecsProtocol.java
deleted file mode 100644
index a9ea19c51..000000000
--- a/src/org/traccar/protocol/HomtecsProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class HomtecsProtocol extends BaseProtocol {
-
- public HomtecsProtocol() {
- super("homtecs");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new HomtecsProtocolDecoder(HomtecsProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/org/traccar/protocol/HuabaoProtocolDecoder.java
deleted file mode 100644
index c31c6af1c..000000000
--- a/src/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BcdUtil;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.Checksum;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.TimeZone;
-
-public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
-
- public HuabaoProtocolDecoder(HuabaoProtocol protocol) {
- super(protocol);
- }
-
- public static final int MSG_GENERAL_RESPONSE = 0x8001;
- public static final int MSG_TERMINAL_REGISTER = 0x0100;
- public static final int MSG_TERMINAL_REGISTER_RESPONSE = 0x8100;
- public static final int MSG_TERMINAL_AUTH = 0x0102;
- public static final int MSG_LOCATION_REPORT = 0x0200;
- public static final int MSG_OIL_CONTROL = 0XA006;
-
- public static final int RESULT_SUCCESS = 0;
-
- public static ChannelBuffer formatMessage(int type, ChannelBuffer id, ChannelBuffer data) {
- ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
- buf.writeByte(0x7e);
- buf.writeShort(type);
- buf.writeShort(data.readableBytes());
- buf.writeBytes(id);
- buf.writeShort(1); // index
- buf.writeBytes(data);
- buf.writeByte(Checksum.xor(buf.toByteBuffer(1, buf.readableBytes() - 1)));
- buf.writeByte(0x7e);
- return buf;
- }
-
- private void sendGeneralResponse(
- Channel channel, SocketAddress remoteAddress, ChannelBuffer id, int type, int index) {
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
- response.writeShort(index);
- response.writeShort(type);
- response.writeByte(RESULT_SUCCESS);
- channel.write(formatMessage(MSG_GENERAL_RESPONSE, id, response), remoteAddress);
- }
- }
-
- private String decodeAlarm(long value) {
- if (BitUtil.check(value, 0)) {
- return Position.ALARM_SOS;
- }
- if (BitUtil.check(value, 1)) {
- return Position.ALARM_OVERSPEED;
- }
- if (BitUtil.check(value, 5)) {
- return Position.ALARM_GPS_ANTENNA_CUT;
- }
- if (BitUtil.check(value, 4) || BitUtil.check(value, 9)
- || BitUtil.check(value, 10) || BitUtil.check(value, 11)) {
- return Position.ALARM_FAULT;
- }
- if (BitUtil.check(value, 8)) {
- return Position.ALARM_POWER_OFF;
- }
- if (BitUtil.check(value, 20)) {
- return Position.ALARM_GEOFENCE;
- }
- if (BitUtil.check(value, 29)) {
- return Position.ALARM_ACCIDENT;
- }
- return null;
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.readUnsignedByte(); // start marker
- int type = buf.readUnsignedShort();
- buf.readUnsignedShort(); // body length
- ChannelBuffer id = buf.readBytes(6); // phone number
- int index = buf.readUnsignedShort();
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(id));
- if (deviceSession == null) {
- return null;
- }
-
- if (type == MSG_TERMINAL_REGISTER) {
-
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.dynamicBuffer();
- response.writeShort(index);
- response.writeByte(RESULT_SUCCESS);
- response.writeBytes("authentication".getBytes(StandardCharsets.US_ASCII));
- channel.write(formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress);
- }
-
- } else if (type == MSG_TERMINAL_AUTH) {
-
- sendGeneralResponse(channel, remoteAddress, id, type, index);
-
- } else if (type == MSG_LOCATION_REPORT) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
-
- int flags = buf.readInt();
-
- position.set(Position.KEY_IGNITION, BitUtil.check(flags, 0));
-
- position.setValid(BitUtil.check(flags, 1));
-
- double lat = buf.readUnsignedInt() * 0.000001;
- double lon = buf.readUnsignedInt() * 0.000001;
-
- if (BitUtil.check(flags, 2)) {
- position.setLatitude(-lat);
- } else {
- position.setLatitude(lat);
- }
-
- if (BitUtil.check(flags, 3)) {
- position.setLongitude(-lon);
- } else {
- position.setLongitude(lon);
- }
-
- position.setAltitude(buf.readShort());
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
- position.setCourse(buf.readUnsignedShort());
-
- DateBuilder dateBuilder = new DateBuilder(TimeZone.getTimeZone("GMT+8"))
- .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());
-
- // additional information
-
- return position;
-
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/org/traccar/protocol/HuabaoProtocolEncoder.java
deleted file mode 100644
index 7d6f0510d..000000000
--- a/src/org/traccar/protocol/HuabaoProtocolEncoder.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.traccar.BaseProtocolEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-
-import javax.xml.bind.DatatypeConverter;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-public class HuabaoProtocolEncoder extends BaseProtocolEncoder {
-
- @Override
- protected Object encodeCommand(Command command) {
-
- ChannelBuffer id = ChannelBuffers.wrappedBuffer(
- DatatypeConverter.parseHexBinary(getUniqueId(command.getDeviceId())));
-
- ChannelBuffer data = ChannelBuffers.dynamicBuffer();
- byte[] time = DatatypeConverter.parseHexBinary(new SimpleDateFormat("yyMMddHHmmss").format(new Date()));
-
- switch (command.getType()) {
- case Command.TYPE_ENGINE_STOP:
- data.writeByte(0x01);
- data.writeBytes(time);
- return HuabaoProtocolDecoder.formatMessage(HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data);
- case Command.TYPE_ENGINE_RESUME:
- data.writeByte(0x00);
- data.writeBytes(time);
- return HuabaoProtocolDecoder.formatMessage(HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data);
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- return null;
- }
- }
-
-}
diff --git a/src/org/traccar/protocol/HunterProProtocol.java b/src/org/traccar/protocol/HunterProProtocol.java
deleted file mode 100644
index 17352a0f3..000000000
--- a/src/org/traccar/protocol/HunterProProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class HunterProProtocol extends BaseProtocol {
-
- public HunterProProtocol() {
- super("hunterpro");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r"));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new HunterProProtocolDecoder(HunterProProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/IdplProtocol.java b/src/org/traccar/protocol/IdplProtocol.java
deleted file mode 100644
index f90d3fe7f..000000000
--- a/src/org/traccar/protocol/IdplProtocol.java
+++ /dev/null
@@ -1,47 +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.protocol;
-
-import java.util.List;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-public class IdplProtocol extends BaseProtocol {
-
- public IdplProtocol() {
- super("idpl");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new IdplProtocolDecoder(IdplProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/IntellitracProtocol.java b/src/org/traccar/protocol/IntellitracProtocol.java
deleted file mode 100644
index 2d9421636..000000000
--- a/src/org/traccar/protocol/IntellitracProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class IntellitracProtocol extends BaseProtocol {
-
- public IntellitracProtocol() {
- super("intellitrac");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new IntellitracFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new IntellitracProtocolDecoder(IntellitracProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/KenjiProtocol.java b/src/org/traccar/protocol/KenjiProtocol.java
deleted file mode 100644
index 8b196a9ed..000000000
--- a/src/org/traccar/protocol/KenjiProtocol.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2016 by Carlos Alvarez (carlos.alvarez.rozas@gmail.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class KenjiProtocol extends BaseProtocol {
-
- public KenjiProtocol() {
- super("kenji");
- }
-
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new KenjiProtocolDecoder(KenjiProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/L100FrameDecoder.java b/src/org/traccar/protocol/L100FrameDecoder.java
deleted file mode 100644
index a597cbd7d..000000000
--- a/src/org/traccar/protocol/L100FrameDecoder.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-
-public class L100FrameDecoder extends FrameDecoder {
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
-
- if (buf.readableBytes() < 80) {
- return null;
- }
-
- int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x02);
- if (index == -1) {
- index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x04);
- if (index == -1) {
- return null;
- }
- }
-
- index += 2; // checksum
-
- if (buf.readableBytes() >= index - buf.readerIndex()) {
- return buf.readBytes(index - buf.readerIndex());
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/L100ProtocolDecoder.java b/src/org/traccar/protocol/L100ProtocolDecoder.java
deleted file mode 100644
index 618448080..000000000
--- a/src/org/traccar/protocol/L100ProtocolDecoder.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.regex.Pattern;
-
-public class L100ProtocolDecoder extends BaseProtocolDecoder {
-
- public L100ProtocolDecoder(L100Protocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("ATL")
- .number("(d{15}),") // imei
- .text("$GPRMC,")
- .number("(dd)(dd)(dd)") // time (hhmmss.sss)
- .number(".(ddd)").optional()
- .expression(",([AV]),") // validity
- .number("(dd)(dd.d+),") // latitude
- .expression("([NS]),")
- .number("(ddd)(dd.d+),") // longitude
- .expression("([EW]),")
- .number("(d+.?d*)?,") // speed
- .number("(d+.?d*)?,") // course
- .number("(dd)(dd)(dd),") // date (ddmmyy)
- .any()
- .text("#")
- .number("([01]+),") // io status
- .number("(d+.?d*|N.C),") // adc
- .expression("[^,]*,") // reserved
- .expression("[^,]*,") // reserved
- .number("(d+.d+),") // odometer
- .number("(d+.d+),") // temperature
- .number("(d+.d+),") // battery
- .number("(d+),") // gsm
- .number("(d+),") // mcc
- .number("(d+),") // mnc
- .number("(x+),") // lac
- .number("(x+)") // cid
- .text("ATL")
- .compile();
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.readUnsignedByte(); // start marker
- buf.readUnsignedByte(); // type
-
- String sentence = buf.readBytes(buf.readableBytes() - 2).toString(StandardCharsets.US_ASCII);
-
- Parser parser = new Parser(PATTERN, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
-
- position.setValid(parser.next().equals("A"));
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
-
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
-
- position.set(Position.KEY_STATUS, parser.next());
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0));
- position.set(Position.PREFIX_TEMP + 1, parser.nextDouble(0));
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
-
- int rssi = parser.nextInt(0);
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0), rssi)));
-
- return position;
- }
-
-}
diff --git a/src/org/traccar/protocol/LaipacProtocol.java b/src/org/traccar/protocol/LaipacProtocol.java
deleted file mode 100644
index 45b803a0f..000000000
--- a/src/org/traccar/protocol/LaipacProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class LaipacProtocol extends BaseProtocol {
-
- public LaipacProtocol() {
- super("laipac");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new LaipacProtocolDecoder(LaipacProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/LaipacProtocolDecoder.java b/src/org/traccar/protocol/LaipacProtocolDecoder.java
deleted file mode 100644
index 32d4ff0e6..000000000
--- a/src/org/traccar/protocol/LaipacProtocolDecoder.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2013 - 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.Checksum;
-import org.traccar.helper.DateBuilder;
-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 LaipacProtocolDecoder extends BaseProtocolDecoder {
-
- public LaipacProtocolDecoder(LaipacProtocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("$AVRMC,")
- .expression("([^,]+),") // identifier
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .expression("([AVRPavrp]),") // validity
- .number("(dd)(dd.d+),") // latitude
- .expression("([NS]),")
- .number("(ddd)(dd.d+),") // longitude
- .number("([EW]),")
- .number("(d+.d+),") // speed
- .number("(d+.d+),") // course
- .number("(dd)(dd)(dd),") // date (ddmmyy)
- .expression("(.),") // type
- .expression("[^*]+").text("*")
- .number("(xx)") // checksum
- .compile();
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- String sentence = (String) msg;
-
- if (sentence.startsWith("$ECHK") && channel != null) {
- channel.write(sentence + "\r\n"); // heartbeat
- return null;
- }
-
- Parser parser = new Parser(PATTERN, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
-
- String status = parser.next();
- position.setValid(status.toUpperCase().equals("A"));
-
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble(0));
- position.setCourse(parser.nextDouble(0));
-
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
-
- String type = parser.next();
- String checksum = parser.next();
-
- if (channel != null) {
-
- if (Character.isLowerCase(status.charAt(0))) {
- String response = "$EAVACK," + type + "," + checksum;
- response += Checksum.nmea(response);
- channel.write(response);
- }
-
- if (type.equals("S") || type.equals("T")) {
- channel.write("$AVCFG,00000000,t*21");
- } else if (type.equals("3")) {
- channel.write("$AVCFG,00000000,d*31");
- } else if (type.equals("X") || type.equals("4")) {
- channel.write("$AVCFG,00000000,x*2D");
- }
-
- }
-
- return position;
- }
-
-}
diff --git a/src/org/traccar/protocol/MaestroProtocol.java b/src/org/traccar/protocol/MaestroProtocol.java
deleted file mode 100644
index 5e0b69916..000000000
--- a/src/org/traccar/protocol/MaestroProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.FixedLengthFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class MaestroProtocol extends BaseProtocol {
-
- public MaestroProtocol() {
- super("maestro");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new FixedLengthFrameDecoder(160));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new MaestroProtocolDecoder(MaestroProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ManPowerProtocol.java b/src/org/traccar/protocol/ManPowerProtocol.java
deleted file mode 100644
index 60ce8b282..000000000
--- a/src/org/traccar/protocol/ManPowerProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ManPowerProtocol extends BaseProtocol {
-
- public ManPowerProtocol() {
- super("manpower");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new ManPowerProtocolDecoder(ManPowerProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/MegastekProtocol.java b/src/org/traccar/protocol/MegastekProtocol.java
deleted file mode 100644
index b28a05b92..000000000
--- a/src/org/traccar/protocol/MegastekProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class MegastekProtocol extends BaseProtocol {
-
- public MegastekProtocol() {
- super("megastek");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new MegastekFrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new MegastekProtocolDecoder(MegastekProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/MeitrackProtocol.java b/src/org/traccar/protocol/MeitrackProtocol.java
deleted file mode 100644
index e89825da5..000000000
--- a/src/org/traccar/protocol/MeitrackProtocol.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class MeitrackProtocol extends BaseProtocol {
-
- public MeitrackProtocol() {
- super("meitrack");
- setSupportedDataCommands(
- Command.TYPE_POSITION_SINGLE,
- Command.TYPE_ENGINE_STOP,
- Command.TYPE_ENGINE_RESUME,
- Command.TYPE_ALARM_ARM,
- Command.TYPE_ALARM_DISARM,
- Command.TYPE_REQUEST_PHOTO,
- Command.TYPE_SEND_SMS);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new MeitrackFrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new MeitrackProtocolEncoder());
- pipeline.addLast("objectDecoder", new MeitrackProtocolDecoder(MeitrackProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- server = new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new MeitrackProtocolEncoder());
- pipeline.addLast("objectDecoder", new MeitrackProtocolDecoder(MeitrackProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
deleted file mode 100644
index 486f406a5..000000000
--- a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
+++ /dev/null
@@ -1,84 +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.protocol;
-
-import java.util.TimeZone;
-
-import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-
-public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
-
- @Override
- public String formatValue(String key, Object value) {
-
- if (key.equals(Command.KEY_ENABLE)) {
- return (Boolean) value ? "1" : "0";
- } else if (key.equals(Command.KEY_TIMEZONE)) {
- return String.format("%+03d", TimeZone.getTimeZone((String) value).getRawOffset() / 3600000);
- } else if (key.equals(Command.KEY_INDEX)) {
- switch (((Number) value).intValue()) {
- case 0:
- return "A";
- case 1:
- return "B";
- case 2:
- return "C";
- default:
- return null;
- }
- }
-
- return null;
- }
-
- @Override
- protected Object encodeCommand(Command command) {
-
- initDevicePassword(command, "123456");
-
- switch (command.getType()) {
- case Command.TYPE_SET_TIMEZONE:
- return formatCommand(command, "{%s}L{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_TIMEZONE);
- case Command.TYPE_VOICE_MONITORING:
- return formatCommand(command, "{%s}P{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
- case Command.TYPE_ALARM_SPEED:
- return formatCommand(command, "{%s}J1{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
- case Command.TYPE_ALARM_GEOFENCE:
- return formatCommand(command, "{%s}R1{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_RADIUS);
- case Command.TYPE_ALARM_VIBRATION:
- return formatCommand(command, "{%s}W1,{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
- case Command.TYPE_SET_AGPS:
- return formatCommand(command, "{%s}AGPS{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
- case Command.TYPE_ALARM_FALL:
- return formatCommand(command, "{%s}F{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
- case Command.TYPE_MODE_POWER_SAVING:
- return formatCommand(command, "{%s}SP{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
- case Command.TYPE_MODE_DEEP_SLEEP:
- return formatCommand(command, "{%s}DS{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
- case Command.TYPE_SOS_NUMBER:
- return formatCommand(command, "{%s}{%s}1,{%s}", this,
- Command.KEY_DEVICE_PASSWORD, Command.KEY_INDEX, Command.KEY_PHONE);
- case Command.TYPE_SET_INDICATOR:
- return formatCommand(command, "{%s}LED{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- return null;
- }
- }
-
-}
diff --git a/src/org/traccar/protocol/MtxProtocol.java b/src/org/traccar/protocol/MtxProtocol.java
deleted file mode 100644
index 58b2361cd..000000000
--- a/src/org/traccar/protocol/MtxProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class MtxProtocol extends BaseProtocol {
-
- public MtxProtocol() {
- super("mtx");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new MtxProtocolDecoder(MtxProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/MxtProtocol.java b/src/org/traccar/protocol/MxtProtocol.java
deleted file mode 100644
index 03c1da71e..000000000
--- a/src/org/traccar/protocol/MxtProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class MxtProtocol extends BaseProtocol {
-
- public MxtProtocol() {
- super("mxt");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new MxtFrameDecoder());
- pipeline.addLast("objectDecoder", new MxtProtocolDecoder(MxtProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/NavigilProtocol.java b/src/org/traccar/protocol/NavigilProtocol.java
deleted file mode 100644
index 6646c6cc6..000000000
--- a/src/org/traccar/protocol/NavigilProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class NavigilProtocol extends BaseProtocol {
-
- public NavigilProtocol() {
- super("navigil");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new NavigilFrameDecoder());
- pipeline.addLast("objectDecoder", new NavigilProtocolDecoder(NavigilProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/NavisProtocol.java b/src/org/traccar/protocol/NavisProtocol.java
deleted file mode 100644
index 771c9d61d..000000000
--- a/src/org/traccar/protocol/NavisProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class NavisProtocol extends BaseProtocol {
-
- public NavisProtocol() {
- super("navis");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(4 * 1024, 12, 2, 2, 0));
- pipeline.addLast("objectDecoder", new NavisProtocolDecoder(NavisProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/NavisProtocolDecoder.java b/src/org/traccar/protocol/NavisProtocolDecoder.java
deleted file mode 100644
index 8d4e367ab..000000000
--- a/src/org/traccar/protocol/NavisProtocolDecoder.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright 2012 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.LinkedList;
-import java.util.List;
-
-public class NavisProtocolDecoder extends BaseProtocolDecoder {
-
- private String prefix;
- private long deviceUniqueId, serverId;
-
- public NavisProtocolDecoder(NavisProtocol protocol) {
- super(protocol);
- }
-
- public static final int F10 = 0x01;
- public static final int F20 = 0x02;
- public static final int F30 = 0x03;
- public static final int F40 = 0x04;
- public static final int F50 = 0x05;
- public static final int F51 = 0x15;
- public static final int F52 = 0x25;
-
- private static boolean isFormat(int type, int... types) {
- for (int i : types) {
- if (type == i) {
- return true;
- }
- }
- return false;
- }
-
- private static final class ParseResult {
- private final long id;
- private final Position position;
-
- private ParseResult(long id, Position position) {
- this.id = id;
- this.position = position;
- }
-
- public long getId() {
- return id;
- }
-
- public Position getPosition() {
- return position;
- }
- }
-
- private ParseResult parsePosition(DeviceSession deviceSession, ChannelBuffer buf) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- position.setDeviceId(deviceSession.getDeviceId());
-
- int format;
- if (buf.getUnsignedByte(buf.readerIndex()) == 0) {
- format = buf.readUnsignedShort();
- } else {
- format = buf.readUnsignedByte();
- }
- position.set("format", format);
-
- long index = buf.readUnsignedInt();
- position.set(Position.KEY_INDEX, index);
-
- position.set(Position.KEY_EVENT, buf.readUnsignedShort());
-
- buf.skipBytes(6); // event time
-
- short armedStatus = buf.readUnsignedByte();
- position.set(Position.KEY_ARMED, armedStatus & 0x7F);
- if (BitUtil.check(armedStatus, 7)) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
- position.set(Position.KEY_STATUS, buf.readUnsignedByte());
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
-
- if (isFormat(format, F10, F20, F30)) {
- position.set(Position.KEY_OUTPUT, buf.readUnsignedShort());
- } else if (isFormat(format, F40, F50, F51, F52)) {
- position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
- }
-
- if (isFormat(format, F10, F20, F30, F40)) {
- position.set(Position.KEY_INPUT, buf.readUnsignedShort());
- } else if (isFormat(format, F50, F51, F52)) {
- position.set(Position.KEY_INPUT, buf.readUnsignedByte());
- }
-
- position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
-
- if (isFormat(format, F10, F20, F30)) {
- position.set(Position.PREFIX_TEMP + 1, buf.readShort());
- }
-
- if (isFormat(format, F10, F20, F50, F52)) {
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
- }
-
- // Impulse counters
- if (isFormat(format, F20, F50, F51, F52)) {
- buf.readUnsignedInt();
- buf.readUnsignedInt();
- }
-
- if (isFormat(format, F20, F50, F51, F52)) {
- int locationStatus = buf.readUnsignedByte();
- position.setValid(BitUtil.check(locationStatus, 1));
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte() + 1, buf.readUnsignedByte());
- position.setTime(dateBuilder.getDate());
-
- position.setLatitude(buf.readFloat() / Math.PI * 180);
- position.setLongitude(buf.readFloat() / Math.PI * 180);
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloat()));
- position.setCourse(buf.readUnsignedShort());
-
- position.set(Position.KEY_ODOMETER, buf.readFloat() * 1000);
- position.set(Position.KEY_DISTANCE, buf.readFloat());
-
- // Segment times
- buf.readUnsignedShort();
- buf.readUnsignedShort();
- }
-
- // Other
- if (isFormat(format, F51, F52)) {
- buf.readUnsignedShort();
- buf.readByte();
- buf.readUnsignedShort();
- buf.readUnsignedShort();
- buf.readByte();
- buf.readUnsignedShort();
- buf.readUnsignedShort();
- buf.readByte();
- buf.readUnsignedShort();
- }
-
- // Four temperature sensors
- if (isFormat(format, F40, F52)) {
- buf.readByte();
- buf.readByte();
- buf.readByte();
- buf.readByte();
- }
-
- return new ParseResult(index, position);
- }
-
- private Object processSingle(DeviceSession deviceSession, Channel channel, ChannelBuffer buf) {
- ParseResult result = parsePosition(deviceSession, buf);
-
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 8);
- response.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "*<T", StandardCharsets.US_ASCII));
- response.writeInt((int) result.getId());
- sendReply(channel, response);
-
- if (result.getPosition().getFixTime() == null) {
- return null;
- }
-
- return result.getPosition();
- }
-
- private Object processArray(DeviceSession deviceSession, Channel channel, ChannelBuffer buf) {
- List<Position> positions = new LinkedList<>();
- int count = buf.readUnsignedByte();
-
- for (int i = 0; i < count; i++) {
- Position position = parsePosition(deviceSession, buf).getPosition();
- if (position.getFixTime() != null) {
- positions.add(position);
- }
- }
-
- ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 8);
- response.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "*<A", StandardCharsets.US_ASCII));
- response.writeByte(count);
- sendReply(channel, response);
-
- if (positions.isEmpty()) {
- return null;
- }
-
- return positions;
- }
-
- private Object processHandshake(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
- buf.readByte(); // semicolon symbol
- if (getDeviceSession(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII)) != null) {
- sendReply(channel, ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "*<S", StandardCharsets.US_ASCII));
- }
- return null;
- }
-
- private static short checksum(ChannelBuffer buf) {
- short sum = 0;
- for (int i = 0; i < buf.readableBytes(); i++) {
- sum ^= buf.getUnsignedByte(i);
- }
- return sum;
- }
-
- private void sendReply(Channel channel, ChannelBuffer data) {
- ChannelBuffer header = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 16);
- header.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, prefix, StandardCharsets.US_ASCII));
- header.writeInt((int) deviceUniqueId);
- header.writeInt((int) serverId);
- header.writeShort(data.readableBytes());
- header.writeByte(checksum(data));
- header.writeByte(checksum(header));
-
- if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer(header, data));
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- prefix = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII);
- buf.skipBytes(prefix.length()); // prefix @NTC by default
- serverId = buf.readUnsignedInt();
- deviceUniqueId = buf.readUnsignedInt();
- int length = buf.readUnsignedShort();
- buf.skipBytes(2); // header and data XOR checksum
-
- if (length == 0) {
- return null; // keep alive message
- }
-
- String type = buf.toString(buf.readerIndex(), 3, StandardCharsets.US_ASCII);
- buf.skipBytes(type.length());
-
- if (type.equals("*>S")) {
- return processHandshake(channel, remoteAddress, buf);
- } else {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession != null) {
- if (type.equals("*>T")) {
- return processSingle(deviceSession, channel, buf);
- } else if (type.equals("*>A")) {
- return processArray(deviceSession, channel, buf);
- }
- }
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/ObdDongleProtocol.java b/src/org/traccar/protocol/ObdDongleProtocol.java
deleted file mode 100644
index 6547a31ab..000000000
--- a/src/org/traccar/protocol/ObdDongleProtocol.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ObdDongleProtocol extends BaseProtocol {
-
- public ObdDongleProtocol() {
- super("obddongle");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1099, 20, 2, 3, 0));
- pipeline.addLast("objectDecoder", new ObdDongleProtocolDecoder(ObdDongleProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/OrionProtocol.java b/src/org/traccar/protocol/OrionProtocol.java
deleted file mode 100644
index f4bfef985..000000000
--- a/src/org/traccar/protocol/OrionProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class OrionProtocol extends BaseProtocol {
-
- public OrionProtocol() {
- super("orion");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new OrionFrameDecoder());
- pipeline.addLast("objectDecoder", new OrionProtocolDecoder(OrionProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/OsmAndProtocol.java b/src/org/traccar/protocol/OsmAndProtocol.java
deleted file mode 100644
index 785f4bd75..000000000
--- a/src/org/traccar/protocol/OsmAndProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class OsmAndProtocol extends BaseProtocol {
-
- public OsmAndProtocol() {
- super("osmand");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("objectDecoder", new OsmAndProtocolDecoder(OsmAndProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/org/traccar/protocol/OsmAndProtocolDecoder.java
deleted file mode 100644
index 15a71c88b..000000000
--- a/src/org/traccar/protocol/OsmAndProtocolDecoder.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2013 - 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpHeaders;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
-import org.jboss.netty.handler.codec.http.QueryStringDecoder;
-import org.joda.time.format.ISODateTimeFormat;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-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.List;
-import java.util.Map;
-
-public class OsmAndProtocolDecoder extends BaseProtocolDecoder {
-
- public OsmAndProtocolDecoder(OsmAndProtocol protocol) {
- super(protocol);
- }
-
- private void sendResponse(Channel channel, HttpResponseStatus status) {
- if (channel != null) {
- HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
- response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0);
- channel.write(response);
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- HttpRequest request = (HttpRequest) msg;
- QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
- Map<String, List<String>> params = decoder.getParameters();
- if (params.isEmpty()) {
- decoder = new QueryStringDecoder(request.getContent().toString(StandardCharsets.US_ASCII), false);
- params = decoder.getParameters();
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setValid(true);
-
- for (Map.Entry<String, List<String>> entry : params.entrySet()) {
- String value = entry.getValue().get(0);
- switch (entry.getKey()) {
- case "id":
- case "deviceid":
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, value);
- if (deviceSession == null) {
- sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
- break;
- case "valid":
- position.setValid(Boolean.parseBoolean(value));
- break;
- case "timestamp":
- try {
- long timestamp = Long.parseLong(value);
- if (timestamp < Integer.MAX_VALUE) {
- timestamp *= 1000;
- }
- position.setTime(new Date(timestamp));
- } catch (NumberFormatException error) {
- if (value.contains("T")) {
- position.setTime(new Date(
- ISODateTimeFormat.dateTimeParser().parseMillis(value)));
- } else {
- DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- position.setTime(dateFormat.parse(value));
- }
- }
- break;
- case "lat":
- position.setLatitude(Double.parseDouble(value));
- break;
- case "lon":
- position.setLongitude(Double.parseDouble(value));
- break;
- case "location":
- String[] location = value.split(",");
- position.setLatitude(Double.parseDouble(location[0]));
- position.setLongitude(Double.parseDouble(location[1]));
- break;
- case "speed":
- position.setSpeed(convertSpeed(Double.parseDouble(value), "kn"));
- break;
- case "bearing":
- case "heading":
- position.setCourse(Double.parseDouble(value));
- break;
- case "altitude":
- position.setAltitude(Double.parseDouble(value));
- break;
- case "accuracy":
- position.setAccuracy(Double.parseDouble(value));
- break;
- case "hdop":
- position.set(Position.KEY_HDOP, Double.parseDouble(value));
- break;
- case "batt":
- position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value));
- break;
- case "driverUniqueId":
- position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
- break;
- default:
- try {
- position.set(entry.getKey(), Double.parseDouble(value));
- } catch (NumberFormatException e) {
- switch (value) {
- case "true":
- position.set(entry.getKey(), true);
- break;
- case "false":
- position.set(entry.getKey(), false);
- break;
- default:
- position.set(entry.getKey(), value);
- break;
- }
- }
- break;
- }
- }
-
- if (position.getFixTime() == null) {
- position.setTime(new Date());
- }
-
- if (position.getDeviceId() != 0) {
- sendResponse(channel, HttpResponseStatus.OK);
- return position;
- } else {
- sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
- return null;
- }
- }
-
-}
diff --git a/src/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/org/traccar/protocol/OwnTracksProtocolDecoder.java
deleted file mode 100644
index f3284d9e2..000000000
--- a/src/org/traccar/protocol/OwnTracksProtocolDecoder.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2017 Jan-Piet Mens (jpmens@gmail.com)
- * Copyright 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
-import org.jboss.netty.handler.codec.http.HttpHeaders;
-import org.jboss.netty.handler.codec.http.HttpRequest;
-import org.jboss.netty.handler.codec.http.HttpResponse;
-import org.jboss.netty.handler.codec.http.HttpResponseStatus;
-import org.jboss.netty.handler.codec.http.HttpVersion;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.model.Position;
-import org.traccar.helper.UnitsConverter;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Date;
-
-import java.io.StringReader;
-import javax.json.Json;
-import javax.json.JsonObject;
-
-public class OwnTracksProtocolDecoder extends BaseProtocolDecoder {
-
- public OwnTracksProtocolDecoder(OwnTracksProtocol protocol) {
- super(protocol);
- }
-
- private void sendResponse(Channel channel, HttpResponseStatus status) {
- if (channel != null) {
- HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
- response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0);
- channel.write(response);
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- HttpRequest request = (HttpRequest) msg;
- JsonObject root = Json.createReader(
- new StringReader(request.getContent().toString(StandardCharsets.US_ASCII))).readObject();
-
- if (!root.containsKey("_type") || !root.getString("_type").equals("location")) {
- sendResponse(channel, HttpResponseStatus.OK);
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setValid(true);
-
- position.setLatitude(root.getJsonNumber("lat").doubleValue());
- position.setLongitude(root.getJsonNumber("lon").doubleValue());
-
- if (root.containsKey("vel")) {
- position.setSpeed(UnitsConverter.knotsFromKph(root.getInt("vel")));
- }
- if (root.containsKey("alt")) {
- position.setAltitude(root.getInt("alt"));
- }
- if (root.containsKey("cog")) {
- position.setCourse(root.getInt("cog"));
- }
- if (root.containsKey("acc")) {
- position.setAccuracy(root.getInt("acc"));
- }
- if (root.containsKey("t")) {
- position.set("t", root.getString("t"));
- }
- if (root.containsKey("batt")) {
- position.set(Position.KEY_BATTERY, root.getInt("batt"));
- }
-
- position.setTime(new Date(root.getJsonNumber("tst").longValue() * 1000));
-
- String uniqueId;
-
- if (root.containsKey("topic")) {
- uniqueId = root.getString("topic");
- if (root.containsKey("tid")) {
- position.set("tid", root.getString("tid"));
- }
- } else {
- uniqueId = root.getString("tid");
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, uniqueId);
- if (deviceSession == null) {
- sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- sendResponse(channel, HttpResponseStatus.OK);
- return position;
- }
-}
diff --git a/src/org/traccar/protocol/PiligrimProtocol.java b/src/org/traccar/protocol/PiligrimProtocol.java
deleted file mode 100644
index a2960f762..000000000
--- a/src/org/traccar/protocol/PiligrimProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
-import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
-import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class PiligrimProtocol extends BaseProtocol {
-
- public PiligrimProtocol() {
- super("piligrim");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
- pipeline.addLast("httpAggregator", new HttpChunkAggregator(16384));
- pipeline.addLast("objectDecoder", new PiligrimProtocolDecoder(PiligrimProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/PretraceProtocol.java b/src/org/traccar/protocol/PretraceProtocol.java
deleted file mode 100644
index 8f0a22851..000000000
--- a/src/org/traccar/protocol/PretraceProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class PretraceProtocol extends BaseProtocol {
-
- public PretraceProtocol() {
- super("pretrace");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new PretraceProtocolDecoder(PretraceProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/PricolProtocol.java b/src/org/traccar/protocol/PricolProtocol.java
deleted file mode 100644
index 0005dc3c1..000000000
--- a/src/org/traccar/protocol/PricolProtocol.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.FixedLengthFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class PricolProtocol extends BaseProtocol {
-
- public PricolProtocol() {
- super("pricol");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new FixedLengthFrameDecoder(64));
- pipeline.addLast("objectDecoder", new PricolProtocolDecoder(PricolProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new PricolProtocolDecoder(PricolProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ProgressProtocol.java b/src/org/traccar/protocol/ProgressProtocol.java
deleted file mode 100644
index 6e2093346..000000000
--- a/src/org/traccar/protocol/ProgressProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class ProgressProtocol extends BaseProtocol {
-
- public ProgressProtocol() {
- super("progress");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, 4, 0));
- pipeline.addLast("objectDecoder", new ProgressProtocolDecoder(ProgressProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/Pt3000Protocol.java b/src/org/traccar/protocol/Pt3000Protocol.java
deleted file mode 100644
index 9c9da3301..000000000
--- a/src/org/traccar/protocol/Pt3000Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Pt3000Protocol extends BaseProtocol {
-
- public Pt3000Protocol() {
- super("pt3000");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, 'd')); // probably wrong
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Pt3000ProtocolDecoder(Pt3000Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Pt502FrameDecoder.java b/src/org/traccar/protocol/Pt502FrameDecoder.java
deleted file mode 100644
index 252c8dd02..000000000
--- a/src/org/traccar/protocol/Pt502FrameDecoder.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2014 - 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-
-public class Pt502FrameDecoder extends FrameDecoder {
-
- private static final int BINARY_HEADER = 5;
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
-
- if (buf.readableBytes() < BINARY_HEADER) {
- return null;
- }
-
- if (buf.getUnsignedByte(buf.readerIndex()) == 0xbf) {
- buf.skipBytes(BINARY_HEADER);
- }
-
- int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r');
- if (index < 0) {
- index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\n');
- }
-
- if (index > 0) {
- ChannelBuffer result = buf.readBytes(index - buf.readerIndex());
- while (buf.readable()
- && (buf.getByte(buf.readerIndex()) == '\r' || buf.getByte(buf.readerIndex()) == '\n')) {
- buf.skipBytes(1);
- }
- return result;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/Pt502Protocol.java b/src/org/traccar/protocol/Pt502Protocol.java
deleted file mode 100644
index 0116422c2..000000000
--- a/src/org/traccar/protocol/Pt502Protocol.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class Pt502Protocol extends BaseProtocol {
-
- public Pt502Protocol() {
- super("pt502");
- setSupportedDataCommands(
- Command.TYPE_CUSTOM,
- Command.TYPE_SET_TIMEZONE,
- Command.TYPE_ALARM_SPEED,
- Command.TYPE_OUTPUT_CONTROL,
- Command.TYPE_REQUEST_PHOTO);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Pt502FrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectEncoder", new Pt502ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Pt502ProtocolDecoder(Pt502Protocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/RaveonProtocol.java b/src/org/traccar/protocol/RaveonProtocol.java
deleted file mode 100644
index e4e100e0b..000000000
--- a/src/org/traccar/protocol/RaveonProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class RaveonProtocol extends BaseProtocol {
-
- public RaveonProtocol() {
- super("raveon");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new RaveonProtocolDecoder(RaveonProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/RecodaProtocol.java b/src/org/traccar/protocol/RecodaProtocol.java
deleted file mode 100644
index daf167fd9..000000000
--- a/src/org/traccar/protocol/RecodaProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class RecodaProtocol extends BaseProtocol {
-
- public RecodaProtocol() {
- super("recoda");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 4, 4, -8, 0));
- pipeline.addLast("objectDecoder", new RecodaProtocolDecoder(RecodaProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/RitiProtocol.java b/src/org/traccar/protocol/RitiProtocol.java
deleted file mode 100644
index 0ff3ce87b..000000000
--- a/src/org/traccar/protocol/RitiProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class RitiProtocol extends BaseProtocol {
-
- public RitiProtocol() {
- super("riti");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 105, 2, 3, 0));
- pipeline.addLast("objectDecoder", new RitiProtocolDecoder(RitiProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/SanavProtocol.java b/src/org/traccar/protocol/SanavProtocol.java
deleted file mode 100644
index 4f463725e..000000000
--- a/src/org/traccar/protocol/SanavProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class SanavProtocol extends BaseProtocol {
-
- public SanavProtocol() {
- super("sanav");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '*'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new SanavProtocolDecoder(SanavProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/SiwiProtocol.java b/src/org/traccar/protocol/SiwiProtocol.java
deleted file mode 100644
index 667e083f1..000000000
--- a/src/org/traccar/protocol/SiwiProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class SiwiProtocol extends BaseProtocol {
-
- public SiwiProtocol() {
- super("siwi");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new SiwiProtocolDecoder(SiwiProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/StarLinkProtocol.java b/src/org/traccar/protocol/StarLinkProtocol.java
deleted file mode 100644
index e71d94fd0..000000000
--- a/src/org/traccar/protocol/StarLinkProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class StarLinkProtocol extends BaseProtocol {
-
- public StarLinkProtocol() {
- super("starlink");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new StarLinkProtocolDecoder(StarLinkProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Stl060Protocol.java b/src/org/traccar/protocol/Stl060Protocol.java
deleted file mode 100644
index 6fe2c5181..000000000
--- a/src/org/traccar/protocol/Stl060Protocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Stl060Protocol extends BaseProtocol {
-
- public Stl060Protocol() {
- super("stl060");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new Stl060FrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Stl060ProtocolDecoder(Stl060Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java
deleted file mode 100644
index 6dfc6f77f..000000000
--- a/src/org/traccar/protocol/SuntechProtocolDecoder.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright 2013 - 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.TimeZone;
-
-public class SuntechProtocolDecoder extends BaseProtocolDecoder {
-
- private int protocolType;
- private boolean hbm;
- private boolean includeAdc;
- private boolean includeTemp;
-
- public SuntechProtocolDecoder(SuntechProtocol protocol) {
- super(protocol);
-
- protocolType = Context.getConfig().getInteger(getProtocolName() + ".protocolType");
- hbm = Context.getConfig().getBoolean(getProtocolName() + ".hbm");
- includeAdc = Context.getConfig().getBoolean(getProtocolName() + ".includeAdc");
- includeTemp = Context.getConfig().getBoolean(getProtocolName() + ".includeTemp");
- }
-
- public void setProtocolType(int protocolType) {
- this.protocolType = protocolType;
- }
-
- public void setHbm(boolean hbm) {
- this.hbm = hbm;
- }
-
- public void setIncludeAdc(boolean includeAdc) {
- this.includeAdc = includeAdc;
- }
-
- public void setIncludeTemp(boolean includeTemp) {
- this.includeTemp = includeTemp;
- }
-
- private Position decode9(
- Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
- int index = 1;
-
- String type = values[index++];
-
- if (!type.equals("Location") && !type.equals("Emergency") && !type.equals("Alert")) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- if (type.equals("Emergency") || type.equals("Alert")) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
-
- if (!type.equals("Alert") || protocolType == 0) {
- position.set(Position.KEY_VERSION_FW, values[index++]);
- }
-
- DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- position.setTime(dateFormat.parse(values[index++] + values[index++]));
-
- if (protocolType == 1) {
- index += 1; // cell
- }
-
- position.setLatitude(Double.parseDouble(values[index++]));
- position.setLongitude(Double.parseDouble(values[index++]));
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
- position.setCourse(Double.parseDouble(values[index++]));
-
- position.setValid(values[index++].equals("1"));
-
- if (protocolType == 1) {
- position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
- }
-
- return position;
- }
-
- private String decodeEmergency(int value) {
- switch (value) {
- case 1:
- return Position.ALARM_SOS;
- case 2:
- return Position.ALARM_PARKING;
- case 3:
- return Position.ALARM_POWER_CUT;
- case 5:
- case 6:
- return Position.ALARM_DOOR;
- case 7:
- return Position.ALARM_MOVEMENT;
- case 8:
- return Position.ALARM_SHOCK;
- default:
- return null;
- }
- }
-
- private String decodeAlert(int value) {
- switch (value) {
- case 1:
- return Position.ALARM_OVERSPEED;
- case 5:
- return Position.ALARM_GEOFENCE_EXIT;
- case 6:
- return Position.ALARM_GEOFENCE_ENTER;
- case 14:
- return Position.ALARM_LOW_BATTERY;
- case 15:
- return Position.ALARM_SHOCK;
- case 16:
- return Position.ALARM_ACCIDENT;
- case 46:
- return Position.ALARM_ACCELERATION;
- case 47:
- return Position.ALARM_BRAKING;
- case 48:
- return Position.ALARM_ACCIDENT;
- case 50:
- return Position.ALARM_JAMMING;
- default:
- return null;
- }
- }
-
- private Position decode235(
- Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException {
- int index = 0;
-
- String type = values[index++].substring(5);
-
- if (!type.equals("STT") && !type.equals("EMG") && !type.equals("EVT") && !type.equals("ALT")) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_TYPE, type);
-
- if (protocol.equals("ST300") || protocol.equals("ST500")) {
- index += 1; // model
- }
-
- position.set(Position.KEY_VERSION_FW, values[index++]);
-
- DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- position.setTime(dateFormat.parse(values[index++] + values[index++]));
-
- if (!protocol.equals("ST500")) {
- index += 1; // cell
- }
-
- position.setLatitude(Double.parseDouble(values[index++]));
- position.setLongitude(Double.parseDouble(values[index++]));
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
- position.setCourse(Double.parseDouble(values[index++]));
-
- position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
-
- position.setValid(values[index++].equals("1"));
-
- position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
- position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
-
- String io = values[index++];
- if (io.length() == 6) {
- position.set(Position.KEY_IGNITION, io.charAt(0) == '1');
- position.set(Position.PREFIX_IN + 1, io.charAt(1) == '1');
- position.set(Position.PREFIX_IN + 2, io.charAt(2) == '1');
- position.set(Position.PREFIX_IN + 3, io.charAt(3) == '1');
- position.set(Position.PREFIX_OUT + 1, io.charAt(4) == '1');
- position.set(Position.PREFIX_OUT + 2, io.charAt(5) == '1');
- }
-
- switch (type) {
- case "STT":
- index += 1; // mode
- position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
- break;
- case "EMG":
- position.set(Position.KEY_ALARM, decodeEmergency(Integer.parseInt(values[index++])));
- break;
- case "EVT":
- position.set(Position.KEY_EVENT, Integer.parseInt(values[index++]));
- break;
- case "ALT":
- position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++])));
- break;
- default:
- break;
- }
-
- if (hbm) {
-
- if (index < values.length) {
- position.set(Position.KEY_HOURS, Integer.parseInt(values[index++]));
- }
-
- if (index < values.length) {
- position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++]));
- }
-
- if (index < values.length && values[index++].equals("0")) {
- position.set(Position.KEY_ARCHIVE, true);
- }
-
- if (includeAdc) {
- position.set(Position.PREFIX_ADC + 1, Double.parseDouble(values[index++]));
- position.set(Position.PREFIX_ADC + 2, Double.parseDouble(values[index++]));
- position.set(Position.PREFIX_ADC + 3, Double.parseDouble(values[index++]));
- }
-
- if (values.length - index >= 2) {
- String driverUniqueId = values[index++];
- if (values[index++].equals("1") && !driverUniqueId.isEmpty()) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
- }
- }
-
- if (includeTemp) {
- for (int i = 1; i <= 3; i++) {
- String temperature = values[index++];
- String value = temperature.substring(temperature.indexOf(':') + 1);
- if (!value.isEmpty()) {
- position.set(Position.PREFIX_TEMP + i, Double.parseDouble(value));
- }
- }
-
- }
-
- }
-
- return position;
- }
-
- private Position decodeUniversal(
- Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
- int index = 0;
-
- String type = values[index++];
-
- if (!type.equals("STT")) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_TYPE, type);
-
- int mask = Integer.parseInt(values[index++], 16);
-
- if (BitUtil.check(mask, 1)) {
- index += 1; // model
- }
-
- if (BitUtil.check(mask, 2)) {
- position.set(Position.KEY_VERSION_FW, values[index++]);
- }
-
- if (BitUtil.check(mask, 3) && values[index++].equals("0")) {
- position.set(Position.KEY_ARCHIVE, true);
- }
-
- if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) {
- DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
- dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- position.setTime(dateFormat.parse(values[index++] + values[index++]));
- }
-
- if (BitUtil.check(mask, 6)) {
- index += 1; // cell
- }
-
- if (BitUtil.check(mask, 7)) {
- index += 1; // mcc
- }
-
- if (BitUtil.check(mask, 8)) {
- index += 1; // mnc
- }
-
- if (BitUtil.check(mask, 9)) {
- index += 1; // lac
- }
-
- if (BitUtil.check(mask, 10)) {
- position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
- }
-
- if (BitUtil.check(mask, 11)) {
- position.setLatitude(Double.parseDouble(values[index++]));
- }
-
- if (BitUtil.check(mask, 12)) {
- position.setLongitude(Double.parseDouble(values[index++]));
- }
-
- if (BitUtil.check(mask, 13)) {
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
- }
-
- if (BitUtil.check(mask, 14)) {
- position.setCourse(Double.parseDouble(values[index++]));
- }
-
- if (BitUtil.check(mask, 15)) {
- position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
- }
-
- if (BitUtil.check(mask, 16)) {
- position.setValid(values[index++].equals("1"));
- }
-
- return position;
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- String[] values = ((String) msg).split(";");
-
- if (values[0].length() < 5) {
- return decodeUniversal(channel, remoteAddress, values);
- } else if (values[0].equals("ST910")) {
- return decode9(channel, remoteAddress, values);
- } else {
- return decode235(channel, remoteAddress, values[0].substring(0, 5), values);
- }
- }
-
-}
diff --git a/src/org/traccar/protocol/SuntechProtocolEncoder.java b/src/org/traccar/protocol/SuntechProtocolEncoder.java
deleted file mode 100644
index 5b1c802fa..000000000
--- a/src/org/traccar/protocol/SuntechProtocolEncoder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-
-public class SuntechProtocolEncoder extends StringProtocolEncoder {
-
- @Override
- protected Object encodeCommand(Command command) {
-
- switch (command.getType()) {
- case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "SA200CMD;{%s};02;Reboot\r", Command.KEY_UNIQUE_ID);
- case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "SA200GTR;{%s};02;\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, "SA200CMD;{%s};02;Enable{%s}\r",
- Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
- } else {
- return formatCommand(command, "SA200CMD;{%s};02;Disable{%s}\r",
- Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
- }
- }
- case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "SA200CMD;{%s};02;Enable1\r", Command.KEY_UNIQUE_ID);
- case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "SA200CMD;{%s};02;Disable1\r", Command.KEY_UNIQUE_ID);
- case Command.TYPE_ALARM_ARM:
- return formatCommand(command, "SA200CMD;{%s};02;Enable2\r", Command.KEY_UNIQUE_ID);
- case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, "SA200CMD;{%s};02;Disable2\r", Command.KEY_UNIQUE_ID);
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/SupermateProtocol.java b/src/org/traccar/protocol/SupermateProtocol.java
deleted file mode 100644
index c9ae86fc1..000000000
--- a/src/org/traccar/protocol/SupermateProtocol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class SupermateProtocol extends BaseProtocol {
-
- public SupermateProtocol() {
- super("supermate");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "#"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new SupermateProtocolDecoder(SupermateProtocol.this));
- }
- });
- }
-}
diff --git a/src/org/traccar/protocol/T55Protocol.java b/src/org/traccar/protocol/T55Protocol.java
deleted file mode 100644
index 402fd46b8..000000000
--- a/src/org/traccar/protocol/T55Protocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class T55Protocol extends BaseProtocol {
-
- public T55Protocol() {
- super("t55");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new T55ProtocolDecoder(T55Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new T55ProtocolDecoder(T55Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/T800xProtocolDecoder.java b/src/org/traccar/protocol/T800xProtocolDecoder.java
deleted file mode 100644
index 6430b1344..000000000
--- a/src/org/traccar/protocol/T800xProtocolDecoder.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BcdUtil;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.ByteOrder;
-
-public class T800xProtocolDecoder extends BaseProtocolDecoder {
-
- public T800xProtocolDecoder(T800xProtocol protocol) {
- super(protocol);
- }
-
- public static final int MSG_LOGIN = 0x01;
- public static final int MSG_GPS = 0x02;
- public static final int MSG_HEARTBEAT = 0x03;
- public static final int MSG_ALARM = 0x04;
- public static final int MSG_COMMAND = 0x81;
-
- private static float readSwappedFloat(ChannelBuffer buf) {
- byte[] bytes = new byte[4];
- buf.readBytes(bytes);
- return ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, bytes).readFloat();
- }
-
- private void sendResponse(Channel channel, int type, ChannelBuffer imei) {
- if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(15);
- response.writeByte(0x23);
- response.writeByte(0x23); // header
- response.writeByte(type);
- response.writeShort(response.capacity()); // length
- response.writeShort(0x0001); // index
- response.writeBytes(imei);
- channel.write(response);
- }
- }
-
- private String decodeAlarm(short value) {
- switch (value) {
- case 3:
- return Position.ALARM_SOS;
- case 4:
- return Position.ALARM_OVERSPEED;
- case 5:
- return Position.ALARM_GEOFENCE_ENTER;
- case 6:
- return Position.ALARM_GEOFENCE_EXIT;
- case 8:
- case 10:
- return Position.ALARM_VIBRATION;
- default:
- break;
- }
- return null;
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.skipBytes(2);
- int type = buf.readUnsignedByte();
- buf.readUnsignedShort(); // length
- int index = buf.readUnsignedShort();
- ChannelBuffer imei = buf.readBytes(8);
-
- DeviceSession deviceSession = getDeviceSession(
- channel, remoteAddress, ChannelBuffers.hexDump(imei).substring(1));
- if (deviceSession == null) {
- return null;
- }
-
- if (type == MSG_LOGIN || type == MSG_ALARM || type == MSG_HEARTBEAT) {
- sendResponse(channel, type, imei);
- }
-
- if (type == MSG_GPS || type == MSG_ALARM) {
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_INDEX, index);
-
- buf.readUnsignedShort(); // acc on interval
- buf.readUnsignedShort(); // acc off interval
- buf.readUnsignedByte(); // angle compensation
- buf.readUnsignedShort(); // distance compensation
- buf.readUnsignedShort(); // speed alarm
-
- int locationStatus = buf.readUnsignedByte();
-
- buf.readUnsignedByte(); // gsensor manager status
- buf.readUnsignedByte(); // other flags
- buf.readUnsignedByte(); // heartbeat
- buf.readUnsignedByte(); // relay status
- buf.readUnsignedShort(); // drag alarm setting
-
- int io = buf.readUnsignedShort();
- position.set(Position.KEY_IGNITION, BitUtil.check(io, 14));
- position.set("ac", BitUtil.check(io, 13));
-
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
-
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
-
- buf.readUnsignedByte(); // reserved
-
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
-
- int battery = BcdUtil.readInteger(buf, 2);
- if (battery == 0) {
- battery = 100;
- }
- position.set(Position.KEY_BATTERY, battery);
-
- DateBuilder dateBuilder = new DateBuilder()
- .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));
-
- if (BitUtil.check(locationStatus, 6)) {
-
- position.setValid(!BitUtil.check(locationStatus, 7));
- position.setTime(dateBuilder.getDate());
- position.setAltitude(readSwappedFloat(buf));
- position.setLongitude(readSwappedFloat(buf));
- position.setLatitude(readSwappedFloat(buf));
- position.setSpeed(UnitsConverter.knotsFromKph(
- BcdUtil.readInteger(buf, 4) * 0.1));
- position.setCourse(buf.readUnsignedShort());
-
- } else {
-
- getLastLocation(position, dateBuilder.getDate());
-
- byte[] array = new byte[16];
- buf.readBytes(array);
- ChannelBuffer swapped = ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, array);
-
- position.setNetwork(new Network(CellTower.from(
- swapped.readUnsignedShort(), swapped.readUnsignedShort(),
- swapped.readUnsignedShort(), swapped.readUnsignedShort())));
-
- // two more cell towers
-
- }
-
- return position;
-
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/TaipProtocol.java b/src/org/traccar/protocol/TaipProtocol.java
deleted file mode 100644
index cbfc44122..000000000
--- a/src/org/traccar/protocol/TaipProtocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TaipProtocol extends BaseProtocol {
-
- public TaipProtocol() {
- super("taip");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '<'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new TaipProtocolDecoder(TaipProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new TaipProtocolDecoder(TaipProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TelicProtocol.java b/src/org/traccar/protocol/TelicProtocol.java
deleted file mode 100644
index fdd0b94c6..000000000
--- a/src/org/traccar/protocol/TelicProtocol.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class TelicProtocol extends BaseProtocol {
-
- public TelicProtocol() {
- super("telic");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new TelicFrameDecoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new TelicProtocolDecoder(TelicProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/TeltonikaProtocol.java b/src/org/traccar/protocol/TeltonikaProtocol.java
deleted file mode 100644
index d0177da97..000000000
--- a/src/org/traccar/protocol/TeltonikaProtocol.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.util.List;
-
-public class TeltonikaProtocol extends BaseProtocol {
-
- public TeltonikaProtocol() {
- super("teltonika");
- setSupportedDataCommands(
- Command.TYPE_CUSTOM);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new TeltonikaFrameDecoder());
- pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder());
- pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder());
- pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/ThinkRaceProtocol.java b/src/org/traccar/protocol/ThinkRaceProtocol.java
deleted file mode 100644
index 98f43e2e3..000000000
--- a/src/org/traccar/protocol/ThinkRaceProtocol.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class ThinkRaceProtocol extends BaseProtocol {
-
- public ThinkRaceProtocol() {
- super("thinkrace");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2 + 12 + 1 + 1, 2, 2, 0));
- pipeline.addLast("objectDecoder", new ThinkRaceProtocolDecoder(ThinkRaceProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Tk102Protocol.java b/src/org/traccar/protocol/Tk102Protocol.java
deleted file mode 100644
index 962f2401b..000000000
--- a/src/org/traccar/protocol/Tk102Protocol.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Tk102Protocol extends BaseProtocol {
-
- public Tk102Protocol() {
- super("tk102");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 1 + 1 + 10, 1, 1, 0));
- pipeline.addLast("objectDecoder", new Tk102ProtocolDecoder(Tk102Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Tk103Protocol.java b/src/org/traccar/protocol/Tk103Protocol.java
deleted file mode 100644
index 07a68e2d8..000000000
--- a/src/org/traccar/protocol/Tk103Protocol.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2017 Christoph Krey (c@ckrey.de)
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.util.List;
-
-public class Tk103Protocol extends BaseProtocol {
-
- public Tk103Protocol() {
- super("tk103");
- setSupportedDataCommands(
- Command.TYPE_POSITION_SINGLE,
- Command.TYPE_POSITION_PERIODIC,
- Command.TYPE_POSITION_STOP,
- Command.TYPE_GET_VERSION,
- Command.TYPE_REBOOT_DEVICE,
- Command.TYPE_SET_ODOMETER,
- Command.TYPE_ENGINE_STOP,
- Command.TYPE_ENGINE_RESUME);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new Tk103ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Tk103ProtocolDecoder(Tk103Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new Tk103ProtocolEncoder());
- pipeline.addLast("objectDecoder", new Tk103ProtocolDecoder(Tk103Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/org/traccar/protocol/Tk103ProtocolEncoder.java
deleted file mode 100644
index 9e49b6ff1..000000000
--- a/src/org/traccar/protocol/Tk103ProtocolEncoder.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2017 Christoph Krey (c@ckrey.de)
- * Copyright 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.protocol;
-
-import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-
-public class Tk103ProtocolEncoder extends StringProtocolEncoder {
-
- @Override
- protected Object encodeCommand(Command command) {
-
- switch (command.getType()) {
- case Command.TYPE_GET_VERSION:
- return formatCommand(command, "({%s}AP07)", Command.KEY_UNIQUE_ID);
- case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "({%s}AT00)", Command.KEY_UNIQUE_ID);
- case Command.TYPE_SET_ODOMETER:
- return formatCommand(command, "({%s}AX01)", Command.KEY_UNIQUE_ID);
- case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "({%s}AP00)", Command.KEY_UNIQUE_ID);
- case Command.TYPE_POSITION_PERIODIC:
- return formatCommand(command, "({%s}AR00%s0000)", Command.KEY_UNIQUE_ID,
- String.format("%04X", command.getInteger(Command.KEY_FREQUENCY)));
- case Command.TYPE_POSITION_STOP:
- return formatCommand(command, "({%s}AR0000000000)", Command.KEY_UNIQUE_ID);
- case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "({%s}AV011)", Command.KEY_UNIQUE_ID);
- case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "({%s}AV010)", Command.KEY_UNIQUE_ID);
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/Tlt2hProtocol.java b/src/org/traccar/protocol/Tlt2hProtocol.java
deleted file mode 100644
index 752b0d8ef..000000000
--- a/src/org/traccar/protocol/Tlt2hProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Tlt2hProtocol extends BaseProtocol {
-
- public Tlt2hProtocol() {
- super("tlt2h");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(32 * 1024, "##\r\n"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Tlt2hProtocolDecoder(Tlt2hProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TmgProtocol.java b/src/org/traccar/protocol/TmgProtocol.java
deleted file mode 100644
index f30d61e9a..000000000
--- a/src/org/traccar/protocol/TmgProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TmgProtocol extends BaseProtocol {
-
- public TmgProtocol() {
- super("tmg");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new TmgProtocolDecoder(TmgProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TopflytechProtocol.java b/src/org/traccar/protocol/TopflytechProtocol.java
deleted file mode 100644
index 7d4b13ee6..000000000
--- a/src/org/traccar/protocol/TopflytechProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TopflytechProtocol extends BaseProtocol {
-
- public TopflytechProtocol() {
- super("topflytech");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new TopflytechProtocolDecoder(TopflytechProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Tr20Protocol.java b/src/org/traccar/protocol/Tr20Protocol.java
deleted file mode 100644
index 8de004be9..000000000
--- a/src/org/traccar/protocol/Tr20Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Tr20Protocol extends BaseProtocol {
-
- public Tr20Protocol() {
- super("tr20");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Tr20ProtocolDecoder(Tr20Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Tr900Protocol.java b/src/org/traccar/protocol/Tr900Protocol.java
deleted file mode 100644
index 40f287efa..000000000
--- a/src/org/traccar/protocol/Tr900Protocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Tr900Protocol extends BaseProtocol {
-
- public Tr900Protocol() {
- super("tr900");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Tr900ProtocolDecoder(Tr900Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new Tr900ProtocolDecoder(Tr900Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TrackboxProtocol.java b/src/org/traccar/protocol/TrackboxProtocol.java
deleted file mode 100644
index c1aa5ac6a..000000000
--- a/src/org/traccar/protocol/TrackboxProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TrackboxProtocol extends BaseProtocol {
-
- public TrackboxProtocol() {
- super("trackbox");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new TrackboxProtocolDecoder(TrackboxProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TrakMateProtocol.java b/src/org/traccar/protocol/TrakMateProtocol.java
deleted file mode 100644
index f6d9bf457..000000000
--- a/src/org/traccar/protocol/TrakMateProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TrakMateProtocol extends BaseProtocol {
-
- public TrakMateProtocol() {
- super("trakmate");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new TrakMateProtocolDecoder(TrakMateProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TramigoFrameDecoder.java b/src/org/traccar/protocol/TramigoFrameDecoder.java
deleted file mode 100644
index 20992c04b..000000000
--- a/src/org/traccar/protocol/TramigoFrameDecoder.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-
-import java.nio.ByteOrder;
-
-public class TramigoFrameDecoder extends LengthFieldBasedFrameDecoder {
-
- public TramigoFrameDecoder() {
- super(1024, 6, 2, -8, 0);
- }
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
-
- if (buf.readableBytes() < 20) {
- return null;
- }
-
- // Swap byte order for legacy protocol
- if (buf.getUnsignedByte(buf.readerIndex()) == 0x80) {
- int length = buf.readableBytes();
- byte[] bytes = new byte[length];
- buf.getBytes(buf.readerIndex(), bytes);
-
- ChannelBuffer result = (ChannelBuffer) super.decode(
- ctx, channel, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, bytes));
- if (result != null) {
- buf.skipBytes(result.readableBytes());
- }
- return result;
- }
-
- return super.decode(ctx, channel, buf);
- }
-
-}
diff --git a/src/org/traccar/protocol/TramigoProtocol.java b/src/org/traccar/protocol/TramigoProtocol.java
deleted file mode 100644
index 28673c97b..000000000
--- a/src/org/traccar/protocol/TramigoProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.nio.ByteOrder;
-import java.util.List;
-
-public class TramigoProtocol extends BaseProtocol {
-
- public TramigoProtocol() {
- super("tramigo");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new TramigoFrameDecoder());
- pipeline.addLast("objectDecoder", new TramigoProtocolDecoder(TramigoProtocol.this));
- }
- };
- server.setEndianness(ByteOrder.LITTLE_ENDIAN);
- serverList.add(server);
- }
-
-}
diff --git a/src/org/traccar/protocol/TrvProtocol.java b/src/org/traccar/protocol/TrvProtocol.java
deleted file mode 100644
index 348ccd92a..000000000
--- a/src/org/traccar/protocol/TrvProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TrvProtocol extends BaseProtocol {
-
- public TrvProtocol() {
- super("trv");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new TrvProtocolDecoder(TrvProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/Tt8850Protocol.java b/src/org/traccar/protocol/Tt8850Protocol.java
deleted file mode 100644
index 79dc031bc..000000000
--- a/src/org/traccar/protocol/Tt8850Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Tt8850Protocol extends BaseProtocol {
-
- public Tt8850Protocol() {
- super("tt8850");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "$"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Tt8850ProtocolDecoder(Tt8850Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/TzoneProtocol.java b/src/org/traccar/protocol/TzoneProtocol.java
deleted file mode 100644
index 38d5b139a..000000000
--- a/src/org/traccar/protocol/TzoneProtocol.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class TzoneProtocol extends BaseProtocol {
-
- public TzoneProtocol() {
- super("tzone");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(256, 2, 2, 2, 0));
- pipeline.addLast("objectDecoder", new TzoneProtocolDecoder(TzoneProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/UproProtocol.java b/src/org/traccar/protocol/UproProtocol.java
deleted file mode 100644
index c00f859ee..000000000
--- a/src/org/traccar/protocol/UproProtocol.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class UproProtocol extends BaseProtocol {
-
- public UproProtocol() {
- super("upro");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new UproProtocolDecoder(UproProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/UproProtocolDecoder.java b/src/org/traccar/protocol/UproProtocolDecoder.java
deleted file mode 100644
index 7a0dca8a2..000000000
--- a/src/org/traccar/protocol/UproProtocolDecoder.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.text.ParseException;
-import java.util.regex.Pattern;
-
-public class UproProtocolDecoder extends BaseProtocolDecoder {
-
- public UproProtocolDecoder(UproProtocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN_HEADER = new PatternBuilder()
- .text("*")
- .expression("..20")
- .expression("([01])") // ack
- .number("(d+),") // device id
- .expression("(.)") // type
- .expression("(.)") // subtype
- .any()
- .compile();
-
- private static final Pattern PATTERN_LOCATION = new PatternBuilder()
- .number("(dd)(dd)(dd)") // time (hhmmss)
- .number("(dd)(dd)(dddd)") // latitude
- .number("(ddd)(dd)(dddd)") // longitude
- .number("(d)") // flags
- .number("(dd)") // speed
- .number("(dd)") // course
- .number("(dd)(dd)(dd)") // date (ddmmyy)
- .compile();
-
- private void decodeLocation(Position position, String data) {
- Parser parser = new Parser(PATTERN_LOCATION, data);
- if (parser.matches()) {
-
- 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));
- if (!BitUtil.check(flags, 1)) {
- position.setLatitude(-position.getLatitude());
- }
- if (!BitUtil.check(flags, 2)) {
- position.setLongitude(-position.getLongitude());
- }
-
- position.setSpeed(parser.nextInt(0) * 2);
- position.setCourse(parser.nextInt(0) * 10);
-
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
- position.setTime(dateBuilder.getDate());
-
- }
- }
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- if (buf.getByte(buf.readerIndex()) != '*') {
- throw new ParseException(null, 0);
- }
-
- int headerIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '&');
- if (headerIndex < 0) {
- headerIndex = buf.writerIndex();
- }
- String header = buf.readBytes(headerIndex - buf.readerIndex()).toString(StandardCharsets.US_ASCII);
-
- Parser parser = new Parser(PATTERN_HEADER, header);
- if (!parser.matches()) {
- return null;
- }
-
- boolean reply = parser.next().equals("1");
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- String type = parser.next();
- String subtype = parser.next();
-
- if (reply && channel != null) {
- channel.write("*MG20Y" + type + subtype + "#");
- }
-
- while (buf.readable()) {
-
- buf.readByte(); // skip delimiter
-
- byte dataType = buf.readByte();
-
- int delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '&');
- if (delimiterIndex < 0) {
- delimiterIndex = buf.writerIndex();
- }
-
- ChannelBuffer data = buf.readBytes(delimiterIndex - buf.readerIndex());
-
- switch (dataType) {
- case 'A':
- decodeLocation(position, data.toString(StandardCharsets.US_ASCII));
- break;
- case 'B':
- position.set(Position.KEY_STATUS, data.toString(StandardCharsets.US_ASCII));
- break;
- case 'C':
- long odometer = 0;
- while (data.readable()) {
- odometer <<= 4;
- odometer += data.readByte() - (byte) '0';
- }
- position.set(Position.KEY_ODOMETER, odometer * 2 * 1852 / 3600);
- break;
- case 'P':
- position.setNetwork(new Network(CellTower.from(
- Integer.parseInt(data.readBytes(4).toString(StandardCharsets.US_ASCII)),
- Integer.parseInt(data.readBytes(4).toString(StandardCharsets.US_ASCII)),
- Integer.parseInt(data.readBytes(4).toString(StandardCharsets.US_ASCII), 16),
- Integer.parseInt(data.readBytes(4).toString(StandardCharsets.US_ASCII), 16))));
- break;
- case 'Q':
- position.set("obd-pid", ChannelBuffers.hexDump(data));
- break;
- case 'R':
- position.set("odb-travel", ChannelBuffers.hexDump(data));
- break;
- case 'S':
- position.set("obd-traffic", ChannelBuffers.hexDump(data));
- break;
- default:
- break;
- }
-
- }
-
- if (position.getLatitude() != 0 && position.getLongitude() != 0) {
- return position;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/V680Protocol.java b/src/org/traccar/protocol/V680Protocol.java
deleted file mode 100644
index 98c64830b..000000000
--- a/src/org/traccar/protocol/V680Protocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class V680Protocol extends BaseProtocol {
-
- public V680Protocol() {
- super("v680");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "##"));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new V680ProtocolDecoder(V680Protocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new V680ProtocolDecoder(V680Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/VisiontekProtocol.java b/src/org/traccar/protocol/VisiontekProtocol.java
deleted file mode 100644
index c6dd09562..000000000
--- a/src/org/traccar/protocol/VisiontekProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class VisiontekProtocol extends BaseProtocol {
-
- public VisiontekProtocol() {
- super("visiontek");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new VisiontekProtocolDecoder(VisiontekProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/VtfmsProtocol.java b/src/org/traccar/protocol/VtfmsProtocol.java
deleted file mode 100644
index 61e0bf2b9..000000000
--- a/src/org/traccar/protocol/VtfmsProtocol.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class VtfmsProtocol extends BaseProtocol {
-
- public VtfmsProtocol() {
- super("vtfms");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new VtfmsFrameDecoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new VtfmsProtocolDecoder(VtfmsProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new VtfmsProtocolDecoder(VtfmsProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/WatchFrameDecoder.java b/src/org/traccar/protocol/WatchFrameDecoder.java
deleted file mode 100644
index 826a8b4d0..000000000
--- a/src/org/traccar/protocol/WatchFrameDecoder.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 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.protocol;
-
-import org.jboss.netty.buffer.ChannelBuffer;
-import org.jboss.netty.buffer.ChannelBuffers;
-import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.ChannelHandlerContext;
-import org.jboss.netty.handler.codec.frame.FrameDecoder;
-
-import java.nio.charset.StandardCharsets;
-
-public class WatchFrameDecoder extends FrameDecoder {
-
- public static final int MESSAGE_HEADER = 20;
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
-
- if (buf.readableBytes() >= MESSAGE_HEADER) {
- ChannelBuffer lengthBuffer = ChannelBuffers.dynamicBuffer();
- buf.getBytes(buf.readerIndex() + MESSAGE_HEADER - 4 - 1, lengthBuffer, 4);
- int length = Integer.parseInt(lengthBuffer.toString(StandardCharsets.US_ASCII), 16) + MESSAGE_HEADER + 1;
- if (buf.readableBytes() >= length) {
- ChannelBuffer frame = ChannelBuffers.dynamicBuffer();
- int endIndex = buf.readerIndex() + length;
- while (buf.readerIndex() < endIndex) {
- byte b = buf.readByte();
- if (b == 0x7D) {
- switch (buf.readByte()) {
- case 0x01:
- frame.writeByte(0x7D);
- break;
- case 0x02:
- frame.writeByte(0x5B);
- break;
- case 0x03:
- frame.writeByte(0x5D);
- break;
- case 0x04:
- frame.writeByte(0x2C);
- break;
- case 0x05:
- frame.writeByte(0x2A);
- break;
- default:
- throw new IllegalArgumentException();
- }
- } else {
- frame.writeByte(b);
- }
- }
- return frame;
- }
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/WatchProtocolEncoder.java b/src/org/traccar/protocol/WatchProtocolEncoder.java
deleted file mode 100644
index d2d3b52d1..000000000
--- a/src/org/traccar/protocol/WatchProtocolEncoder.java
+++ /dev/null
@@ -1,138 +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.protocol;
-
-import org.traccar.StringProtocolEncoder;
-import org.traccar.helper.Log;
-import org.traccar.model.Command;
-
-import javax.xml.bind.DatatypeConverter;
-import java.nio.charset.StandardCharsets;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-
-public class WatchProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
-
- @Override
- public String formatValue(String key, Object value) {
- if (key.equals(Command.KEY_TIMEZONE)) {
- double offset = TimeZone.getTimeZone((String) value).getRawOffset() / 3600000.0;
- DecimalFormat fmt = new DecimalFormat("+#.##;-#.##", DecimalFormatSymbols.getInstance(Locale.US));
- return fmt.format(offset);
- }
-
- return null;
- }
-
-
- @Override
- protected String formatCommand(Command command, String format, String... keys) {
- String content = formatCommand(command, format, this, keys);
- return String.format("[CS*%s*%04x*%s]",
- getUniqueId(command.getDeviceId()), content.length(), content);
- }
-
- private int getEnableFlag(Command command) {
- if (command.getBoolean(Command.KEY_ENABLE)) {
- return 1;
- } else {
- return 0;
- }
- }
-
- private static Map<Byte, Byte> mapping = new HashMap<>();
-
- static {
- mapping.put((byte) 0x7d, (byte) 0x01);
- mapping.put((byte) 0x5B, (byte) 0x02);
- mapping.put((byte) 0x5D, (byte) 0x03);
- mapping.put((byte) 0x2C, (byte) 0x04);
- mapping.put((byte) 0x2A, (byte) 0x05);
- }
-
- private String getBinaryData(Command command) {
- byte[] data = DatatypeConverter.parseHexBinary(command.getString(Command.KEY_DATA));
-
- int encodedLength = data.length;
- for (byte b : data) {
- if (mapping.containsKey(b)) {
- encodedLength += 1;
- }
- }
-
- int index = 0;
- byte[] encodedData = new byte[encodedLength];
-
- for (byte b : data) {
- Byte replacement = mapping.get(b);
- if (replacement != null) {
- encodedData[index] = 0x7D;
- index += 1;
- encodedData[index] = replacement;
- } else {
- encodedData[index] = b;
- }
- index += 1;
- }
-
- return new String(encodedData, StandardCharsets.US_ASCII);
- }
-
- @Override
- protected Object encodeCommand(Command command) {
-
- switch (command.getType()) {
- case Command.TYPE_CUSTOM:
- return formatCommand(command, command.getString(Command.KEY_DATA));
- case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "RG");
- case Command.TYPE_SOS_NUMBER:
- return formatCommand(command, "SOS{%s},{%s}", Command.KEY_INDEX, Command.KEY_PHONE);
- case Command.TYPE_ALARM_SOS:
- return formatCommand(command, "SOSSMS," + getEnableFlag(command));
- case Command.TYPE_ALARM_BATTERY:
- return formatCommand(command, "LOWBAT," + getEnableFlag(command));
- case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "RESET");
- case Command.TYPE_ALARM_REMOVE:
- return formatCommand(command, "REMOVE," + getEnableFlag(command));
- case Command.TYPE_SILENCE_TIME:
- return formatCommand(command, "SILENCETIME,{%s}", Command.KEY_DATA);
- case Command.TYPE_ALARM_CLOCK:
- return formatCommand(command, "REMIND,{%s}", Command.KEY_DATA);
- case Command.TYPE_SET_PHONEBOOK:
- return formatCommand(command, "PHB,{%s}", Command.KEY_DATA);
- case Command.TYPE_VOICE_MESSAGE:
- return formatCommand(command, "TK," + getBinaryData(command));
- case Command.TYPE_POSITION_PERIODIC:
- return formatCommand(command, "UPLOAD,{%s}", Command.KEY_FREQUENCY);
- case Command.TYPE_SET_TIMEZONE:
- return formatCommand(command, "LZ,,{%s}", Command.KEY_TIMEZONE);
- case Command.TYPE_SET_INDICATOR:
- return formatCommand(command, "FLOWER,{%s}", Command.KEY_DATA);
- default:
- Log.warning(new UnsupportedOperationException(command.getType()));
- break;
- }
-
- return null;
- }
-
-}
diff --git a/src/org/traccar/protocol/WialonProtocol.java b/src/org/traccar/protocol/WialonProtocol.java
deleted file mode 100644
index 02da154e2..000000000
--- a/src/org/traccar/protocol/WialonProtocol.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.Context;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-public class WialonProtocol extends BaseProtocol {
-
- public WialonProtocol() {
- super("wialon");
- setSupportedDataCommands(
- Command.TYPE_REBOOT_DEVICE,
- Command.TYPE_SEND_USSD,
- Command.TYPE_IDENTIFICATION,
- Command.TYPE_OUTPUT_CONTROL);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(4 * 1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- boolean utf8 = Context.getConfig().getBoolean(getName() + ".utf8");
- if (utf8) {
- pipeline.addLast("stringDecoder", new StringDecoder(StandardCharsets.UTF_8));
- } else {
- pipeline.addLast("stringDecoder", new StringDecoder());
- }
- pipeline.addLast("objectEncoder", new WialonProtocolEncoder());
- pipeline.addLast("objectDecoder", new WialonProtocolDecoder(WialonProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/WondexProtocol.java b/src/org/traccar/protocol/WondexProtocol.java
deleted file mode 100644
index ef25265aa..000000000
--- a/src/org/traccar/protocol/WondexProtocol.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-import org.traccar.model.Command;
-
-import java.util.List;
-
-public class WondexProtocol extends BaseProtocol {
-
- public WondexProtocol() {
- super("wondex");
- setTextCommandEncoder(new WondexProtocolEncoder());
- setSupportedCommands(
- Command.TYPE_GET_DEVICE_STATUS,
- Command.TYPE_GET_MODEM_STATUS,
- Command.TYPE_REBOOT_DEVICE,
- Command.TYPE_POSITION_SINGLE,
- Command.TYPE_GET_VERSION,
- Command.TYPE_IDENTIFICATION);
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new WondexFrameDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new WondexProtocolEncoder());
- pipeline.addLast("objectDecoder", new WondexProtocolDecoder(WondexProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectEncoder", new WondexProtocolEncoder());
- pipeline.addLast("objectDecoder", new WondexProtocolDecoder(WondexProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/XirgoProtocol.java b/src/org/traccar/protocol/XirgoProtocol.java
deleted file mode 100644
index 9d7475308..000000000
--- a/src/org/traccar/protocol/XirgoProtocol.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.protocol;
-
-import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
-import org.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.CharacterDelimiterFrameDecoder;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class XirgoProtocol extends BaseProtocol {
-
- public XirgoProtocol() {
- super("xirgo");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "##"));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new XirgoProtocolDecoder(XirgoProtocol.this));
- }
- });
- serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new XirgoProtocolDecoder(XirgoProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/XirgoProtocolDecoder.java b/src/org/traccar/protocol/XirgoProtocolDecoder.java
deleted file mode 100644
index b1442170d..000000000
--- a/src/org/traccar/protocol/XirgoProtocolDecoder.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright 2015 - 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.protocol;
-
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
-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 XirgoProtocolDecoder extends BaseProtocolDecoder {
-
- public XirgoProtocolDecoder(XirgoProtocol protocol) {
- super(protocol);
- }
-
- private Boolean newFormat;
-
- private static final Pattern PATTERN_OLD = new PatternBuilder()
- .text("$$")
- .number("(d+),") // imei
- .number("(d+),") // event
- .number("(dddd)/(dd)/(dd),") // date (yyyy/mm/dd)
- .number("(dd):(dd):(dd),") // time (hh:mm:ss)
- .number("(-?d+.?d*),") // latitude
- .number("(-?d+.?d*),") // longitude
- .number("(-?d+.?d*),") // altitude
- .number("(d+.?d*),") // speed
- .number("(d+.?d*),") // course
- .number("(d+),") // satellites
- .number("(d+.?d*),") // hdop
- .number("(d+.d+),") // battery
- .number("(d+),") // gsm
- .number("(d+.?d*),") // odometer
- .number("(d+),") // gps
- .any()
- .compile();
-
- private static final Pattern PATTERN_NEW = new PatternBuilder()
- .text("$$")
- .number("(d+),") // imei
- .number("(d+),") // event
- .number("(dddd)/(dd)/(dd),") // date (yyyy/mm/dd)
- .number("(dd):(dd):(dd),") // time (hh:mm:ss)
- .number("(-?d+.?d*),") // latitude
- .number("(-?d+.?d*),") // longitude
- .number("(-?d+.?d*),") // altitude
- .number("(d+.?d*),") // speed
- .number("d+.?d*,") // acceleration
- .number("d+.?d*,") // deceleration
- .number("d+,")
- .number("(d+.?d*),") // course
- .number("(d+),") // satellites
- .number("(d+.?d*),") // hdop
- .number("(d+.?d*),") // odometer
- .number("(d+.?d*),") // fuel consumption
- .number("(d+.d+),") // battery
- .number("(d+),") // gsm
- .number("(d+),") // gps
- .groupBegin()
- .number("d,") // reset mode
- .expression("([01])") // input 1
- .expression("([01])") // input 1
- .expression("([01])") // input 1
- .expression("([01]),") // output 1
- .number("(d+.?d*),") // adc 1
- .number("(d+.?d*),") // fuel level
- .number("d+,") // engine load
- .number("(d+),") // engine hours
- .number("(d+),") // oil pressure
- .number("(d+),") // oil level
- .number("(-?d+),") // oil temperature
- .number("(d+),") // coolant pressure
- .number("(d+),") // coolant level
- .number("(-?d+)") // coolant temperature
- .groupEnd("?")
- .any()
- .compile();
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- String sentence = (String) msg;
-
- Parser parser;
- if (newFormat == null) {
- parser = new Parser(PATTERN_NEW, sentence);
- if (parser.matches()) {
- newFormat = true;
- } else {
- parser = new Parser(PATTERN_OLD, sentence);
- if (parser.matches()) {
- newFormat = false;
- } else {
- return null;
- }
- }
- } else {
- if (newFormat) {
- parser = new Parser(PATTERN_NEW, sentence);
- } else {
- parser = new Parser(PATTERN_OLD, sentence);
- }
- if (!parser.matches()) {
- return null;
- }
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_EVENT, parser.next());
-
- position.setTime(parser.nextDateTime());
-
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
- position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
-
- position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.set(Position.KEY_HDOP, parser.nextDouble());
-
- if (newFormat) {
- position.set(Position.KEY_ODOMETER, UnitsConverter.metersFromMiles(parser.nextDouble(0)));
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
- }
-
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
- position.set(Position.KEY_RSSI, parser.nextDouble());
-
- if (!newFormat) {
- position.set(Position.KEY_ODOMETER, UnitsConverter.metersFromMiles(parser.nextDouble(0)));
- }
-
- position.setValid(parser.nextInt(0) == 1);
-
- if (newFormat && parser.hasNext(13)) {
- position.set(Position.PREFIX_IN + 1, parser.nextInt());
- position.set(Position.PREFIX_IN + 2, parser.nextInt());
- position.set(Position.PREFIX_IN + 3, parser.nextInt());
- position.set(Position.PREFIX_OUT + 1, parser.nextInt());
- position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
- position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
- position.set(Position.KEY_HOURS, parser.nextInt());
- position.set("oilPressure", parser.nextInt());
- position.set("oilLevel", parser.nextInt());
- position.set("oilTemp", parser.nextInt());
- position.set("coolantPressure", parser.nextInt());
- position.set("coolantLevel", parser.nextInt());
- position.set("coolantTemp", parser.nextInt());
- }
-
- return position;
- }
-
-}
diff --git a/src/org/traccar/protocol/Xt013Protocol.java b/src/org/traccar/protocol/Xt013Protocol.java
deleted file mode 100644
index ad3e24df0..000000000
--- a/src/org/traccar/protocol/Xt013Protocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class Xt013Protocol extends BaseProtocol {
-
- public Xt013Protocol() {
- super("xt013");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Xt013ProtocolDecoder(Xt013Protocol.this));
- }
- });
- }
-
-}
diff --git a/src/org/traccar/protocol/YwtProtocol.java b/src/org/traccar/protocol/YwtProtocol.java
deleted file mode 100644
index 412365ecb..000000000
--- a/src/org/traccar/protocol/YwtProtocol.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.jboss.netty.bootstrap.ServerBootstrap;
-import org.jboss.netty.channel.ChannelPipeline;
-import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
-import org.jboss.netty.handler.codec.string.StringDecoder;
-import org.jboss.netty.handler.codec.string.StringEncoder;
-import org.traccar.BaseProtocol;
-import org.traccar.TrackerServer;
-
-import java.util.List;
-
-public class YwtProtocol extends BaseProtocol {
-
- public YwtProtocol() {
- super("ywt");
- }
-
- @Override
- public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
- @Override
- protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new YwtProtocolDecoder(YwtProtocol.this));
- }
- });
- }
-
-}
diff --git a/src/test/java/org/traccar/BaseTest.java b/src/test/java/org/traccar/BaseTest.java
new file mode 100644
index 000000000..0b2c616ce
--- /dev/null
+++ b/src/test/java/org/traccar/BaseTest.java
@@ -0,0 +1,34 @@
+package org.traccar;
+
+import io.netty.buffer.ByteBuf;
+import org.traccar.database.MediaManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BaseTest {
+
+ public static class MockMediaManager extends MediaManager {
+ Map<String, ByteBuf> files = new HashMap<>();
+
+ MockMediaManager() {
+ super("");
+ }
+
+ @Override
+ public String writeFile(String uniqueId, ByteBuf buf, String extension) {
+ String fileName = uniqueId + "/mock." + extension;
+ files.put(fileName, buf);
+ return fileName;
+ }
+
+ public ByteBuf readFile(String fileName) {
+ return files.get(fileName);
+ }
+ }
+
+ static {
+ Context.init(new TestIdentityManager(), new MockMediaManager());
+ }
+
+}
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
new file mode 100644
index 000000000..bfff4eef3
--- /dev/null
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -0,0 +1,331 @@
+package org.traccar;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpVersion;
+import org.traccar.helper.DataConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Command;
+import org.traccar.model.Position;
+
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class ProtocolTest extends BaseTest {
+
+ protected Position position(String time, boolean valid, double lat, double lon) throws ParseException {
+
+ Position position = new Position();
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(time));
+ position.setValid(valid);
+ position.setLatitude(lat);
+ position.setLongitude(lon);
+
+ return position;
+ }
+
+ private String concatenateStrings(String... strings) {
+ StringBuilder builder = new StringBuilder();
+ for (String s : strings) {
+ builder.append(s);
+ }
+ return builder.toString();
+ }
+
+ protected ByteBuf concatenateBuffers(ByteBuf... buffers) {
+ ByteBuf result = Unpooled.buffer();
+ for (ByteBuf buf : buffers) {
+ result.writeBytes(buf);
+ }
+ return result;
+ }
+
+ protected ByteBuf binary(String... data) {
+ return Unpooled.wrappedBuffer(DataConverter.parseHex(concatenateStrings(data)));
+ }
+
+ protected String text(String... data) {
+ return concatenateStrings(data);
+ }
+
+ protected ByteBuf buffer(String... data) {
+ return Unpooled.copiedBuffer(concatenateStrings(data), StandardCharsets.ISO_8859_1);
+ }
+
+ protected DefaultFullHttpRequest request(String url) {
+ return request(HttpMethod.GET, url);
+ }
+
+ protected DefaultFullHttpRequest request(HttpMethod method, String url) {
+ return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url);
+ }
+
+ protected DefaultFullHttpRequest request(HttpMethod method, String url, ByteBuf data) {
+ return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url, data);
+ }
+
+ protected DefaultFullHttpRequest request(HttpMethod method, String url, HttpHeaders headers) {
+ return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url, Unpooled.buffer(), headers, new DefaultHttpHeaders());
+ }
+
+ protected void verifyNotNull(BaseProtocolDecoder decoder, Object object) throws Exception {
+ assertNotNull(decoder.decode(null, null, object));
+ }
+
+ protected void verifyNull(Object object) {
+ assertNull(object);
+ }
+
+ protected void verifyNull(BaseProtocolDecoder decoder, Object object) throws Exception {
+ assertNull(decoder.decode(null, null, object));
+ }
+
+ protected void verifyAttribute(BaseProtocolDecoder decoder, Object object, String key, Object expected) throws Exception {
+ Object decodedObject = decoder.decode(null, null, object);
+ Position position;
+ if (decodedObject instanceof Collection) {
+ position = (Position) ((Collection) decodedObject).iterator().next();
+ } else {
+ position = (Position) decodedObject;
+ }
+ switch (key) {
+ case "speed":
+ assertEquals(expected, position.getSpeed());
+ break;
+ case "course":
+ assertEquals(expected, position.getCourse());
+ break;
+ default:
+ assertEquals(expected, position.getAttributes().get(key));
+ break;
+ }
+ }
+
+ protected void verifyAttributes(BaseProtocolDecoder decoder, Object object) throws Exception {
+ verifyDecodedPosition(decoder.decode(null, null, object), false, true, null);
+ }
+
+ protected void verifyPosition(BaseProtocolDecoder decoder, Object object) throws Exception {
+ verifyDecodedPosition(decoder.decode(null, null, object), true, false, null);
+ }
+
+ protected void verifyPosition(BaseProtocolDecoder decoder, Object object, Position position) throws Exception {
+ verifyDecodedPosition(decoder.decode(null, null, object), true, false, position);
+ }
+
+ protected void verifyPositions(BaseProtocolDecoder decoder, Object object) throws Exception {
+ verifyDecodedList(decoder.decode(null, null, object), true, null);
+ }
+
+ protected void verifyPositions(BaseProtocolDecoder decoder, boolean checkLocation, Object object) throws Exception {
+ verifyDecodedList(decoder.decode(null, null, object), checkLocation, null);
+ }
+
+ protected void verifyPositions(BaseProtocolDecoder decoder, Object object, Position position) throws Exception {
+ verifyDecodedList(decoder.decode(null, null, object), true, position);
+ }
+
+ private void verifyDecodedList(Object decodedObject, boolean checkLocation, Position expected) {
+
+ assertNotNull("list is null", decodedObject);
+ assertTrue("not a list", decodedObject instanceof List);
+ assertFalse("list is empty", ((List) decodedObject).isEmpty());
+
+ for (Object item : (List) decodedObject) {
+ verifyDecodedPosition(item, checkLocation, false, expected);
+ }
+
+ }
+
+ private void verifyDecodedPosition(Object decodedObject, boolean checkLocation, boolean checkAttributes, Position expected) {
+
+ assertNotNull("position is null", decodedObject);
+ assertTrue("not a position", decodedObject instanceof Position);
+
+ Position position = (Position) decodedObject;
+
+ if (checkLocation) {
+
+ if (expected != null) {
+
+ if (expected.getFixTime() != null) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ assertEquals("time", dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime()));
+ }
+ assertEquals("valid", expected.getValid(), position.getValid());
+ assertEquals("latitude", expected.getLatitude(), position.getLatitude(), 0.00001);
+ assertEquals("longitude", expected.getLongitude(), position.getLongitude(), 0.00001);
+
+ } else {
+
+ assertNotNull(position.getServerTime());
+ assertNotNull(position.getFixTime());
+ assertTrue("year > 1999", position.getFixTime().after(new Date(915148800000L)));
+ assertTrue("time < +25 hours",
+ position.getFixTime().getTime() < System.currentTimeMillis() + 25 * 3600000);
+
+ assertTrue("latitude >= -90", position.getLatitude() >= -90);
+ assertTrue("latitude <= 90", position.getLatitude() <= 90);
+
+ assertTrue("longitude >= -180", position.getLongitude() >= -180);
+ assertTrue("longitude <= 180", position.getLongitude() <= 180);
+
+ }
+
+ assertTrue("altitude >= -12262", position.getAltitude() >= -12262);
+ assertTrue("altitude <= 18000", position.getAltitude() <= 18000);
+
+ assertTrue("speed >= 0", position.getSpeed() >= 0);
+ assertTrue("speed <= 869", position.getSpeed() <= 869);
+
+ assertTrue("course >= 0", position.getCourse() >= 0);
+ assertTrue("course <= 360", position.getCourse() <= 360);
+
+ assertNotNull("protocol is null", position.getProtocol());
+
+ }
+
+ Map<String, Object> attributes = position.getAttributes();
+
+ if (checkAttributes) {
+ assertFalse("no attributes", attributes.isEmpty());
+ }
+
+ if (attributes.containsKey(Position.KEY_INDEX)) {
+ assertTrue(attributes.get(Position.KEY_INDEX) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_HDOP)) {
+ assertTrue(attributes.get(Position.KEY_HDOP) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_VDOP)) {
+ assertTrue(attributes.get(Position.KEY_VDOP) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_PDOP)) {
+ assertTrue(attributes.get(Position.KEY_PDOP) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_SATELLITES)) {
+ assertTrue(attributes.get(Position.KEY_SATELLITES) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_SATELLITES_VISIBLE)) {
+ assertTrue(attributes.get(Position.KEY_SATELLITES_VISIBLE) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_RSSI)) {
+ assertTrue(attributes.get(Position.KEY_RSSI) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_ODOMETER)) {
+ assertTrue(attributes.get(Position.KEY_ODOMETER) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_RPM)) {
+ assertTrue(attributes.get(Position.KEY_RPM) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_FUEL_LEVEL)) {
+ assertTrue(attributes.get(Position.KEY_FUEL_LEVEL) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_POWER)) {
+ assertTrue(attributes.get(Position.KEY_POWER) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_BATTERY)) {
+ assertTrue(attributes.get(Position.KEY_BATTERY) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_BATTERY_LEVEL)) {
+ int batteryLevel = ((Number) attributes.get(Position.KEY_BATTERY_LEVEL)).intValue();
+ assertTrue(batteryLevel <= 100 && batteryLevel >= 0);
+ }
+
+ if (attributes.containsKey(Position.KEY_CHARGE)) {
+ assertTrue(attributes.get(Position.KEY_CHARGE) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_IGNITION)) {
+ assertTrue(attributes.get(Position.KEY_IGNITION) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_MOTION)) {
+ assertTrue(attributes.get(Position.KEY_MOTION) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_ARCHIVE)) {
+ assertTrue(attributes.get(Position.KEY_ARCHIVE) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
+ assertTrue(attributes.get(Position.KEY_DRIVER_UNIQUE_ID) instanceof String);
+ }
+
+ if (attributes.containsKey(Position.KEY_STEPS)) {
+ assertTrue(attributes.get(Position.KEY_STEPS) instanceof Number);
+ }
+
+ if (attributes.containsKey(Position.KEY_ROAMING)) {
+ assertTrue(attributes.get(Position.KEY_ROAMING) instanceof Boolean);
+ }
+
+ if (attributes.containsKey(Position.KEY_HOURS)) {
+ assertTrue(attributes.get(Position.KEY_HOURS) instanceof Number);
+ }
+
+ if (position.getNetwork() != null && position.getNetwork().getCellTowers() != null) {
+ for (CellTower cellTower : position.getNetwork().getCellTowers()) {
+ checkInteger(cellTower.getMobileCountryCode(), 0, 999);
+ checkInteger(cellTower.getMobileNetworkCode(), 0, 999);
+ checkInteger(cellTower.getLocationAreaCode(), 1, 65535);
+ checkInteger(cellTower.getCellId(), 0, 268435455);
+ }
+ }
+
+ }
+
+ private void checkInteger(Object value, int min, int max) {
+ assertNotNull("value is null", value);
+ assertTrue("not int or long", value instanceof Integer || value instanceof Long);
+ long number = ((Number) value).longValue();
+ assertTrue("value too low", number >= min);
+ assertTrue("value too high", number <= max);
+ }
+
+ protected void verifyCommand(
+ BaseProtocolEncoder encoder, Command command, ByteBuf expected) {
+ verifyFrame(expected, encoder.encodeCommand(command));
+ }
+
+ protected void verifyFrame(ByteBuf expected, Object object) {
+ assertNotNull("buffer is null", object);
+ assertTrue("not a buffer", object instanceof ByteBuf);
+ assertEquals(ByteBufUtil.hexDump(expected), ByteBufUtil.hexDump((ByteBuf) object));
+ }
+
+}
diff --git a/src/test/java/org/traccar/TestIdentityManager.java b/src/test/java/org/traccar/TestIdentityManager.java
new file mode 100644
index 000000000..af5dd22df
--- /dev/null
+++ b/src/test/java/org/traccar/TestIdentityManager.java
@@ -0,0 +1,77 @@
+package org.traccar;
+
+import org.traccar.database.IdentityManager;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+
+public final class TestIdentityManager implements IdentityManager {
+
+ private static Device createDevice() {
+ Device device = new Device();
+ device.setId(1);
+ device.setName("test");
+ device.setUniqueId("123456789012345");
+ return device;
+ }
+
+ @Override
+ public long addUnknownDevice(String uniqueId) {
+ return 1;
+ }
+
+ @Override
+ public Device getById(long id) {
+ return createDevice();
+ }
+
+ @Override
+ public Device getByUniqueId(String uniqueId) {
+ return createDevice();
+ }
+
+ @Override
+ public String getDevicePassword(long id, String protocol, String defaultPassword) {
+ return defaultPassword;
+ }
+
+ @Override
+ public Position getLastPosition(long deviceId) {
+ return null;
+ }
+
+ @Override
+ public boolean isLatestPosition(Position position) {
+ return true;
+ }
+
+ @Override
+ public boolean lookupAttributeBoolean(
+ long deviceId, String attributeName, boolean defaultValue, boolean lookupServer, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+ @Override
+ public String lookupAttributeString(
+ long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) {
+ return "alarm,result";
+ }
+
+ @Override
+ public int lookupAttributeInteger(
+ long deviceId, String attributeName, int defaultValue, boolean lookupServer, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+ @Override
+ public long lookupAttributeLong(
+ long deviceId, String attributeName, long defaultValue, boolean lookupServer, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+ @Override
+ public double lookupAttributeDouble(
+ long deviceId, String attributeName, double defaultValue, boolean lookupServer, boolean lookupConfig) {
+ return defaultValue;
+ }
+
+}
diff --git a/src/test/java/org/traccar/WebDataHandlerTest.java b/src/test/java/org/traccar/WebDataHandlerTest.java
new file mode 100644
index 000000000..cfbd71f23
--- /dev/null
+++ b/src/test/java/org/traccar/WebDataHandlerTest.java
@@ -0,0 +1,28 @@
+package org.traccar;
+
+import org.junit.Test;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class WebDataHandlerTest extends ProtocolTest {
+
+ @Test
+ public void testFormatRequest() throws Exception {
+
+ Config config = new Config();
+ config.setString(Keys.FORWARD_URL, "http://localhost/?fixTime={fixTime}&gprmc={gprmc}&name={name}");
+
+ Position position = position("2016-01-01 01:02:03.000", true, 20, 30);
+
+ WebDataHandler handler = new WebDataHandler(config, Context.getIdentityManager(), null, null);
+
+ assertEquals(
+ "http://localhost/?fixTime=1451610123000&gprmc=$GPRMC,010203.000,A,2000.0000,N,03000.0000,E,0.00,0.00,010116,,*05&name=test",
+ handler.formatRequest(position));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/calendar/CalendarTest.java b/src/test/java/org/traccar/calendar/CalendarTest.java
new file mode 100644
index 000000000..56406d4b8
--- /dev/null
+++ b/src/test/java/org/traccar/calendar/CalendarTest.java
@@ -0,0 +1,59 @@
+package org.traccar.calendar;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.Test;
+import org.traccar.model.Calendar;
+
+import net.fortuna.ical4j.data.ParserException;
+
+import static org.junit.Assert.assertTrue;
+
+public class CalendarTest {
+
+ @Test
+ public void testCalendar() throws IOException, ParserException, ParseException, SQLException {
+ String calendarString = "BEGIN:VCALENDAR\n" +
+ "PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN\n" +
+ "VERSION:2.0\n" +
+ "BEGIN:VTIMEZONE\n" +
+ "TZID:Asia/Yekaterinburg\n" +
+ "BEGIN:STANDARD\n" +
+ "TZOFFSETFROM:+0500\n" +
+ "TZOFFSETTO:+0500\n" +
+ "TZNAME:YEKT\n" +
+ "DTSTART:19700101T000000\n" +
+ "END:STANDARD\n" +
+ "END:VTIMEZONE\n" +
+ "BEGIN:VEVENT\n" +
+ "CREATED:20161213T045151Z\n" +
+ "LAST-MODIFIED:20161213T045242Z\n" +
+ "DTSTAMP:20161213T045242Z\n" +
+ "UID:9d000df0-6354-479d-a407-218dac62c7c9\n" +
+ "SUMMARY:Every night\n" +
+ "RRULE:FREQ=DAILY\n" +
+ "DTSTART;TZID=Asia/Yekaterinburg:20161130T230000\n" +
+ "DTEND;TZID=Asia/Yekaterinburg:20161201T070000\n" +
+ "TRANSP:OPAQUE\n" +
+ "END:VEVENT\n" +
+ "END:VCALENDAR";
+ Calendar calendar = new Calendar();
+ calendar.setData(calendarString.getBytes());
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX");
+
+ Date date = format.parse("2016-12-13 22:59:59+05");
+ assertTrue(!calendar.checkMoment(date));
+ date = format.parse("2016-12-13 23:00:01+05");
+ assertTrue(calendar.checkMoment(date));
+
+ date = format.parse("2016-12-13 06:59:59+05");
+ assertTrue(calendar.checkMoment(date));
+ date = format.parse("2016-12-13 07:00:01+05");
+ assertTrue(!calendar.checkMoment(date));
+ }
+}
diff --git a/src/test/java/org/traccar/config/ConfigTest.java b/src/test/java/org/traccar/config/ConfigTest.java
new file mode 100644
index 000000000..13d0ffb04
--- /dev/null
+++ b/src/test/java/org/traccar/config/ConfigTest.java
@@ -0,0 +1,19 @@
+package org.traccar.config;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import org.traccar.config.Config;
+
+public class ConfigTest {
+
+ @Test
+ public void testFormat() {
+ assertEquals("DATABASE_URL", Config.getEnvironmentVariableName("database.url"));
+ assertEquals("DATABASE_CHECK_CONNECTION", Config.getEnvironmentVariableName("database.checkConnection"));
+ assertEquals("DATABASE_MAX_POOL_SIZE", Config.getEnvironmentVariableName("database.maxPoolSize"));
+ assertEquals("DEVICE_MANAGER_LOOKUP_GROUPS_ATTRIBUTE", Config.getEnvironmentVariableName("deviceManager.lookupGroupsAttribute"));
+ assertEquals("COMMAND_FALLBACK_TO_SMS", Config.getEnvironmentVariableName("command.fallbackToSms"));
+ assertEquals("STATUS_TIMEOUT", Config.getEnvironmentVariableName("status.timeout"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/database/DataManagerTest.java b/src/test/java/org/traccar/database/DataManagerTest.java
new file mode 100644
index 000000000..23043e96e
--- /dev/null
+++ b/src/test/java/org/traccar/database/DataManagerTest.java
@@ -0,0 +1,81 @@
+package org.traccar.database;
+
+import org.junit.Test;
+import org.traccar.model.Attribute;
+import org.traccar.model.Device;
+import org.traccar.model.Driver;
+import org.traccar.model.Geofence;
+import org.traccar.model.Group;
+import org.traccar.model.ManagedUser;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class DataManagerTest {
+
+ @Test
+ public void constructObjectQuery() {
+ assertEquals("SELECT * FROM tc_users",
+ DataManager.constructObjectQuery(DataManager.ACTION_SELECT_ALL, User.class, false));
+ assertEquals("DELETE FROM tc_groups WHERE id = :id",
+ DataManager.constructObjectQuery(DataManager.ACTION_DELETE, Group.class, false));
+ assertEquals("SELECT * FROM tc_positions WHERE id = :id",
+ DataManager.constructObjectQuery(DataManager.ACTION_SELECT, Position.class, false));
+
+ String insertDevice = DataManager.constructObjectQuery(DataManager.ACTION_INSERT, Device.class, false);
+ assertFalse(insertDevice.contains("class"));
+ assertFalse(insertDevice.contains("id"));
+ assertFalse(insertDevice.contains("status"));
+ assertFalse(insertDevice.contains("geofenceIds"));
+
+ String updateDeviceStatus = DataManager.constructObjectQuery("update", Device.class, true);
+ assertTrue(updateDeviceStatus.contains("lastUpdate"));
+
+ String updateUser = DataManager.constructObjectQuery(DataManager.ACTION_UPDATE, User.class, false);
+ assertFalse(updateUser.contains("class"));
+ assertFalse(updateUser.contains("password"));
+ assertFalse(updateUser.contains("salt"));
+
+ String updateUserPassword = DataManager.constructObjectQuery(DataManager.ACTION_UPDATE, User.class, true);
+ assertFalse(updateUserPassword.contains("name"));
+ assertTrue(updateUserPassword.contains("hashedPassword"));
+ assertTrue(updateUserPassword.contains("salt"));
+
+ String insertPosition = DataManager.constructObjectQuery(DataManager.ACTION_INSERT, Position.class, false);
+ assertFalse(insertPosition.contains("type"));
+ assertFalse(insertPosition.contains("outdated"));
+
+ }
+
+ @Test
+ public void constructPermissionsQuery() {
+ assertEquals("SELECT userId, deviceId FROM tc_user_device",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, User.class, Device.class));
+
+ assertEquals("SELECT userId, managedUserId FROM tc_user_user",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, User.class, ManagedUser.class));
+
+ assertEquals("SELECT deviceId, driverId FROM tc_device_driver",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, Device.class, Driver.class));
+
+ assertEquals("SELECT groupId, geofenceId FROM tc_group_geofence",
+ DataManager.constructPermissionQuery(DataManager.ACTION_SELECT_ALL, Group.class, Geofence.class));
+
+ assertEquals("INSERT INTO tc_user_device (userId, deviceId) VALUES (:userId, :deviceId)",
+ DataManager.constructPermissionQuery(DataManager.ACTION_INSERT, User.class, Device.class));
+
+ assertEquals("DELETE FROM tc_user_user WHERE userId = :userId AND managedUserId = :managedUserId",
+ DataManager.constructPermissionQuery(DataManager.ACTION_DELETE, User.class, ManagedUser.class));
+
+ assertEquals("INSERT INTO tc_device_geofence (deviceId, geofenceId) VALUES (:deviceId, :geofenceId)",
+ DataManager.constructPermissionQuery(DataManager.ACTION_INSERT, Device.class, Geofence.class));
+
+ assertEquals("DELETE FROM tc_group_attribute WHERE groupId = :groupId AND attributeId = :attributeId",
+ DataManager.constructPermissionQuery(DataManager.ACTION_DELETE, Group.class, Attribute.class));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/database/GroupTreeTest.java b/src/test/java/org/traccar/database/GroupTreeTest.java
new file mode 100644
index 000000000..b547aab60
--- /dev/null
+++ b/src/test/java/org/traccar/database/GroupTreeTest.java
@@ -0,0 +1,56 @@
+package org.traccar.database;
+
+import org.junit.Test;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+
+public class GroupTreeTest {
+
+ private static Group createGroup(long id, String name, long parent) {
+ Group group = new Group();
+ group.setId(id);
+ group.setName(name);
+ group.setGroupId(parent);
+ return group;
+ }
+
+ private static Device createDevice(long id, String name, long parent) {
+ Device device = new Device();
+ device.setId(id);
+ device.setName(name);
+ device.setGroupId(parent);
+ return device;
+ }
+
+ @Test
+ public void testGetDescendants() {
+ Collection<Group> groups = new ArrayList<>();
+ groups.add(createGroup(1, "First", 0));
+ groups.add(createGroup(2, "Second", 1));
+ groups.add(createGroup(3, "Third", 2));
+ groups.add(createGroup(4, "Fourth", 2));
+ groups.add(createGroup(5, "Fifth", 4));
+
+ Collection<Device> devices = new ArrayList<>();
+ devices.add(createDevice(1, "One", 3));
+ devices.add(createDevice(2, "Two", 5));
+ devices.add(createDevice(3, "One", 5));
+
+ GroupTree groupTree = new GroupTree(groups, devices);
+
+ assertEquals(4, groupTree.getGroups(1).size());
+ assertEquals(3, groupTree.getGroups(2).size());
+ assertEquals(0, groupTree.getGroups(3).size());
+ assertEquals(1, groupTree.getGroups(4).size());
+
+ assertEquals(3, groupTree.getDevices(1).size());
+ assertEquals(1, groupTree.getDevices(3).size());
+ assertEquals(2, groupTree.getDevices(4).size());
+ }
+
+}
diff --git a/src/test/java/org/traccar/geocoder/AddressFormatTest.java b/src/test/java/org/traccar/geocoder/AddressFormatTest.java
new file mode 100644
index 000000000..0cc5168ef
--- /dev/null
+++ b/src/test/java/org/traccar/geocoder/AddressFormatTest.java
@@ -0,0 +1,33 @@
+package org.traccar.geocoder;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class AddressFormatTest {
+
+ private void test(Address address, String format, String expected) {
+ assertEquals(expected, new AddressFormat(format).format(address));
+ }
+
+ @Test
+ public void testFormat() {
+
+ Address address = new Address();
+ address.setCountry("NZ");
+ address.setSettlement("Auckland");
+ address.setStreet("Queen St");
+ address.setHouse("1A");
+
+ test(address, "%h %r %t %d %s %c %p", "1A Queen St Auckland NZ");
+ test(address, "%h %r %t", "1A Queen St Auckland");
+ test(address, "%h %r, %t", "1A Queen St, Auckland");
+ test(address, "%h %r, %t %p", "1A Queen St, Auckland");
+ test(address, "%t %d %c", "Auckland NZ");
+ test(address, "%t, %d, %c", "Auckland, NZ");
+ test(address, "%d %c", "NZ");
+ test(address, "%d, %c", "NZ");
+ test(address, "%p", "");
+ }
+
+}
diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java
new file mode 100644
index 000000000..9f59d0b23
--- /dev/null
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -0,0 +1,88 @@
+package org.traccar.geocoder;
+
+import java.util.Locale;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class GeocoderTest {
+
+ static {
+ Locale.setDefault(Locale.US);
+ }
+
+ @Ignore
+ @Test
+ public void testGoogle() {
+ Geocoder geocoder = new GoogleGeocoder(null, null, 0, new AddressFormat());
+ String address = geocoder.getAddress(31.776797, 35.211489, null);
+ assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", address);
+ }
+
+ @Ignore
+ @Test
+ public void testNominatim() {
+ Geocoder geocoder = new NominatimGeocoder(null, null, null, 0, new AddressFormat());
+ String address = geocoder.getAddress(40.7337807, -73.9974401, null);
+ assertEquals("35 West 9th Street, NYC, New York, US", address);
+ }
+
+ @Ignore
+ @Test
+ public void testGisgraphy() {
+ Geocoder geocoder = new GisgraphyGeocoder(new AddressFormat());
+ String address = geocoder.getAddress(48.8530000, 2.3400000, null);
+ assertEquals("Rue du Jardinet, Paris, Île-de-France, FR", address);
+ }
+
+ @Ignore
+ @Test
+ public void testOpenCage() {
+ Geocoder geocoder = new OpenCageGeocoder(
+ "http://api.opencagedata.com/geocode/v1", "SECRET", 0, new AddressFormat());
+ String address = geocoder.getAddress(34.116302, -118.051519, null);
+ assertEquals("Charleston Road, California, US", address);
+ }
+
+ @Ignore
+ @Test
+ public void testGeocodeFarm() {
+ Geocoder geocoder = new GeocodeFarmGeocoder(null, null, 0, new AddressFormat());
+ String address = geocoder.getAddress(34.116302, -118.051519, null);
+ assertEquals("Estrella Avenue, Arcadia, California, United States", address);
+ }
+
+ @Ignore
+ @Test
+ public void testGeocodeXyz() {
+ Geocoder geocoder = new GeocodeXyzGeocoder(null, 0, new AddressFormat());
+ String address = geocoder.getAddress(34.116302, -118.051519, null);
+ assertEquals("605 ESTRELLA AVE, ARCADIA, California United States of America, US", address);
+ }
+
+ @Ignore
+ @Test
+ public void testBan() {
+ Geocoder geocoder = new BanGeocoder(0, new AddressFormat("%f [%d], %c"));
+ String address = geocoder.getAddress(48.8575, 2.2944, null);
+ assertEquals("8 Avenue Gustave Eiffel 75007 Paris [75, Paris, Île-de-France], FR", address);
+ }
+
+ @Ignore
+ @Test
+ public void testHere() {
+ Geocoder geocoder = new HereGeocoder(null, "", "", null, 0, new AddressFormat());
+ String address = geocoder.getAddress(48.8575, 2.2944, null);
+ assertEquals("6 Avenue Gustave Eiffel, Paris, Île-de-France, FRA", address);
+ }
+
+ @Ignore
+ @Test
+ public void testMapmyIndia() {
+ Geocoder geocoder = new MapmyIndiaGeocoder("", "", 0, new AddressFormat("%f"));
+ String address = geocoder.getAddress(28.6129602407977, 77.2294557094574, null);
+ assertEquals("New Delhi, Delhi. 1 m from India Gate pin-110001 (India)", address);
+ }
+}
diff --git a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
new file mode 100644
index 000000000..259a8fb77
--- /dev/null
+++ b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java
@@ -0,0 +1,28 @@
+package org.traccar.geofence;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GeofenceCircleTest {
+
+ @Test
+ public void testCircleWkt() throws ParseException {
+ String test = "CIRCLE (55.75414 37.6204, 100)";
+ GeofenceGeometry geofenceGeometry = new GeofenceCircle();
+ geofenceGeometry.fromWkt(test);
+ assertEquals(geofenceGeometry.toWkt(), test);
+ }
+
+ @Test
+ public void testContainsCircle() throws ParseException {
+ String test = "CIRCLE (55.75414 37.6204, 100)";
+ GeofenceGeometry geofenceGeometry = new GeofenceCircle();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(55.75477, 37.62025));
+ assertTrue(!geofenceGeometry.containsPoint(55.75545, 37.61921));
+ }
+}
diff --git a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java
new file mode 100644
index 000000000..94b73af3a
--- /dev/null
+++ b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java
@@ -0,0 +1,52 @@
+package org.traccar.geofence;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GeofencePolygonTest {
+
+ @Test
+ public void testPolygonWkt() throws ParseException {
+ String test = "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertEquals(geofenceGeometry.toWkt(), test);
+ }
+
+ @Test
+ public void testContainsPolygon() throws ParseException {
+ String test = "POLYGON ((55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(55.75476, 37.61915));
+ assertTrue(!geofenceGeometry.containsPoint(55.75545, 37.61921));
+
+ }
+
+ @Test
+ public void testContainsPolygon180() throws ParseException {
+ String test = "POLYGON ((66.9494 179.838, 66.9508 -179.8496, 66.8406 -180.0014))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(66.9015, -180.0096));
+ assertTrue(geofenceGeometry.containsPoint(66.9015, 179.991));
+ assertTrue(!geofenceGeometry.containsPoint(66.8368, -179.8792));
+
+ }
+
+ @Test
+ public void testContainsPolygon0() throws ParseException {
+ String test = "POLYGON ((51.1966 -0.6207, 51.1897 0.4147, 50.9377 0.5136, 50.8675 -0.6082))";
+ GeofenceGeometry geofenceGeometry = new GeofencePolygon();
+ geofenceGeometry.fromWkt(test);
+ assertTrue(geofenceGeometry.containsPoint(51.0466, -0.0165));
+ assertTrue(geofenceGeometry.containsPoint(51.0466, 0.018));
+ assertTrue(!geofenceGeometry.containsPoint(50.9477, 0.5836));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
new file mode 100644
index 000000000..1e9dcb7c3
--- /dev/null
+++ b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java
@@ -0,0 +1,47 @@
+package org.traccar.geofence;
+
+import java.text.ParseException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GeofencePolylineTest {
+
+ @Test
+ public void testPolylineWkt() throws ParseException {
+ String test = "LINESTRING (55.75474 37.61823, 55.75513 37.61888, 55.7535 37.6222, 55.75315 37.62165)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline();
+ geofenceGeometry.fromWkt(test);
+ assertEquals(geofenceGeometry.toWkt(), test);
+ }
+
+ @Test
+ public void testContainsPolyline1Interval() throws ParseException {
+ String test = "LINESTRING (56.83777 60.59833, 56.83766 60.5968)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 35);
+ assertTrue(geofenceGeometry.containsPoint(56.83801, 60.59748));
+ ((GeofencePolyline) geofenceGeometry).setDistance(15);
+ assertTrue(!geofenceGeometry.containsPoint(56.83801, 60.59748));
+ }
+
+ @Test
+ public void testContainsPolyline3Intervals() throws ParseException {
+ String test = "LINESTRING (56.836 60.6126, 56.8393 60.6114, 56.83887 60.60811, 56.83782 60.5988)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 15);
+ assertTrue(geofenceGeometry.containsPoint(56.83847, 60.60458));
+ assertTrue(!geofenceGeometry.containsPoint(56.83764, 60.59725));
+ assertTrue(!geofenceGeometry.containsPoint(56.83861, 60.60822));
+
+ }
+
+ @Test
+ public void testContainsPolylineNear180() throws ParseException {
+ String test = "LINESTRING (66.9494 179.838, 66.9508 -179.8496)";
+ GeofenceGeometry geofenceGeometry = new GeofencePolyline(test, 25);
+ assertTrue(geofenceGeometry.containsPoint(66.95, 180.0));
+ assertTrue(!geofenceGeometry.containsPoint(66.96, 180.0));
+ assertTrue(!geofenceGeometry.containsPoint(66.9509, -179.83));
+ }
+}
diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
new file mode 100644
index 000000000..2729052d6
--- /dev/null
+++ b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.geolocation;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class GeolocationProviderTest extends BaseTest {
+
+ @Ignore
+ @Test
+ public void test() throws Exception {
+ testLocationProvider();
+ }
+
+ public void testLocationProvider() throws Exception {
+ MozillaGeolocationProvider provider = new MozillaGeolocationProvider(null);
+
+ Network network = new Network(CellTower.from(208, 1, 2, 1234567));
+
+ provider.getLocation(network, new GeolocationProvider.LocationProviderCallback() {
+ @Override
+ public void onSuccess(double latitude, double longitude, double accuracy) {
+ assertEquals(60.07254, latitude, 0.00001);
+ assertEquals(30.30996, longitude, 0.00001);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ fail();
+ }
+ });
+
+ Thread.sleep(Long.MAX_VALUE);
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/ComputedAttributesTest.java b/src/test/java/org/traccar/handler/ComputedAttributesTest.java
new file mode 100644
index 000000000..a76d8169b
--- /dev/null
+++ b/src/test/java/org/traccar/handler/ComputedAttributesTest.java
@@ -0,0 +1,71 @@
+package org.traccar.handler;
+
+import java.util.Date;
+
+import org.junit.Test;
+import org.traccar.config.Config;
+import org.traccar.model.Attribute;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class ComputedAttributesTest {
+
+ @Test
+ public void testComputedAttributes() {
+
+ ComputedAttributesHandler handler = new ComputedAttributesHandler(new Config(), null, null);
+
+ Date date = new Date();
+ Position position = new Position();
+ position.setTime(date);
+ position.setSpeed(42);
+ position.setValid(false);
+ position.set("adc1", 128);
+ position.set("booleanFlag", true);
+ position.set("adc2", 100);
+ position.set("bitFlag", 7);
+ position.set("event", 42);
+ position.set("result", "success");
+ Attribute attribute = new Attribute();
+
+ attribute.setExpression("adc1");
+ assertEquals(128, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("!booleanFlag");
+ assertEquals(false, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("adc2 * 2 + 50");
+ assertEquals(250, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("(bitFlag & 4) != 0");
+ assertEquals(true, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("if (event == 42) \"lowBattery\"");
+ assertEquals("lowBattery", handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("speed > 5 && valid");
+ assertEquals(false, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("fixTime");
+ assertEquals(date, handler.computeAttribute(attribute, position));
+
+ attribute.setExpression("math:pow(adc1, 2)");
+ assertEquals(16384.0, handler.computeAttribute(attribute, position));
+
+ // modification tests
+ attribute.setExpression("adc1 = 256");
+ handler.computeAttribute(attribute, position);
+ assertEquals(128, position.getInteger("adc1"));
+
+ attribute.setExpression("result = \"fail\"");
+ handler.computeAttribute(attribute, position);
+ assertEquals("success", position.getString("result"));
+
+ attribute.setExpression("fixTime = \"2017-10-18 10:00:01\"");
+ handler.computeAttribute(attribute, position);
+ assertEquals(date, position.getFixTime());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/DistanceHandlerTest.java b/src/test/java/org/traccar/handler/DistanceHandlerTest.java
new file mode 100644
index 000000000..f7c6e42cd
--- /dev/null
+++ b/src/test/java/org/traccar/handler/DistanceHandlerTest.java
@@ -0,0 +1,30 @@
+package org.traccar.handler;
+
+import org.junit.Test;
+import org.traccar.config.Config;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class DistanceHandlerTest {
+
+ @Test
+ public void testCalculateDistance() {
+
+ DistanceHandler distanceHandler = new DistanceHandler(new Config(), null);
+
+ Position position = distanceHandler.handlePosition(new Position());
+
+ assertEquals(0.0, position.getAttributes().get(Position.KEY_DISTANCE));
+ assertEquals(0.0, position.getAttributes().get(Position.KEY_TOTAL_DISTANCE));
+
+ position.set(Position.KEY_DISTANCE, 100);
+
+ position = distanceHandler.handlePosition(position);
+
+ assertEquals(100.0, position.getAttributes().get(Position.KEY_DISTANCE));
+ assertEquals(100.0, position.getAttributes().get(Position.KEY_TOTAL_DISTANCE));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/FilterHandlerTest.java b/src/test/java/org/traccar/handler/FilterHandlerTest.java
new file mode 100644
index 000000000..ad8d244a6
--- /dev/null
+++ b/src/test/java/org/traccar/handler/FilterHandlerTest.java
@@ -0,0 +1,88 @@
+package org.traccar.handler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.Position;
+
+import java.util.Date;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class FilterHandlerTest extends BaseTest {
+
+ private FilterHandler passingHandler = new FilterHandler(new Config());
+ private FilterHandler filteringHandler;
+
+ @Before
+ public void before() {
+ Config config = new Config();
+ config.setString(Keys.FILTER_INVALID, String.valueOf(true));
+ config.setString(Keys.FILTER_ZERO, String.valueOf(true));
+ config.setString(Keys.FILTER_DUPLICATE, String.valueOf(true));
+ config.setString(Keys.FILTER_FUTURE, String.valueOf(5 * 60));
+ config.setString(Keys.FILTER_APPROXIMATE, String.valueOf(true));
+ config.setString(Keys.FILTER_STATIC, String.valueOf(true));
+ config.setString(Keys.FILTER_DISTANCE, String.valueOf(10));
+ config.setString(Keys.FILTER_MAX_SPEED, String.valueOf(500));
+ config.setString(Keys.FILTER_SKIP_LIMIT, String.valueOf(10));
+ config.setString(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE, String.valueOf(true));
+ filteringHandler = new FilterHandler(config);
+ }
+
+ private Position createPosition(
+ long deviceId,
+ Date time,
+ boolean valid,
+ double latitude,
+ double longitude,
+ double altitude,
+ double speed,
+ double course) {
+
+ Position position = new Position();
+ position.setDeviceId(deviceId);
+ position.setTime(time);
+ position.setValid(valid);
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ position.setAltitude(altitude);
+ position.setSpeed(speed);
+ position.setCourse(course);
+ return position;
+ }
+
+ @Test
+ public void testFilter() {
+
+ Position position = createPosition(0, new Date(), true, 10, 10, 10, 10, 10);
+
+ assertNotNull(filteringHandler.handlePosition(position));
+ assertNotNull(passingHandler.handlePosition(position));
+
+ position = createPosition(0, new Date(Long.MAX_VALUE), true, 10, 10, 10, 10, 10);
+
+ assertNull(filteringHandler.handlePosition(position));
+ assertNotNull(passingHandler.handlePosition(position));
+
+ position = createPosition(0, new Date(), false, 10, 10, 10, 10, 10);
+
+ assertNull(filteringHandler.handlePosition(position));
+ assertNotNull(passingHandler.handlePosition(position));
+
+ }
+
+ @Test
+ public void testSkipAttributes() {
+
+ Position position = createPosition(0, new Date(), true, 10, 10, 10, 0, 10);
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+
+ assertNotNull(filteringHandler.handlePosition(position));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/MotionHandlerTest.java b/src/test/java/org/traccar/handler/MotionHandlerTest.java
new file mode 100644
index 000000000..9e0859664
--- /dev/null
+++ b/src/test/java/org/traccar/handler/MotionHandlerTest.java
@@ -0,0 +1,21 @@
+package org.traccar.handler;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.traccar.model.Position;
+
+public class MotionHandlerTest {
+
+ @Test
+ public void testCalculateMotion() {
+
+ MotionHandler motionHandler = new MotionHandler(0.01);
+
+ Position position = motionHandler.handlePosition(new Position());
+
+ assertEquals(false, position.getAttributes().get(Position.KEY_MOTION));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java
new file mode 100644
index 000000000..3f0823245
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java
@@ -0,0 +1,30 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.TestIdentityManager;
+import org.traccar.config.Config;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class AlertEventHandlerTest extends BaseTest {
+
+ @Test
+ public void testAlertEventHandler() {
+
+ AlertEventHandler alertEventHandler = new AlertEventHandler(new Config(), new TestIdentityManager());
+
+ Position position = new Position();
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ Map<Event, Position> events = alertEventHandler.analyzePosition(position);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_ALARM, event.getType());
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
new file mode 100644
index 000000000..0ccf9f6b4
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java
@@ -0,0 +1,28 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class CommandResultEventHandlerTest extends BaseTest {
+
+ @Test
+ public void testCommandResultEventHandler() throws Exception {
+
+ CommandResultEventHandler commandResultEventHandler = new CommandResultEventHandler();
+
+ Position position = new Position();
+ position.set(Position.KEY_RESULT, "Test Result");
+ Map<Event, Position> events = commandResultEventHandler.analyzePosition(position);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_COMMAND_RESULT, event.getType());
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
new file mode 100644
index 000000000..dade20fb8
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java
@@ -0,0 +1,27 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.TestIdentityManager;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class IgnitionEventHandlerTest extends BaseTest {
+
+ @Test
+ public void testIgnitionEventHandler() {
+
+ IgnitionEventHandler ignitionEventHandler = new IgnitionEventHandler(new TestIdentityManager());
+
+ Position position = new Position();
+ position.set(Position.KEY_IGNITION, true);
+ position.setValid(true);
+ Map<Event, Position> events = ignitionEventHandler.analyzePosition(position);
+ assertNull(events);
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
new file mode 100644
index 000000000..f57c16635
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java
@@ -0,0 +1,119 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.model.DeviceState;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.reports.model.TripsConfig;
+
+public class MotionEventHandlerTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ @Test
+ public void testMotionWithPosition() throws Exception {
+ MotionEventHandler motionEventHandler = new MotionEventHandler(
+ null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, false, false, 0.01));
+
+ Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
+ position.set(Position.KEY_MOTION, true);
+ position.set(Position.KEY_TOTAL_DISTANCE, 0);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+ Position nextPosition = new Position();
+
+ nextPosition.setTime(date("2017-01-01 00:02:00"));
+ nextPosition.set(Position.KEY_MOTION, true);
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200);
+
+ Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNull(events);
+
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 600);
+ events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+ nextPosition.setTime(date("2017-01-01 00:06:00"));
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200);
+ events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNotNull(event);
+ event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+ }
+
+ @Test
+ public void testMotionWithStatus() throws Exception {
+ MotionEventHandler motionEventHandler = new MotionEventHandler(
+ null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, false, false, 0.01));
+
+ Position position = new Position();
+ position.setTime(new Date(System.currentTimeMillis() - 360000));
+ position.set(Position.KEY_MOTION, true);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+
+ Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState);
+
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+ }
+
+ @Test
+ public void testStopWithPositionIgnition() throws Exception {
+ MotionEventHandler motionEventHandler = new MotionEventHandler(
+ null, null, new TripsConfig(500, 300 * 1000, 300 * 1000, 0, true, false, 0.01));
+
+ Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
+ position.set(Position.KEY_MOTION, false);
+ position.set(Position.KEY_IGNITION, true);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(true);
+ deviceState.setMotionPosition(position);
+
+ Position nextPosition = new Position();
+ nextPosition.setTime(date("2017-01-01 00:02:00"));
+ nextPosition.set(Position.KEY_MOTION, false);
+ nextPosition.set(Position.KEY_IGNITION, false);
+
+ Map<Event, Position> events = motionEventHandler.updateMotionState(deviceState, nextPosition);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_STOPPED, event.getType());
+ assertFalse(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
new file mode 100644
index 000000000..515f37b5d
--- /dev/null
+++ b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java
@@ -0,0 +1,128 @@
+package org.traccar.handler.events;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.model.DeviceState;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class OverspeedEventHandlerTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ private void testOverspeedWithPosition(boolean notRepeat, long geofenceId) throws ParseException {
+ Config config = new Config();
+ config.setString(Keys.EVENT_OVERSPEED_NOT_REPEAT, String.valueOf(notRepeat));
+ config.setString(Keys.EVENT_OVERSPEED_MINIMAL_DURATION, String.valueOf(15));
+ config.setString(Keys.EVENT_OVERSPEED_PREFER_LOWEST, String.valueOf(false));
+ OverspeedEventHandler overspeedEventHandler = new OverspeedEventHandler(config, null, null);
+
+ Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
+ position.setSpeed(50);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setOverspeedState(false);
+
+ Map<Event, Position> events = overspeedEventHandler.updateOverspeedState(deviceState, position, 40, geofenceId);
+ assertNull(events);
+ assertFalse(deviceState.getOverspeedState());
+ assertEquals(position, deviceState.getOverspeedPosition());
+ assertEquals(geofenceId, deviceState.getOverspeedGeofenceId());
+
+ Position nextPosition = new Position();
+ nextPosition.setTime(date("2017-01-01 00:00:10"));
+ nextPosition.setSpeed(55);
+
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNull(events);
+
+ nextPosition.setTime(date("2017-01-01 00:00:20"));
+
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_OVERSPEED, event.getType());
+ assertEquals(50, event.getDouble("speed"), 0.1);
+ assertEquals(40, event.getDouble(OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT), 0.1);
+ assertEquals(geofenceId, event.getGeofenceId());
+
+ assertEquals(notRepeat, deviceState.getOverspeedState());
+ assertNull(deviceState.getOverspeedPosition());
+ assertEquals(0, deviceState.getOverspeedGeofenceId());
+
+ nextPosition.setTime(date("2017-01-01 00:00:30"));
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNull(events);
+ assertEquals(notRepeat, deviceState.getOverspeedState());
+
+ if (notRepeat) {
+ assertNull(deviceState.getOverspeedPosition());
+ assertEquals(0, deviceState.getOverspeedGeofenceId());
+ } else {
+ assertNotNull(deviceState.getOverspeedPosition());
+ assertEquals(geofenceId, deviceState.getOverspeedGeofenceId());
+ }
+
+ nextPosition.setTime(date("2017-01-01 00:00:40"));
+ nextPosition.setSpeed(30);
+
+ events = overspeedEventHandler.updateOverspeedState(deviceState, nextPosition, 40, geofenceId);
+ assertNull(events);
+ assertFalse(deviceState.getOverspeedState());
+ assertNull(deviceState.getOverspeedPosition());
+ assertEquals(0, deviceState.getOverspeedGeofenceId());
+ }
+
+ private void testOverspeedWithStatus(boolean notRepeat) {
+ Config config = new Config();
+ config.setString(Keys.EVENT_OVERSPEED_NOT_REPEAT, String.valueOf(notRepeat));
+ config.setString(Keys.EVENT_OVERSPEED_MINIMAL_DURATION, String.valueOf(15));
+ config.setString(Keys.EVENT_OVERSPEED_PREFER_LOWEST, String.valueOf(false));
+ OverspeedEventHandler overspeedEventHandler = new OverspeedEventHandler(config, null, null);
+
+ Position position = new Position();
+ position.setTime(new Date(System.currentTimeMillis() - 30000));
+ position.setSpeed(50);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setOverspeedState(false);
+ deviceState.setOverspeedPosition(position);
+
+ Map<Event, Position> events = overspeedEventHandler.updateOverspeedState(deviceState, 40);
+
+ assertNotNull(events);
+ Event event = events.keySet().iterator().next();
+ assertEquals(Event.TYPE_DEVICE_OVERSPEED, event.getType());
+ assertEquals(notRepeat, deviceState.getOverspeedState());
+ }
+
+ @Test
+ public void testOverspeedEventHandler() throws Exception {
+ testOverspeedWithPosition(false, 0);
+ testOverspeedWithPosition(true, 0);
+
+ testOverspeedWithPosition(false, 1);
+ testOverspeedWithPosition(true, 1);
+
+ testOverspeedWithStatus(false);
+ testOverspeedWithStatus(true);
+ }
+
+}
diff --git a/src/test/java/org/traccar/handler/protocol/Arnavi4FrameDecoderTest.java b/src/test/java/org/traccar/handler/protocol/Arnavi4FrameDecoderTest.java
new file mode 100644
index 000000000..b634f0cdc
--- /dev/null
+++ b/src/test/java/org/traccar/handler/protocol/Arnavi4FrameDecoderTest.java
@@ -0,0 +1,50 @@
+package org.traccar.protocol;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import java.nio.ByteOrder;
+
+public class Arnavi4FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeValidPackets() throws Exception {
+
+ Arnavi4FrameDecoder decoder = new Arnavi4FrameDecoder();
+
+ Assert.assertEquals( // Valid HEADER v1 packet with IMEI
+ binary(ByteOrder.LITTLE_ENDIAN, "ff22f30c45f5c90f0300"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "ff22f30c45f5c90f0300")));
+
+ Assert.assertEquals( // Valid PACKAGE with one DATA packet
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ Assert.assertEquals( // Valid PACKAGE with two DATA packet
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ Assert.assertEquals( // Valid PACKAGE with one TEXT packet.
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01030700e3f16b50747261636361721b5d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01030700e3f16b50747261636361721b5d")));
+
+ Assert.assertEquals( // Valid PACKAGE with two TEXT packet.
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01030700e3f16b50747261636361721b030700e3f16b50747261636361721b5d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01030700e3f16b50747261636361721b030700e3f16b50747261636361721b5d")));
+
+ Assert.assertEquals( // Valid PACKAGE with one BINARY packet.
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d")));
+
+ Assert.assertEquals( // Valid PACKAGE with two BINARY packet.
+ binary(ByteOrder.LITTLE_ENDIAN, "5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d")));
+
+ Assert.assertEquals( // Valid PACKAGE with answer to server on file transfer
+ binary(ByteOrder.LITTLE_ENDIAN, "5bfd005d"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "5bfd005d")));
+
+ }
+
+} \ No newline at end of file
diff --git a/src/test/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoderTest.java b/src/test/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoderTest.java
new file mode 100644
index 000000000..d789b1c9c
--- /dev/null
+++ b/src/test/java/org/traccar/handler/protocol/Arnavi4ProtocolDecoderTest.java
@@ -0,0 +1,40 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import java.nio.ByteOrder;
+
+public class Arnavi4ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testHeader1Decode() throws Exception {
+
+ Arnavi4ProtocolDecoder decoder;
+
+ decoder = new Arnavi4ProtocolDecoder(new Arnavi4Protocol());
+
+ verifyNull(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid HEADER v1 packet with IMEI
+ "ff22f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid PACKAGE packet with one DATA packet
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+ }
+
+ @Test
+ public void testHeader2Decode() throws Exception {
+
+ Arnavi4ProtocolDecoder decoder;
+
+ decoder = new Arnavi4ProtocolDecoder(new Arnavi4Protocol());
+
+ verifyNull(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid HEADER v2 packet with IMEI
+ "ff23f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN, // Valid PACKAGE packet with two DATA packet
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/BcdUtilTest.java b/src/test/java/org/traccar/helper/BcdUtilTest.java
new file mode 100644
index 000000000..86a32f725
--- /dev/null
+++ b/src/test/java/org/traccar/helper/BcdUtilTest.java
@@ -0,0 +1,24 @@
+package org.traccar.helper;
+
+import io.netty.buffer.Unpooled;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class BcdUtilTest {
+
+ @Test
+ public void testReadInteger() {
+ byte[] buf = {0x01, (byte) 0x90, 0x34};
+ int result = BcdUtil.readInteger(Unpooled.wrappedBuffer(buf), 5);
+ assertEquals(1903, result);
+ }
+
+ @Test
+ public void testReadCoordinate() {
+ byte[] buf = {0x03, (byte) 0x85, 0x22, 0x59, 0x34};
+ double result = BcdUtil.readCoordinate(Unpooled.wrappedBuffer(buf));
+ assertEquals(38.870989, result, 0.00001);
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/BitBufferTest.java b/src/test/java/org/traccar/helper/BitBufferTest.java
new file mode 100644
index 000000000..c2abad36d
--- /dev/null
+++ b/src/test/java/org/traccar/helper/BitBufferTest.java
@@ -0,0 +1,23 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class BitBufferTest {
+
+ @Test
+ public void test() {
+ BitBuffer buffer = new BitBuffer();
+
+ buffer.write(0b100100);
+ buffer.write(0b110110);
+ buffer.write(0b111111);
+ buffer.write(0b111111);
+
+ assertEquals(0b100, buffer.readUnsigned(3));
+ assertEquals(-7, buffer.readSigned(4));
+ assertEquals(0b10110, buffer.readUnsigned(5));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/BitUtilTest.java b/src/test/java/org/traccar/helper/BitUtilTest.java
new file mode 100644
index 000000000..90431bf55
--- /dev/null
+++ b/src/test/java/org/traccar/helper/BitUtilTest.java
@@ -0,0 +1,38 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class BitUtilTest {
+
+ @Test
+ public void testCheck() {
+ assertFalse(BitUtil.check(0, 0));
+ assertTrue(BitUtil.check(1, 0));
+ assertFalse(BitUtil.check(2, 0));
+ }
+
+ @Test
+ public void testBetween() {
+ assertEquals(0, BitUtil.between(0, 0, 0));
+ assertEquals(1, BitUtil.between(1, 0, 1));
+ assertEquals(2, BitUtil.between(2, 0, 2));
+ assertEquals(2, BitUtil.between(6, 0, 2));
+ }
+
+ @Test
+ public void testFrom() {
+ assertEquals(1, BitUtil.from(1, 0));
+ assertEquals(0, BitUtil.from(1, 1));
+ }
+
+ @Test
+ public void testTo() {
+ assertEquals(2, BitUtil.to(2, 2));
+ assertEquals(0, BitUtil.to(2, 1));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java
new file mode 100644
index 000000000..5737b9ff5
--- /dev/null
+++ b/src/test/java/org/traccar/helper/ChecksumTest.java
@@ -0,0 +1,39 @@
+package org.traccar.helper;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+
+public class ChecksumTest {
+
+ @Test
+ public void testCrc8() {
+ ByteBuf buf = Unpooled.copiedBuffer("123456789", StandardCharsets.US_ASCII);
+
+ assertEquals(0xF7, Checksum.crc8(Checksum.CRC8_EGTS, buf.nioBuffer()));
+ assertEquals(0xD0, Checksum.crc8(Checksum.CRC8_ROHC, buf.nioBuffer()));
+ }
+
+ @Test
+ public void testCrc16() {
+ ByteBuf buf = Unpooled.copiedBuffer("123456789", StandardCharsets.US_ASCII);
+
+ assertEquals(0xBB3D, Checksum.crc16(Checksum.CRC16_IBM, buf.nioBuffer()));
+ assertEquals(0x4B37, Checksum.crc16(Checksum.CRC16_MODBUS, buf.nioBuffer()));
+ assertEquals(0x906e, Checksum.crc16(Checksum.CRC16_X25, buf.nioBuffer()));
+ assertEquals(0x29b1, Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.nioBuffer()));
+ assertEquals(0x2189, Checksum.crc16(Checksum.CRC16_KERMIT, buf.nioBuffer()));
+ assertEquals(0x31c3, Checksum.crc16(Checksum.CRC16_XMODEM, buf.nioBuffer()));
+ }
+
+ @Test
+ public void testLuhn() {
+ assertEquals(7, Checksum.luhn(12345678901234L));
+ assertEquals(0, Checksum.luhn(63070019470771L));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/DateBuilderTest.java b/src/test/java/org/traccar/helper/DateBuilderTest.java
new file mode 100644
index 000000000..b6323cc1d
--- /dev/null
+++ b/src/test/java/org/traccar/helper/DateBuilderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+
+public class DateBuilderTest {
+
+ @Test
+ public void testDateBuilder() throws ParseException {
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(2015, 10, 20).setTime(1, 21, 11);
+
+ assertEquals(dateFormat.parse("2015-10-20 01:21:11"), dateBuilder.getDate());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/DateUtilTest.java b/src/test/java/org/traccar/helper/DateUtilTest.java
new file mode 100644
index 000000000..ec42e71ae
--- /dev/null
+++ b/src/test/java/org/traccar/helper/DateUtilTest.java
@@ -0,0 +1,30 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import static org.junit.Assert.assertEquals;
+
+public class DateUtilTest {
+
+ @Test
+ public void testCorrectDate() throws ParseException {
+
+ DateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ assertEquals(f.parse("2015-12-31 23:59:59"),
+ DateUtil.correctDate(f.parse("2016-01-01 00:00:01"), f.parse("2016-01-01 23:59:59"), Calendar.DAY_OF_MONTH));
+
+ assertEquals(f.parse("2016-01-01 00:00:02"),
+ DateUtil.correctDate(f.parse("2016-01-01 00:00:01"), f.parse("2016-01-01 00:00:02"), Calendar.DAY_OF_MONTH));
+
+ assertEquals(f.parse("2016-01-01 00:00:02"),
+ DateUtil.correctDate(f.parse("2016-01-01 00:00:01"), f.parse("2015-12-31 00:00:02"), Calendar.DAY_OF_MONTH));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/DistanceCalculatorTest.java b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java
new file mode 100644
index 000000000..a7457b6c4
--- /dev/null
+++ b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java
@@ -0,0 +1,24 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class DistanceCalculatorTest {
+
+ @Test
+ public void testDistance() {
+ assertEquals(
+ DistanceCalculator.distance(0.0, 0.0, 0.05, 0.05), 7863.0, 10.0);
+ }
+
+ @Test
+ public void testDistanceToLine() {
+ assertEquals(DistanceCalculator.distanceToLine(
+ 56.83801, 60.59748, 56.83777, 60.59833, 56.83766, 60.5968), 33.0, 5.0);
+
+ assertEquals(DistanceCalculator.distanceToLine(
+ 56.83753, 60.59508, 56.83777, 60.59833, 56.83766, 60.5968), 105.0, 5.0);
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/LocationTreeTest.java b/src/test/java/org/traccar/helper/LocationTreeTest.java
new file mode 100644
index 000000000..21604144a
--- /dev/null
+++ b/src/test/java/org/traccar/helper/LocationTreeTest.java
@@ -0,0 +1,30 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class LocationTreeTest {
+
+ @Test
+ public void testLocationTree() {
+
+ List<LocationTree.Item> items = new ArrayList<>();
+ items.add(new LocationTree.Item(1, 1, "a"));
+ items.add(new LocationTree.Item(3, 2, "b"));
+ items.add(new LocationTree.Item(1, 3, "c"));
+ items.add(new LocationTree.Item(4, 3, "d"));
+
+ LocationTree tree = new LocationTree(items);
+
+ assertEquals("a", tree.findNearest(new LocationTree.Item(1f, 1f)).getData());
+ assertEquals("d", tree.findNearest(new LocationTree.Item(10f, 10f)).getData());
+ assertEquals("c", tree.findNearest(new LocationTree.Item(1f, 2.5f)).getData());
+ assertEquals("a", tree.findNearest(new LocationTree.Item(1.5f, 1.5f)).getData());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/LogTest.java b/src/test/java/org/traccar/helper/LogTest.java
new file mode 100644
index 000000000..ef33c32ba
--- /dev/null
+++ b/src/test/java/org/traccar/helper/LogTest.java
@@ -0,0 +1,23 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class LogTest {
+
+ @Test
+ public void testExceptionStack() {
+ assertEquals(
+ "test - Exception (LogTest:11 < ...)",
+ Log.exceptionStack(new Exception("test")));
+ }
+
+ @Test
+ public void testExceptionStackRootCause() {
+ assertEquals(
+ "root - Exception (LogTest:18 < ...)",
+ Log.exceptionStack(new Exception("test", new Exception("root"))));
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/ObdDecoderTest.java b/src/test/java/org/traccar/helper/ObdDecoderTest.java
new file mode 100644
index 000000000..1ffe68c8b
--- /dev/null
+++ b/src/test/java/org/traccar/helper/ObdDecoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ObdDecoderTest {
+
+ @Test
+ public void testDecode() {
+
+ assertEquals(83, ObdDecoder.decode(0x01, "057b").getValue());
+ assertEquals(1225, ObdDecoder.decode(0x01, "0C1324").getValue());
+ assertEquals(20, ObdDecoder.decode(0x01, "0D14").getValue());
+ assertEquals(64050, ObdDecoder.decode(0x01, "31fa32").getValue());
+ assertEquals(25, ObdDecoder.decode(0x01, "2F41").getValue());
+
+ }
+
+ @Test
+ public void testDecodeCodes() throws Exception {
+ assertEquals("P0D14", ObdDecoder.decodeCodes("0D14").getValue());
+ assertEquals("dtcs", ObdDecoder.decodeCodes("0D14").getKey());
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/PatternBuilderTest.java b/src/test/java/org/traccar/helper/PatternBuilderTest.java
new file mode 100644
index 000000000..4c76bc463
--- /dev/null
+++ b/src/test/java/org/traccar/helper/PatternBuilderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.helper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class PatternBuilderTest {
+
+ @Test
+ public void testPatternBuilder() {
+ assertEquals("\\$GPRMC", new PatternBuilder().text("$GPRMC").toString());
+ assertEquals("(\\d{2}\\.[0-9a-fA-F]+)", new PatternBuilder().number("(dd.x+)").toString());
+ assertEquals("a(?:bc)?", new PatternBuilder().text("a").text("b").text("c").optional(2).toString());
+ assertEquals("a|b", new PatternBuilder().expression("a|b").toString());
+ assertEquals("ab\\|", new PatternBuilder().expression("ab|").toString());
+ assertEquals("|", new PatternBuilder().or().toString());
+ assertEquals("\\|\\d|\\d\\|", new PatternBuilder().number("|d|d|").toString());
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/PatternUtilTest.java b/src/test/java/org/traccar/helper/PatternUtilTest.java
new file mode 100644
index 000000000..77660078a
--- /dev/null
+++ b/src/test/java/org/traccar/helper/PatternUtilTest.java
@@ -0,0 +1,18 @@
+package org.traccar.helper;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class PatternUtilTest {
+
+ @Ignore
+ @Test
+ public void testCheckPattern() {
+
+ assertEquals("ab", PatternUtil.checkPattern("abc", "abd").getPatternMatch());
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/helper/ServletHelperTest.java b/src/test/java/org/traccar/helper/ServletHelperTest.java
new file mode 100644
index 000000000..e419b6491
--- /dev/null
+++ b/src/test/java/org/traccar/helper/ServletHelperTest.java
@@ -0,0 +1,65 @@
+package org.traccar.helper;
+
+import org.apache.struts.mock.MockHttpServletRequest;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class ServletHelperTest {
+
+ @Test
+ public void testRetrieveRemoteAddressProxyMultiple() {
+ MockRequest request = new MockRequest();
+ request.setRemoteAddress("147.120.1.5");
+ request.addHeader("X-FORWARDED-FOR", "231.23.45.65, 10.20.10.33, 10.20.20.34");
+
+ assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request));
+ }
+
+ @Test
+ public void testRetrieveRemoteAddressProxySingle() {
+ MockRequest request = new MockRequest();
+ request.setRemoteAddress("147.120.1.5");
+ request.addHeader("X-FORWARDED-FOR", "231.23.45.65");
+
+ assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request));
+ }
+
+ @Test
+ public void testRetrieveRemoteAddressNoProxy() {
+ MockRequest request = new MockRequest();
+ request.setRemoteAddress("231.23.45.65");
+
+ assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request));
+ }
+
+ private final static class MockRequest extends MockHttpServletRequest {
+
+ private String remoteAddress;
+
+ private Map<String, String> headers = new HashMap<>();
+
+ public void setRemoteAddress(String remoteAddress) {
+ this.remoteAddress = remoteAddress;
+ }
+
+ public void addHeader(String name, String value) {
+ headers.put(name, value);
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return headers.get(name);
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return remoteAddress;
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/model/MiscFormatterTest.java b/src/test/java/org/traccar/model/MiscFormatterTest.java
new file mode 100644
index 000000000..eb93d5b38
--- /dev/null
+++ b/src/test/java/org/traccar/model/MiscFormatterTest.java
@@ -0,0 +1,20 @@
+package org.traccar.model;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class MiscFormatterTest {
+
+ @Test
+ public void testToString() throws Exception {
+
+ Position position = new Position();
+ position.set("a", "1");
+ position.set("b", "2");
+ position.set("a", "3");
+
+ assertEquals("<info><a>3</a><b>2</b></info>", MiscFormatter.toXmlString(position.getAttributes()));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/notification/NotificiationMailTest.java b/src/test/java/org/traccar/notification/NotificiationMailTest.java
new file mode 100644
index 000000000..b82bec02e
--- /dev/null
+++ b/src/test/java/org/traccar/notification/NotificiationMailTest.java
@@ -0,0 +1,59 @@
+package org.traccar.notification;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.util.Properties;
+
+public class NotificiationMailTest {
+
+ private static final String FROM = "notification@traccar.org";
+ private static final String TO = "anton@traccar.org";
+
+ private static final String BODY = "Test email body.";
+ private static final String SUBJECT = "Test";
+
+ private static final String SMTP_USERNAME = "username";
+ private static final String SMTP_PASSWORD = "password";
+
+ private static final String HOST = "email-smtp.us-west-2.amazonaws.com";
+
+ private static final int PORT = 25;
+
+ @Ignore
+ @Test
+ public void test() throws Exception {
+
+ Properties props = System.getProperties();
+ props.put("mail.transport.protocol", "smtps");
+ props.put("mail.smtp.port", PORT);
+
+ props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.starttls.enable", "true");
+ props.put("mail.smtp.starttls.required", "true");
+
+ Session session = Session.getInstance(props);
+
+ MimeMessage msg = new MimeMessage(session);
+ msg.setFrom(new InternetAddress(FROM));
+ msg.setRecipient(Message.RecipientType.TO, new InternetAddress(TO));
+ msg.setSubject(SUBJECT);
+ msg.setContent(BODY, "text/plain");
+
+ Transport transport = session.getTransport();
+
+ try {
+ transport.connect(HOST, SMTP_USERNAME, SMTP_PASSWORD);
+ transport.sendMessage(msg, msg.getAllRecipients());
+ } finally {
+ transport.close();
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java
new file mode 100644
index 000000000..599dd6190
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java
@@ -0,0 +1,38 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AdmProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AdmProtocolDecoder decoder = new AdmProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "010042033836313331313030323639343838320501000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073"));
+
+ verifyPosition(decoder, binary(
+ "01002e40041c0744009dfe6742c6c860427402000000f4ff077752c8f55b000000000b4132010213430100041e"));
+
+ verifyPosition(decoder, binary(
+ "01002680336510002062A34C423DCF8E42A50B1700005801140767E30F568F2534107D220000"));
+
+ verifyPosition(decoder, binary(
+ "010022003300072020000000000000000044062A330000000000107F10565D4A8310"));
+
+ verifyPosition(decoder, binary(
+ "0100268033641080207AA34C424CCF8E4239030800005B01140755E30F560000F00F70220000"));
+
+ verifyPosition(decoder, binary(
+ "01002680336510002062A34C423DCF8E42A50B1700005801140767E30F568F2534107D220000"));
+
+ verifyPosition(decoder, binary(
+ "01002200333508202000000000000000007F0D9F030000000000E39A1056E24A8210"));
+
+ verifyNotNull(decoder, binary(
+ "01008449443d3120536f66743d30783531204750533d313036382054696d653d30383a35393a32302031302e30392e31372056616c3d30204c61743d36312e36373738204c6f6e3d35302e3832343520563d3020536174436e743d342b3720537461743d30783030313020496e5f616c61726d3d30783030000000000000000000000000"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
new file mode 100644
index 000000000..fd6214c57
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Anatoliy Golubev (darth.naihil@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class AdmProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ AdmProtocolEncoder encoder = new AdmProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_GET_DEVICE_STATUS);
+ assertEquals("STATUS\r\n", encoder.encodeCommand(command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "INPUT 0");
+ assertEquals("INPUT 0\r\n", encoder.encodeCommand(command));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java
new file mode 100644
index 000000000..9cb9f695a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AisProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AisProtocolDecoder decoder = new AisProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "!AIVDM,2,1,8,A,53UlSb01l>Ei=H4KF218PTpv222222222222221?8h=766gB0<Ck11DTp888,0*14s:MTb827ebc7686b,c:1481688227737*4d\\\r\n" +
+ "!AIVDM,2,2,8,A,88888888888,2*24\r\n" +
+ "!AIVDM,1,1,,A,13T=Qr0P001cmmLEf;A00?wN0PSU,0*29\r\n" +
+ "!AIVDM,1,1,,A,35Qf023Ohi1n5gdDRLW5FSQP00u@,0*18\r\n" +
+ "!AIVDM,1,1,,A,B3P@f>0000K6J;5KAIT03wpUkP06,0*5D\\s:MTb827ebc7686b,c:1481688230418*45\\\r\n" +
+ "!AIVDM,1,1,,B,B52Q8a@00Ul`9N5@ssbmCwr5oP06,0*36\r\n" +
+ "!AIVDM,1,1,,A,1815<S@01VQnKGlE0sk:WHcT0@O4,0*78\r\n" +
+ "!AIVDM,1,1,,A,35N7G;5OhQG?oJfE`G`cM9E`0001,0*6C\r\n" +
+ "!AIVDM,1,1,,B,13Ug;r0P011cqHJEevuEiOwf0L3h,0*6A\r\n" +
+ "!AIVDM,1,1,,A,13MKsr?0001dJC2Ee4W;jnal08Qj,0*00\r\n\r\n"));
+
+ verifyPositions(decoder, text(
+ "!AIVDM,1,1,,A,H3FUli4T000000000000001p0400,0*6E\\s:MTb827eba584a8,c:1481688176110*46\\\r\n" +
+ "!AIVDM,1,1,,B,13UhUh0P01QcoRTEdtB>4?v<2D=j,0*54\r\n\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java
new file mode 100644
index 000000000..47ea8137e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AlematicsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AlematicsProtocolDecoder decoder = new AlematicsProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$T,2,64,866050035975497,20180726103446,20180726103514,23.033305,72.558032,0,0,41,5.4,4,0,0,0.000,12.960,0,"));
+
+ verifyPosition(decoder, text(
+ "$T,2,65,866050035975497,20180726103646,20180726103736,23.033305,72.558032,0,0,41,5.4,4,0,0,0.000,12.976,0,0"));
+
+ verifyPosition(decoder, text(
+ "$T,2,552,868259020159698,20170515060949,20170515060949,25.035277,121.561986,0,202,78,1.0,8,1,0,0.000,12.768,1629,38,12770,4109,9"));
+
+ verifyPosition(decoder, text(
+ "$T,2,553,868259020159698,20170515061019,20170515061019,25.035295,121.561981,0,202,79,1.0,8,1,0,0.000,12.768,1629,38,12772,4109,8"));
+
+ verifyPosition(decoder, text(
+ "$T,4,4,868259020159698,20170515061033,20170515061033,25.035303,121.561975,0,202,81,1.7,6,1,0,0.000,12.770,1629,0,$S,A1,1,,12345.67,88.4,301.5,,2593.25,12.4,89.2,,5999.44,789.572,2345.67,,10763,1024,5,"));
+
+ verifyPosition(decoder, text(
+ "$T,2,554,868259020159698,20170515061049,20170515061049,25.035309,121.561976,0,202,82,1.1,7,1,0,0.000,12.768,1629,38,12770,4109,9"));
+
+ verifyPosition(decoder, text(
+ "$T,4,5,868259020159698,20170515061058,20170515061058,25.035308,121.561976,0,202,82,1.2,7,1,0,0.000,12.772,1629,0,$S,A1,1,,12345.67,88.4,301.5,,2593.25,12.4,89.2,,5999.44,789.572,2345.67,,10763,1024,5,"));
+
+ verifyPosition(decoder, text(
+ "$T,50,592,868259020159698,20170515062915,20170515062915,25.035005,121.561555,0,31,89,3.7,5,1,0,0.000,12.752,1629,38,12752,4203,6"));
+
+ verifyPosition(decoder, text(
+ "$T,50,594,868259020159698,20170515062928,20170515062928,25.035151,121.561671,0,31,93,1.8,5,0,0,0.000,12.752,1629,38,12756,4205,6"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java
new file mode 100644
index 000000000..e3aa0550b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AnytrekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AnytrekProtocolDecoder decoder = new AnytrekProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "78783500300086428703204121160085015111050C0A0D20C6FD24A102FF8EAC0C01001404000000FFFFFFFF131702210000000000000000000D0A"));
+
+ verifyPosition(decoder, binary(
+ "787835003000867279033457792c009801001209080a3408c81b2a7d0305b88b0c00001ccb0000000f00000002b90174f30b000000000000000d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java
new file mode 100644
index 000000000..c32abe6a8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ApelProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ApelProtocolDecoder decoder = new ApelProtocolDecoder(null);
+
+ /*byte[] buf1 = {0x40,0x4E,0x54,0x43,0x01,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x13,0x00,0x44,0x34,0x2A,0x3E,0x53,0x3A,0x38,0x36,0x31,0x37,0x38,0x35,0x30,0x30,0x35,0x32,0x30,0x35,0x30,0x37,0x39};
+ verifyNull(decoder, text( ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, buf1)));*/
+
+ //0c002900f12a00000f003235303032363533343135313036340f0033353638393530333632373938313101002000000000
+ //5c00380046e6a95136b693277f11b41a00172709f2ff03160002b9bc630007000000000000000000000000000000c31071090000880500000000000000000000
+ //5c00380072e7a95136b693277f11b41a00172709f2ff03160002b9bc630007000000000000000000000000000000c31071090000880500000000000000000000
+
+ //7900040069ea030000000000
+ //8300c20003006aea03005c003800223aab5107a393276617b41a0030d506e3000414010250bf630007000000000000000000000000000000c3107209000089050000000000006bea03005c003800403aab5107a393276617b41a0030d506e3000414010250bf630007000000000000000000000000000000c3107209000089050000000000006cea03005c0038005e3aab5107a393276617b41a0030d506e3000414010250bf630007000000000000000000000000000000c31072090000890500000000000000000000
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java
new file mode 100644
index 000000000..581f7696f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class AplicomFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AplicomFrameDecoder decoder = new AplicomFrameDecoder();
+
+ assertEquals(
+ binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"),
+ decoder.decode(null, null, binary("33353733303030373030393233333644C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C")));
+
+ assertEquals(
+ binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"),
+ decoder.decode(null, null, binary("44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java
new file mode 100644
index 000000000..0b4180bd0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java
@@ -0,0 +1,93 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AplicomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AplicomProtocolDecoder decoder = new AplicomProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "434946010A0100075253F85F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FEEA0000FEE90000F0030000F0040000FEF10000FEF20000FEF50000FEFC0000FEC10000FEE500"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667c0e462019800b05f7700005b7d3bdd00000000000000000000000000000000805e420fd60a0d57fffffb002141100000011001000c0932200000000000000a18136fcafefffffb002141100000011001000c0932200000000000000a1812e6dbfefffffb002141100000011001000c746578746167204e000008e3980bfefffffb002141100000011001000ce2009000170400890310f290e873fefffffb002141100000011001000ce2009000170400890290f28e409afefffffb002141100000011001000c0932200000000000000a1812e6dbfefffffb002141100000011001000ce2009000170400890280f28d4e2dfefffffb002141100000011001000c0001460000000000000021427a6cfefffffb002141100000011001000c0001460000000000000021606a6efefffffb002141100000011001000c746578746167204e000008badcc4fefffffb002141100000011001000c00014600000000000000218c0843fefffffb002141100000011001000ce2009000170400890270f6d45e09fefffffb002141100000011001000ce2801160600002053cd57a16549efe"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667ca8e6b003200b45f7700f05b6565e75b6565e6031f845100c6f0e70c00001483387c0fe60001000a263433233031303138373433303746230d0a"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667ca8e6b002d00305f7700d45b307a9a5b307a97031f840a00c6f05b0600000083000000263433233031303138373433303746230d0a"));
+
+ verifyAttributes(decoder, binary(
+ "44c30144f667c4316500e903ffdfbc00f059aebeb659aebeb302e3f5860065fe32120000ae0000000e47000000000000000000000000000127cd0000014c00000000000000ff010a002900000000000000014542016d0001010090070e140144f667c4316559ae620402e3f7f300660714c0010d15ff0f3332373937313100000000000000000000002a01010737341d331fffcf0103020b8601060c0001a5860001a58600000000010b1001ca01ca7d007d007c7cffffffffffff010a240000ffff0000000100010001ffff0000ffffffffffffffffffffffffffff00000002ffff010c06fec6febffeec"));
+
+ verifyAttributes(decoder, binary(
+ "45c20144f667c06ff9005d0161ef17000104596da2dc4b10c0c01d99020d6c04004cba7a010d44463030303235333731363238303030000000000000000000000000000000000000000000000000000001010d44463030303235333731363238303030000000000000031c"));
+
+ verifyAttributes(decoder, binary(
+ "45c20144f667c07287008c01ffff6d01000059368963d0340a0616207d7f4b10c0c019e6000039d7000039d71f40ffff5001574442393036363035533132333435363700014142432d33343520202020202000011231303331373139343039303030303031000000000000000000000000000000000000000000000000000001011231303331373139343039303030303031000000000000005a"));
+
+ verifyAttributes(decoder, binary(
+ "46c30144f667c1711f00340007ff750058b8f77701037c06b8000000330033000000000b760000425e0100640000b3a90185d5823155000131070204000219641004"));
+
+ verifyAttributes(decoder, binary(
+ "46c30144f667c1711f00340007ff75005891601401025707b50236003b003b003500000a9300006bd50100640000a5250167d2f9034c01010107020400021a901004"));
+
+ verifyAttributes(decoder, binary(
+ "48C1014143B4493145004900203F6D014B5557C20003000015060110FF00C800000000000000003D01141E283C500100260404010200000000000000000000000000C8000000000000010200110019001E0064019003E8"));
+
+ verifyAttributes(decoder, binary(
+ "48c10144b9de54e6b2008700205f710a57d23ec957d23b8d00000000300d0106ff00000000000000000000000000000000000000000000000000000000000000010a141e28323c46505a646e7801000f020104ff000000000000000000010102000f020104ff000000000000000000010103000f020104ff000000000000000000010105000f020104ff0000000000000000000101"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6dd1970230d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6dd9170250d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6de0970270d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "44c3014645e8ecff3c00ea03ffffbc00f457d68a6557d68a6303bb55fa018843da1100009881000000000000000000000000000000000000000000000000000000000000000000000000000000ff0056007600000000000000014542016d0001010095070e14014645e8ecff3c57d68a6403bb55fa018843dac0010d14ff050102030405060708090a0b0c0d0e0f10112a01010730343f3c1ff5cf01020700007d007d23010103022f2e01060c67452301efcdab8967452301010b10000000007d007d007d7dffffffffffff010a2400000000000000010000000000000000ffffffffffffffff00010001ffff00000000ffff010c02fec6"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66002300a21f0b01f056d3e62856d3e626031f845f00c6ee440800000000000000000017bd1cb30000"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66002300a21f0b00f056d3e64756d3e646031f845f00c6ee440800000000000000000017bd1cb30001"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66001f00221f0b01f456ba1e0d56ba1e0b031f842200c6ef550c000000000017bd1cb30004"));
+
+ verifyAttributes(decoder, binary(
+ "44c3014645e8e9bada003e03fff7070055a4f24200000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00000001000000000000000044c3014645e8e9bada003e03fff77bff55a4f24300000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00300002000000000000000044c3014645e8e9bada003e03fff7690655a4f24500000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff003000030000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e9d29a002d0022ff6d00f455893b4d55893b4c027a7e1500189d710800009e0000000000000000000000023300000000000000009d"));
+
+ verifyPosition(decoder, binary(
+ "44C20146B710C158DA002100B09F0700C054CA0EA254CA0E9C03BE0BF6015D7069070000142A600000000000000001"));
+
+ verifyPosition(decoder, binary(
+ "44C20143720729D6840043031fff7191C0450ef906450ef90603b20b8003b20b80066465b3870ce30f010ce30ce3003200001520000000030aa200003b13000000320300000bcb17acff0099000186a002"));
+
+ verifyPosition(decoder, binary(
+ "440129D684002b0700C0450ef906450ef90603b20b8003b20b80066465b3870ce30f010ce30ce300003b130300000bcb170a"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e9152e008900b09f7700f4558c07e8558c07e703be0bd8015d6faf0e0000003240000000000000000f4349460107010007558c07e70000000000000002d209df028f05fffe00000000000000002eff13fe11fe1a00011000000000000010ff11ff3cff11008c00080060f41b0043502015000000000000020d0000030d0000040c0000040d0000050c0000050d0000058c"));
+
+ verifyPosition(decoder, binary(
+ "44c20144563508385a009500b09f7700c0555ea99e555ea9b103bb569f01883ff50b00002a30f000000000000013074349460108010007555ea99e000000000000003f0000ae017605b3ff00000000010000006700d900d500000003000000000000006700d900d500000087002500c4ff0000435020150000000040512001000000000000020d0000030d0000040c0000040d0000050c0000050d0000058c0000060c"));
+
+ verifyPosition(decoder, binary(
+ "44C20146B710C158DA009500B09F7700C054CA0EA454CA0EA403BE0BF6015D706B070000142A600000000000000002434946010801000754CA0EA4000000000000008400000000000000000000000000000000300000FE00FE0000000000000000000000000000000000000000000000000000000000000000000040502035000000000000020D0000030D0000040C0000040D0000050C0000050D0000058C0000060C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java
new file mode 100644
index 000000000..ac0858e82
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AppelloProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AppelloProtocolDecoder decoder = new AppelloProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "FOLLOWIT,867273024233699,UTCTIME,0.000000,0.000000,0,0,0,0,L,262:3:c703:4bf8:64:255|262:3:c703:9a18:45|262:3:c703:e838:33|262:3:c703:7190:20|262:3:c704:d896:17|,02,44,,31,,4.20,0,0,86/44/24,,,,26,02264DFF6E16:69|4C09D408554E:79|4C09D408554F:79|E0885DE705E5:81|E2885DE705E7:81|246511122CCC:83|,34925"));
+
+ verifyAttributes(decoder, text(
+ "FOLLOWIT,867273024233699,UTCTIME,0.000000,0.000000,0,0,0,0,L,262:3:c703:4bf8:64:1|262:3:c703:9a18:44|262:3:c703:e838:33|262:3:c703:7190:21|262:3:c704:d896:18|262:3:c703:71a6:13|262:3:c703:253d:13|,02,44,,22,,4.20,0,0,86/44/24,,,,30,02264DFF6E16:67|E0885DE705E5:87|B4A5EF284B94:88|E2885DE705E7:85|4C09D408554E:78|3481C4D71B13:43|,24033"));
+
+ verifyAttributes(decoder, text(
+ "FOLLOWIT,860719028336968,UTCTIME,-12.112660,-77.045189,0,0,3,-0,L,716,10,049C,2A47,23,,4.22,,53,999/00/00,,,,,,59826,"));
+
+ verifyPosition(decoder, text(
+ "FOLLOWIT,860719028336968,160211221959,-12.112660,-77.045258,1,0,6,116,F,716,17,4E85,050C,29,,4.22,,39,999/00/00,,,,,,46206,"));
+
+ verifyPosition(decoder, text(
+ "FOLLOWIT,359586019278139,130809160321,22.340218,114.030737,60,120,05,152,F,460,01,2533,720B,31,out,3.90,1,192,20/00/00,12.5,100%,80,45,1CFA68BB754E:60|2CFA68BB754E:100|3CFA68BB754E:100|4CFA68BB754E:100|5CFA68BB754E:100|,46672"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java
new file mode 100644
index 000000000..ad13c60f6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AppletProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AppletProtocolDecoder decoder = new AppletProtocolDecoder(null);
+
+ DefaultHttpHeaders headers = new DefaultHttpHeaders();
+
+ headers.add("HOST", "192.168.0.1:8080");
+ headers.add("X-Admin-Protocol", "globalplatform-remote-admin/1.0");
+ headers.add("X-Admin-From", "8943170080001406197F");
+ headers.add("User-Agent", "oma-scws-admin-agent/1.1");
+ headers.add("From", "8943170080001406197F");
+
+ verifyNull(decoder, request(HttpMethod.POST, "/pli?=", headers));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java
new file mode 100644
index 000000000..489024ed5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AquilaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeA() throws Exception {
+
+ AquilaProtocolDecoder decoder = new AquilaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170215089,20,18.462809,73.824188,170613182744,A,01,123456,*37"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170215089,1,18.462809,73.824188,170613182744,A,19,0,0,256,4,4.860000,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,259,3731,*37"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170222318,101,22.846016,75.949104,170321103506,A,0,0,244991,0,10,0.860000,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,12483,294,*3D"));
+
+ verifyNull(decoder, text(
+ "$$CLIENT_1ZF,170222318,15,2_00AP,70.35.195.185,5089,internet,T1:10 S,T2:1 M,Ad1:9164061023,Ad2:9164061023,TOF:0 S,,OSC:75 KM,OST:0 S,GPS:YES,Ignition:ON,*75"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,170222318,1,22.836912,75.942215,170321110736,A,11,12,247196,103,10,0.810000,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,13325,306,*36"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,130329214,1,12.962985,77.576484,140127165433,A,22,0,0,140,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1253,420,1,0,1,0,0,1,P(01:410104000102|05:410521|0C:410C0000|0D:410D65|21:4121161C),D(P0121|B2105),-895,745,-145,300,*26"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1NS,101010119,1,22.845943,75.949059,170202184000,A,27,0,0,120,31141,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,399,3709,0,0,0,0,0,0,P(01:|04:|05:|0B:|0C:|0D:|10:|1C:|21:|23:|30:|31:|1F:|11:|00:|00:|),D(),-89,44,-1062,0,*49"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1DT,151028368,1,19.108438,72.925308,160628154920,A,22,0,0,131,3503,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,*1D"));
+
+ verifyNull(decoder, text(
+ "$$CLIENT_1DT,160319372,1,28.549541,77.249802,160628140743,A,23,0,-65025,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,*0D"));
+
+ verifyPosition(decoder, text(
+ "$$SRINI_1MS,141214807,1,12.963515,77.533844,150925161628,A,27,0,8,0,68,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,*43"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1ZF,130329214,1,12.962985,77.576484,140127165433,A,22,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,*26"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1WP,141216511,3,12.963123,77.534012,150908163534,A,31,0,0,0,7,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,*28"));
+
+ verifyPosition(decoder, text(
+ "$$CLIENT_1WP,141216511,3,12.963212,77.533989,150908164041,V,31,0,0,0,8,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,*2A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java
new file mode 100644
index 000000000..8498bb7f9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Ardi01ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Ardi01ProtocolDecoder decoder = new Ardi01ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "013227003054776,20141010052719,24.4736042,56.8445807,110,289,40,7,5,78,-1"),
+ position("2014-10-10 05:27:19.000", true, 56.84458, 24.47360));
+
+ verifyPosition(decoder, text(
+ "013227003054776,20141010052719,24.4736042,56.8445807,110,289,40,7,5,78,-1"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java
new file mode 100644
index 000000000..18901d9a6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArknavProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ArknavProtocolDecoder decoder = new ArknavProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "358266016278447,05*827,000,L001,V,4821.6584,N,01053.8650,E,000.0,000.0,00.0,08:46:04 17-03-16,9.5A,D7,0,79,0,,,,"),
+ position("2016-03-17 08:46:04.000", false, 48.36097, 10.89775));
+
+ verifyPosition(decoder, text(
+ "123456789012345,05*850,000,L001,A,2459.3640,N,12125.2958,E,000.0,224.8,00.8,07:47:26 09-09-05,9.00,D3,0,C4,1,,,,"));
+
+ verifyPosition(decoder, text(
+ "123456789012345,05*850,000,L001,A,2459.3640,N,12125.2958,E,000.0,224.8,00.8,07:47:26 09-09-05,9.00,D3,0,C4,1,,,00000000,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java
new file mode 100644
index 000000000..dfe1435d1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArknavX8ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ArknavX8ProtocolDecoder decoder = new ArknavX8ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "351856045213782,241111"));
+
+ verifyPosition(decoder, text(
+ "1G,181213092101,A,0347.0756N,09842.7435E,0.0,183,1.1,11008000"));
+
+ verifyAttributes(decoder, text(
+ "2G,181213092101,08,4084.0,00.04,04.01,000396255.0"));
+
+ verifyNull(decoder, text(
+ "2R,090214235955,00,,00.04,03.76,001892024.9"));
+
+ verifyNull(decoder, text(
+ "351856040005407,240101"));
+
+ verifyPosition(decoder, text(
+ "1R,110509053244,A,2457.9141N,12126.3321E,220.0,315,10.0,00000000"));
+
+ verifyNull(decoder, text(
+ "2R,110509053244,837493,,998372,,,"));
+
+ verifyPosition(decoder, text(
+ "1G,110509053245,A,2457.9141N,12126.3192E,3.1,35,2.0,00000001"));
+
+ verifyPosition(decoder, text(
+ "1G,110509053246,A,2457.9121N,12126.3415E,2.0,288,1.7,00000000"));
+
+ verifyPosition(decoder, text(
+ "1M,110509053247,A,2457.9118N,12126.3522E,1.0,55,2.2,00000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java
new file mode 100644
index 000000000..6b075facc
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArnaviProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ArnaviProtocolDecoder decoder = new ArnaviProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$AV,V4,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,80.0,56.1,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
+
+ verifyNull(decoder, text(
+ "$AV,V3DI,85164,20707,-1,19,0008C56A,000879AC,0C000002,863071013041618,89997077111301204297,*0B"));
+
+ verifyNull(decoder, text(
+ "$AV,V6SD,85164,20708,-1,3,6,37,33,*52"));
+
+ verifyAttributes(decoder, text(
+ "$AV,V4,85164,20709,1148,418,-1,0,1,192,0,0,0,0,0,0,,,000023,0000.0000N,00000.0000E,0.0,0.0,060180,0,0,32767,*4F"));
+
+ verifyNull(decoder, text(
+ "$AV,V3GSMINFO,85164,-1,20450,KMOBILE,1,2,1,23,0,40101,cc3,1,ce19,401,16,65304,5613,72,,SF*7F"));
+
+ verifyAttributes(decoder, text(
+ "$AV,V4,85164,20451,1146,418,-1,1,1,192,0,0,0,0,0,0,,,104340,0000.0000N,00000.0000E,0.0,0.0,060219,11,0,32767,,SF*47"));
+
+ verifyNull(decoder, text(
+ "$AV,V6SD,85164,20452,-1,3,3,5,6769,,SF*5D"));
+
+ verifyPosition(decoder, text(
+ "$AV,V2,32768,12487,2277,203,-1,0,0,193,0,0,1,13,200741,5950.6773N,03029.1043E,0.0,0.0,121012,*6E"));
+
+ verifyPosition(decoder, text(
+ "$AV,V3,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java
new file mode 100644
index 000000000..453ec9da5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AstraProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AstraProtocolDecoder decoder = new AstraProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "4b00700529c0c265976b8202cba9ff00676d864554a9c30000000020073401006436000300030008000000000000a0000100001920c43d00009600428302cba9ff00676d864554aa3e000000002007240100643b000300020008000000000000b0000100001920c43d00009600420f0e"));
+
+ verifyPositions(decoder, binary(
+ "4b00320524c1da58769e6d0322617effe874024453065600a800000100080000643e0000000000000000000000069500e7bb"));
+
+ verifyPositions(decoder, binary(
+ "4b013c02213aec35c501ad031368b8ffcd1ad043e5c4500c79000100003101005c490c001c0009020200020015069600ae03136801ffcd1af143e5c452125e000100003101005c491200090011010000020015068500af0313629effcd1f4b43e5c45d1e46000100003101005c491e00080409040500040015068700b0031359d5ffcd35ad43e5c47b2a3b000001003101005c492a1b1a0d0b0f0b00080015068700b103134984ffcd4b1e43e5c4913354000100003101005c49340b0103090606000f0015067700b203132e1affcd5a5a43e5c4af3348000001003101005c4935070a08000a070017001505f700b30313192cffcd7af143e5c4cd3733000001003101005c4937091206050a0800200015058600b403130debffcda88743e5c4eb2c3e000001003101005c493707030601080600290015058600b377"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java
new file mode 100644
index 000000000..c5829d588
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class At2000FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ At2000FrameDecoder decoder = new At2000FrameDecoder();
+
+ verifyFrame(
+ binary("01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8"),
+ decoder.decode(null, null, binary("01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8")));
+
+ verifyFrame(
+ binary("893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e"),
+ decoder.decode(null, null, binary("893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java
new file mode 100644
index 000000000..9e2f180d2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java
@@ -0,0 +1,59 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assume.assumeTrue;
+
+public class At2000ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ assumeTrue(Boolean.parseBoolean(System.getenv("CI")));
+
+ At2000ProtocolDecoder decoder;
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f00000000000000000000000000003335373435343037313632373539388b57ec3a6ec7e3310a1ceb0a70fd751b8f2e7be6df1d6dcd80129f66fff0ea1c"));
+
+ verifyNull(decoder, binary(
+ "89000000000000000000000000000000"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f0000000000000000000000000000333537343534303731363036313936ddf189075add9a32d97b54073025963e65849a3a59940d05fd8db655fc84bc6d"));
+
+ verifyPositions(decoder, binary(
+ "893b01000000000000000000000000003b40bcdab6387d829146701d8cb53daaa87b84d24b40cb24fd86da5d3f5f02b0f6f603c43c5a513418a0b2bdcaba603dbe737687cfe9082c57668eb6789d2b029a35aeac6a609558b96de5d7ad37917c902efc878ca9aff474f9d5d2417191285b8d5749bd3ffa86cc99096ce24c1f6ac350ae9adf3d5c788f80b4e3d3dc2dbb8abc1414ea1b52fdb55b2bb8af223ec528245f99d451b715e5774c5397db645d9ae441e645f8dae70230b728e81f51240868712d6f426fd694dbad8026fcf487c268939f04593ad86391cc829b1a1bdac8804ff7507544a69dc0b1b3927d7344e8a5b26fa56825283b3e476330b36d15011e1647ebd9f2ef71844ed32c0dc050457bfbd79160e6d1d8cda00a0927c8957631770e98eb20735aa46b0b18502baf4c45d2623ee51a4320cf3018010e7bbf8bc0dd79eb28e88b727ea67e980b8a91"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f000000000000000000000000000033353734353430373136303631393637f5441a9862260117858237fe3160388490f0df7d46c09112ee087235a92101"));
+
+ verifyPositions(decoder, binary(
+ "89043203000000000000000000000000d01ff1df1b56ba9185bb741ddc253f3cee093b1f8193100cd95777b5288a6f29d1b343a952f882ce8825679f7e27ad88ed7898bff92f716acadfc3c17fa8c1a6b9d0934e8f042433a183776c06c1acd73efb4b9f19030debb4dceb161fb3e6630757d25c3e995b7cf5f446318dcc1677eb215d1af49f11cd7300598bcdc40cc25466ed2391d836c782e44bc04a332e902b2b34f5597a542af4ca670cdfc18d87ce2a225c3e6f2f32359d4914c6df09aa5ee306c229260d4a56da53f93398bc8a6e77095305ee214cf605de20d3876a993fb810486f75bcd514c12442bf4dc3fbe7963b20d5100b5ecff1c1aef4c4b3736a04e245d50f538327db21d55270b279db5ac5a9658876bae3d9b5026b8975bb2bf4d100b8492760d66ae31f27bf9c525c2d794860eabca9c788b91152dbce79f336daaf6a7a9547bf1dd8e3334c891f4548fd6d112ebf45125c2a8abd3a786ebbcfdd03101b524bbf465f14a9a424305ce7de56ffca85b4657fc8c03e4349c0ca6be64d1cf595ee91f8173678ef2267dae54dd00028450c48d9b74c925af0f245d409d8773238dce5832747587f53a12155869c1d464eb0630f94cf8dceb76aa39995411d4ce7743b1501692425afec498535526067e79f568b7f71ee47d8b4929118d57b13d56cdbfd26582d579dee"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f0000000000000000000000000000333537343534303731363035353033dd529a1eb5df9f3b6d320b38250e03306692957e8c2127d8e381a717f639b4c9"));
+
+ verifyPositions(decoder, binary(
+ "89898701000000000000000000000000ae99e38f13d44f536769eb4930a6826dfebe5b98a6048941e89b17c9cdcb276be4af7c0d188d07c90d6e94aa9efcb465fe7aeaff4d85caf837483b4e9b32fbbacfbc4e175eebf57a27f552a64fc3419565d2dfbea668511a08d5a526342fad0e93b20c4449ad8defab4a9ac68cf7dad86971eb2cd96810d9d6a9c56e07fd90e4c28cfc53a069b63efe37a0523a69b607a2dc011ba17b177c5332c04be1faeeabed24539b3b790fa8a8610ab3633e0140ed79690fcae9dea43c7daad780d95a511d8f4875e621bcfe7516a03b80eb3c473ffd4bc1eda298dfa7d994a2cfeaa5d24c190d52d72fd90975a2e6f9ed3b95017133952262f91787c46839738a80c333dc53ee4d8afe75315d801efe17bc7309f30cfce64906bf70e6844c835781cbb64b49e9315ca3c2cd39d00a03cc7178a4ebc5df230dcdfd44ec588791d488f96bb6ff4007a753f552bda4d1766632aa3ec5eb38feb23ed6efb8f382a7f22b70adc9cb533c09bf749190c36d63b572c1acfc3a59138d51273835ab13c4689df01e3d2c2dd1829e00aac5c56b5d51e60d6731833f82c7464d88df663ca28a20eedeecb60f3704ae78281838caa116184e414db459768321bbfa1e83ad59fe168eb81f3b41cfe0e39c8aa78cbbe5825620bf053a1cb62e04d4cdf17ca2dc9305d47c"));
+
+ decoder = new At2000ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8"));
+
+ verifyPositions(decoder, binary(
+ "893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
new file mode 100644
index 000000000..3a26bb7a7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AtrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AtrackFrameDecoder decoder = new AtrackFrameDecoder();
+
+ verifyFrame(
+ binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a"),
+ decoder.decode(null, null, binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a")));
+
+ verifyFrame(
+ binary("40502c383732442c3731322c36353232312c3335373239383037303432363439382c313533343731353836322c313533343731353836322c313533343732363437342c2d38383531303939352c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353932312c313533343731353932322c313533343732363437342c2d38383531313032332c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32382c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353938322c313533343731353938322c313533343732363437342c2d38383531313034362c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c392c3331303236302c373838383637342c3134342c31360d0a313533343731363034312c313533343731363034322c313533343732363437342c2d38383531313031312c34343236303636332c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731363130322c313533343731363130322c313533343732363437342c2d38383531303938382c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134352c31360d0a"),
+ decoder.decode(null, null, binary("40502c383732442c3731322c36353232312c3335373239383037303432363439382c313533343731353836322c313533343731353836322c313533343732363437342c2d38383531303939352c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353932312c313533343731353932322c313533343732363437342c2d38383531313032332c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32382c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353938322c313533343731353938322c313533343732363437342c2d38383531313034362c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c392c3331303236302c373838383637342c3134342c31360d0a313533343731363034312c313533343731363034322c313533343732363437342c2d38383531313031312c34343236303636332c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731363130322c313533343731363130322c313533343732363437342c2d38383531303938382c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134352c31360d0a")));
+
+ verifyFrame(
+ binary("40502c373542332c3132302c37393737392c3335383930313034383039313535342c32303138303431323134323531342c32303138303431323134323531342c32303138303431333030303635352c31363432333338392c34383137383730302c3130382c322c362e352c392c302c302c302c302c302c323030302c323030302c1a0d0a"),
+ decoder.decode(null, null, binary("40502c373542332c3132302c37393737392c3335383930313034383039313535342c32303138303431323134323531342c32303138303431323134323531342c32303138303431333030303635352c31363432333338392c34383137383730302c3130382c322c362e352c392c302c302c302c302c302c323030302c323030302c1a0d0a")));
+
+ verifyFrame(
+ binary("244F4B0D0A"),
+ decoder.decode(null, null, binary("244F4B0D0A")));
+
+ verifyFrame(
+ binary("fe0200014104d8f196820001"),
+ decoder.decode(null, null, binary("fe0200014104d8f196820001")));
+
+ verifyFrame(
+ binary("40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000"),
+ decoder.decode(null, null, binary("40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
new file mode 100644
index 000000000..6b3dc3010
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
@@ -0,0 +1,121 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AtrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null);
+
+ verifyPositions(decoder, buffer(
+ "@P,93D1,419,0,357766091026083,1557178589,1557178590,1557178590,-121899637,37406241,338,230,2809,8,0,0,0,0,,2000,2000,\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,3A34,146,41431,353816057242284,20180622015809,20180622015809,20180622015809,9720689,4014230,61,2,0,20,1,0,0,0,0,2000,2000,12160,42,624,002,20009,20014,\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,1126,121,104547,358901048091554,20180412143513,20180412143514,20180413060000,16423389,48178700,108,2,6.5,9,0,0,0,0,0,2000,2000,\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,434E,124,104655,358901048091554,20180412143706,20180412143706,20180413060107,16423389,48178700,108,121,6.5,10,0,0,0,0,0,2000,2000,\r\n"));
+
+ verifyPositions(decoder, binary(
+ "4050b5ed004a2523000310c83713f8c05a88b43e5a88b43f5a88b43f021e0ad5fffdc0a800f3020003059100080000000000000007d007d046554c533a463d3230393120743d3137204e3d3039303100"));
+
+ verifyAttributes(decoder, buffer(
+ "$INFO=358683066267395,AX7,Rev.0.61 Build.1624,358683066267395,466924131626767,89886920041316267670,144,0,9,1,12,1,0\r\n"));
+
+ decoder.setLongDate(true);
+
+ verifyPositions(decoder, binary(
+ "0203b494003c00eb00014104d8dd3a3e07de011b0b1f0307de011b0b1f0307de011b0b1f0300307f28030574d30000020000000600160100020000000007d007d000"));
+
+ decoder.setLongDate(false);
+
+ decoder.setCustom(true);
+
+ verifyPositions(decoder, buffer(
+ "@P,9493,402,143,356961075931165,1546830150,1546830151,1546830151,-88429209,44271154,54,10,0,10,1,0,0,0,1858AE010000,2000,2000,\u001A,%CI%FL%ML%VN%PD%FC%EL%ET%AT%MF%MV%BV%DT%GN%GV%ME%RL%RP%SA%SM%TR%IA%MP,0,0,2T1KR32E28C706185,0,1,0,7,251,89,118,41,0,00A5001A040800A5001A040B00A5001A040C00A5001A040900A4001C040D00A50019040900A60019040900A4001B040B00A5001A040900A7001A040E\u001A,008CFE7C03C4\u001A,356961075931165,0,0,12,0,18,5,0\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,FD34,720,12256,357520076794151,1535445349,1535445354,1535500603,106784149,-6283086,105,2,138,0,3,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,0,0,0,0,0,0,0,1011677,0,138,0,0,0\r\n",
+ "1535445376,1535445374,1535500603,106783763,-6282981,105,101,138,6,2,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,141,41,60,12,0,0,7,1011677,0,138,0,0,0\r\n",
+ "1535445380,1535445378,1535500603,106783763,-6282981,105,103,138,6,2,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,135,41,61,12,0,0,9,1011677,0,138,0,0,0\r\n",
+ "1535445415,1535445415,1535500603,106783763,-6282981,105,2,138,7,2,0,0,0,,2000,2000,,%CI%TR%MV%BV%AT%SA%ET%GQ%GS%PC%RP%OD%AV1%XS%VS,0,135,41,61,12,0,21,10,1011677,0,138,0,0,0\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,0EBB,687,1917,357298070426498,1534718280,1534718279,1534739774,-88643875,44210148,270,2,57128,6,1,130,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,5,10,310260,7888593,137,16\r\n",
+ "1534718283,1534718283,1534739774,-88645243,44210145,269,2,57129,6,1,129,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,5,10,310260,7888593,137,16\r\n",
+ "1534718286,1534718285,1534739774,-88646581,44210136,269,2,57130,6,1,127,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,137,16\r\n",
+ "1534718289,1534718288,1534739774,-88647911,44210123,269,2,57131,6,1,127,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,137,16\r\n",
+ "1534718292,1534718291,1534739774,-88649229,44210111,269,2,57132,6,1,124,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,136,16\r\n"));
+
+ verifyPositions(decoder, binary(
+ "4050b63b02c401af000144a77a21281d5b79d8ef5b79d8ef5b79d8f0fab84831029f35580056020003144d00080100130000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41f0900003254314b5233324532384337303631383500000000000053005f3839303132363038383132313532343737353900000000fd078e0085002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34332e3938383331322c2d38382e35383631383920000013ff4e04190023ff13041100a3ffdc0402009affde03fc00a4ffe3040c0093ffab03ee004dffbb04130012ff7a04180010ff6e04100037ff4d0402ffd8000c04140000000144a77a21281d0009470c00131b2b005b79d8f05b79d8f05b79d8f0fab8488e029f356f0043020003144d00080100170000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41f0900003254314b5233324532384337303631383500000000000052005f3839303132363038383132313532343737353900000000fd09190085002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34332e3938383333352c2d38382e35383630393820000013ff4e04190023ff1304110017ff0c0424000cff30041a00a4ffe3040c0093ffab03ee004dffbb04130012ff7a04180010ff6e04100037ff4d0402ffd8000c04140000000144a77a21281d0009470c00171c2b00"));
+
+ verifyPositions(decoder, binary(
+ "405ad77c01670410000144a77a21281d5b74d2335b74d2335b74d233fabaf3bc02a38d3d010c0200030f8e000701001a0000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41c0900003254314b523332453238433730363138350000000000004800543839303132363038383132313532343737353900000000ec06a50089002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237323935372c2d38382e34313132303120000075ff4903fb006fff63040a004dff5d04080030ffa10407003b001304060026000503f9001e002504020078ff6204000073ff7d03f9007aff6903f3ffc0001804040000000144a77a21281d00073f0c001a182400"));
+
+ verifyPositions(decoder, buffer(
+ "@P,6254,235,989,356961075931165,1534381563,1534381564,1534381564,-88429188,44271225,70,2,200563,8,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,18,9,0,0,2T1KR32E28C706185,0,0,0,54,8901260881215247759,252,489,123"));
+
+ verifyPositions(decoder, buffer(
+ "@P,27A6,663,707,356961075931165,1534211298,1534211297,1534211437,-88429190,44271135,288,2,200235,8,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,9,0,0,2T1KR32E28C706185,0,0,0,80,8901260881215247759,251,59,124\r\n",
+ "1534211353,1534211357,1534211437,-88429190,44271135,288,2,200235,7,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,2,0,0,2T1KR32E28C706185,0,0,0,79,8901260881215247759,251,60,124\r\n",
+ "1534211417,1534211417,1534211437,-88429190,44271135,288,2,200235,7,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,2,0,0,2T1KR32E28C706185,0,0,0,78,8901260881215247759,251,56,124\r\n"));
+
+ verifyPositions(decoder, buffer(
+ "@P,CA4B,122,1,358683064932578,1533633976,1533633975,1533633975,121562641,25082649,72,0,1638,15,0,0,1,0,,2000,2000,,%CI%MV%BV,143,0\r\n"));
+
+ verifyPositions(decoder, binary(
+ "405025e30096eb730001452efaf6a7d6562fe4f8562fe4f7562fe52c02a006d902273f810064650000e0f5000a0100000000000007d007d000254349255341254d5625425625475125415400090083002a1a000001a8562fe4f8562fe4f7562fe52c02a006d902273f810064020000e0f5000a0100000000000007d007d000254349255341254d5625425625475125415400090083002a1a000001a8"));
+
+ decoder.setCustom(false);
+
+ verifyNull(decoder, binary(
+ "fe0200014104d8f196820001"));
+
+ verifyPositions(decoder, binary(
+ "40503835003300070001441c3d8ed1c400000000000000c9000000c900000000000000000000020000000003de0100000000000007d007d000"),
+ position("1970-01-01 00:00:00.000", true, 0.00000, 0.00000));
+
+ verifyPositions(decoder, binary(
+ "4050993f005c000200014104d8f19682525666c252568c3c52568c63ffc8338402698885000002000009cf03de0100000000000007d007d000525666c252568c5a52568c63ffc8338402698885000002000009cf03de0100000000000007d007d000"));
+
+ verifyPositions(decoder, binary(
+ "40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000"));
+
+ verifyPositions(decoder, binary(
+ "40501e58003301e000014104d8f19682525ecd5d525ee344525ee35effc88815026ab4d70000020000104403de01000b0000000007d007d000000000000000"));
+
+ verifyAttributes(decoder, buffer(
+ "$OK\r\n"));
+
+ verifyAttributes(decoder, buffer(
+ "$ERROR=101\r\n"));
+
+ }
+
+ @Test
+ public void testDecodeCustom() throws Exception {
+
+ AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null);
+
+ decoder.setCustom(true);
+
+ decoder.setForm("%AT%BV%MV%SA%VN%PD%IA%MP%EL%ET%FC%FL%RP%ML%MF%TR%EH%DL%EG%HA%HB%HC%IP%MT");
+
+ verifyPositions(decoder, buffer(
+ "@P,7E02,186,0,357766091026083,1558908265,1558908266,1558908266,-121900220,37407524,175,2,6,6,1,39,0,0,,2000,2000, ,3,40,142,12,JN8AZ1MU1BW066090,0,30,0,58,90,22,72,1187,0,1232,9,409,0,1,0,0,0,0,1\r\n"));
+
+ decoder.setForm("%AT%BV%CD%CE%CM%CN%DT%GN%GQ%GS%GV%LC%ME%MV%RL%SA%SM%CS%HT%VN%PD%IA%MP%EL%ET%FC%FL%RP%ML%MF%TR%EH%CR%DL%EG%HA%HB%HC%IP%MT%PF");
+
+ verifyPositions(decoder, buffer(
+ "@P,DCCE,422,5818,357766091026083,1557904779,1557904780,1557904780,-121899644,37406291,129,2,21,10,0,0,0,0,,2000,2000,,13,40,8942310017000752067,21096194,295050910083206,310260,0,FF00001F0393FF01001E0395FF01001E0394FF01001F0393FF02001D0393FF00001F0394FF0100200394FF01001F0393FF02001F0395FF0100200394,20,10,002C005C03B4,14953,357766091026083,125,38,11,0,1,Device:Fail,JN8AZ1MU1BW066090,0,0,0,0,0,99,0,0,0,0,0,264,5,0,0,0,0,0,0,0,0\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java
new file mode 100644
index 000000000..aeea48967
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AuroProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AuroProtocolDecoder decoder = new AuroProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "M0028T0000816398975I357325031465123E00001W*****110620150437000068DA#RD01DA240000000001+100408425+013756121100620152137231112240330004400"));
+
+ verifyPosition(decoder, text(
+ "M0029T0000816398975I357325031465123E00001W*****110620150439000068DA#RD01DA240000000001+100407886+013755936100620152138221952123100003400"));
+
+ verifyPosition(decoder, text(
+ "M0030T0000816398975I357325031465123E00001W*****110620150441000068DA#RD01DA240000000000+100408391+013756125100620152140102362238320034400"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java
new file mode 100644
index 000000000..fa8f3a071
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AustinNbProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AustinNbProtocolDecoder decoder = new AustinNbProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "48666666666;2017-01-01 16:31:01;52,1133308410645;21,1000003814697;310;120;2292;1;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48666666666;2017-01-01 16:55:00;52,1636123657227;21,0827789306641;0;90;4000;0;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48666666666;2017-01-01 17:32:01;52,1711120605469;21,0866680145264;70;90;1199;0;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48601601601;2017-01-01 16:31:01;52,1133308410645;21,1000003814697;310;360;2292;1;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48601601601;2017-01-01 16:55:00;52,1636123657227;21,0827789306641;0;360;4000;0;ORANGE"));
+
+ verifyPosition(decoder, text(
+ "48601601601;2017-01-01 17:32:01;52,1711120605469;21,0866680145264;70;360;1199;0;ORANGE"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java
new file mode 100644
index 000000000..9e17b437f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class AutoFonProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AutoFonProtocolDecoder decoder = new AutoFonProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "10556103592310314825728F"));
+
+ verifyPosition(decoder, binary(
+ "02080000251848470afa010262daa690013aa4046da83745f8812560df010001126a"));
+
+ verifyPosition(decoder, binary(
+ "111E00000000000000000100007101010B0C020302010B0C0005A053FFFFFFFF02010B0C00276047FFFFFFFF1F5600FA000176F218C7850C0B0B0C203A033DBD46035783EF009E00320014FFFF45"));
+
+ //verifyPosition(decoder, binary(
+ // "12060000007501010B0C00089CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF02007601010B0C00269CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF4A007601010B0C01089CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF04007501010B0C01269CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFF80007601010B0C02089CFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000003E7FFFFA6007501010B0C02231F5600FA000176F218C70000000000000000000000000000000000000003E7FFFF9629"));
+
+ verifyNull(decoder, binary(
+ "41035151305289931441139602662095148807"));
+
+ verifyNull(decoder, binary(
+ "41032125656985547543619173484002123481"));
+
+ verifyPosition(decoder, binary(
+ "023E00001E004D411EFA01772F185285009C48041F1E366C2961380F26B10B00911C"),
+ position("2010-01-27 04:00:08.000", true, 54.73838, 56.10343));
+
+ verifyPosition(decoder, binary(
+ "023E00001E004D411EFA01772F185285009C48041F1E366C2961380F26B10B00911C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java
new file mode 100644
index 000000000..f71fcd8eb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AutoGradeProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AutoGradeProtocolDecoder decoder = new AutoGradeProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(000000001637868324027912356171116A2250.7611N07556.9425E000.9024427197.36\u008eA0000B0000C0000D0000E0000K0000L0000M0000N0000O0000)"));
+
+ verifyPosition(decoder, text(
+ "(000000007322865733022629240170415A1001.1971N07618.1375E0.000145312128.59?A0024B0024C0000D0000E0000K0000L0000M0000N0000O0000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..5daafcc40
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AutoTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AutoTrackProtocolDecoder decoder = new AutoTrackProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "f1f1f1f1330c00201007090006de7200000000daa3"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java
new file mode 100644
index 000000000..71bc83b4f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class AvemaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AvemaProtocolDecoder decoder = new AvemaProtocolDecoder(null);
+
+ verifyAttribute(decoder, text(
+ "1000000000,20190527072358,121.646024,25.062135,0,0,0,0,10,0.0,1,0.02,12.32,0,0,15,2,466-5,10275,0,0.01,65EB812A000104E0,8000001234,NormanChang"),
+ Position.KEY_DRIVER_UNIQUE_ID, "65EB812A000104E0");
+
+ verifyNotNull(decoder, text(
+ "1000000000,20190522093835,121.645898,25.062268,0,0,0,0,3,0.0,1,0.02,11.48,0,0,19,4,466-5,65534,56589841,0.01"));
+
+ verifyNotNull(decoder, text(
+ "8,20180927150956,19.154864,49.124862,7,56,0,12,3,0.0,0,0.02,14.01,0,0,26,0,219-2,65534,10255884,0.01"));
+
+ verifyPosition(decoder, text(
+ "1130048939,20120224000129,121.447487,25.168025,0,0,0,0,3,0.0,1,0.02V,14.88V,0,1,24,4,46608,F8BC,F9AD,CID0000028"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java
new file mode 100644
index 000000000..0972e3fd4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class Avl301ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Avl301ProtocolDecoder decoder = new Avl301ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "244c0f086058500087335500010d0a"));
+
+ verifyNull(decoder, binary(
+ "24480d1001c3065c0d00010d0a"));
+
+ verifyPosition(decoder, binary(
+ "24242c0f041710001d0e060146944904ff4ac40000148f0651044b001a081001be06590daa00000108a30d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
new file mode 100644
index 000000000..9801b56cc
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BceProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BceProtocolDecoder decoder = new BceProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "3ab90b71bc1503000300c10bff11"));
+
+ verifyPositions(decoder, binary(
+ "cdc3440cf31403001902a58c0a06e0ceb0009f4e4452419417e0ceb08bc0ffcf428014463627b24018104b425b1c508b00d16a9743d188da6e0110ce001455069262002e4c5adabb810200418728157501004229460377000bb4d04b10c000ffff335aa800000000000000000000000000a912963d0042313130303030313236313432303031202020202020202020202020202020203f2946030301f70100007b0400009f130000762700000a26e0ceb06f074e4452419427e0ceb08bc0ffcf42801446ee28b240a40f4b425c1c518a00df414c42d188936eff0fce001455069262002e4c5adabb810200417b28157501004c294603770008b4d04b10c000ffff4c5a89000000000000000000000000009a2c8c3b004231313030303031323631343230303120202020202020202020202020202020492946030301f80100007c040000a9130000802700009477e0ceb08bc0ffcf42801446eb2fb2405c0d4b425d1c53830089e07e43d188a56eff0fce001455069262002eb35700bb810200415227167501007e294603780000b4d04b10c000ffff995700000000000000000000000000008bd9f43b004231313030303031323631343230303120202020202020202020202020202020802946030301fd01000081040000e0130000b72700000a86e0ceb064464e4452410a96e0ceb000a54e4452410aa6e0ceb000914e4452410ab6e0ceb068334e4452410ac6e0ceb0009f4e4452410ac6e0ceb06f074e445241f4"));
+
+ verifyPositions(decoder, binary(
+ "cc2c5792c6160300b000a5520aa6c813ae64465343513840a7c813ae0bc0fd800080040036093f427884ea41001c900e00000000009088c562a301024156d12a004c00006df80c0000000086fb0200562a08005a000000000ac6c813ae0091534351380af6c813ae009f534351380af6c813ae6f075343513840f7c813ae0bc0fd800080040036093f427884ea41001c900e00000000009088f162a301024156d12a004c00006df80c0000000086fb0200562a08005a000000003f"));
+
+ verifyPositions(decoder, false, binary(
+ "76145792c61603003402a59b59a7f722aa8ac00080c086000121800000280f9401056804d181006222ea4201000000000000008081008081008081008081000022ea4201000000000000ffffffffffff00000000ffffffffffff00000000ffffffffffff000059f7f722aa8ac00080c086000121800000260f9401056804d181006222ea4201000000000000008081008081008081008081000022ea4201000000000000ffffffffffff00000000ffffffffffff00000000ffffffffffff00000a16f822aa6f07534352325917f822aa8ac00080c086000120800000190f9401056804d181006222ea4201000000000000008081008081008081008081000022ea4201000000000000ffffffffffff00000000ffffffffffff00000000ffffffffffff00005957f822aa8ac00080c086000121800000240f9401056804d181006222ea4201000000000000008081008081008081008081000022ea4201000000000000ffffffffffff00000000ffffffffffff00000000ffffffffffff00000a66f822aa6f07534352325967f822aa8ac00080c086000121a00000160f9401056804d181006222ea4201000000000000008081008081008081008081000022ea4201000000000000ffffffffffff00000000ffffffffffff00000000ffffffffffff000059b7f822aa8ac00080c086000121800000170f9401056804d181006222ea4201000000000000008081008081008081008081000022ea4201000000000000ffffffffffff00000000ffffffffffff00000000ffffffffffff0000ef"));
+
+ verifyPositions(decoder, binary(
+ "789622d1cb1303003401a53365b70f4a9babc0ffd700c04400f0b6c741e63933428f1c431c015468de43f18221341b007e0ae20001430a698f003f008d000000000031f85900000000f0831c018400000000000000000000000000000000000209000000000000000000000000000000030065f70f4a9babc0ffd700c0440069bcc741e73733427f1c431a01a9378343f1829c391b00a80be2000170056da7003e007c000000000031c04e00000000f0831c01810000000000000000000000000000000000060100000000000000000000000000000003006537104a9babc0ffd700c0440051c1c74129363342721c421801e4809543f18210341b00710ae2000170056da7003e0072000000000031c04800000000b8841c017e00000000000000000000000000000000000306000000000000000000000000000000030069"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601004200a0003fd769c568ffc3db0079161d420683a9414918b1150000000000d102660167040000000000009f06357f0000a401042ea415e10232000000000000000000000051"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601004200a0003ff76cc568ffc3db00bd151d423c8ca9410a18af150000000000d1023a0160040000000000009f06427f0000a401042ea416e1003e00000000000000000000009a"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601004202a5863f57f8b868ffc3db0001712642b70b9d41221946200246d23342d1023e016404000000000000a0065a7f0000a4010496f277e3064300000000000000000000003f97f8b868ffc3db0074712642ae0a9d412919452102fff19042d102a4026304000000000000a006487f0000a4010496f277e3064300000000000000000000003fb7f8b868ffc3db00c6712642000a9d413019442002a6074542d102300165040000000000009f064f7f0000a4010496f277e3064300000000000000000000003fd7f8b868ffc3db002872264245099d413518421f02bea35e42d1021e0164040000000000009f06377f0000a4010496f277e3064300000000000000000000003fe7f8b868ffc3db0061722642e3089d413a28421f02a05ff641d102580163040000000000009f06577f0000a4010496f277e3064300000000000000000000003f17f9b868ffc3db0021732642a3079d414119411d02d69fcc42d102440165040000000000009f06437f0000a4010496f277e3064300000000000000000000003f37f9b868ffc3db00ae732642b4069d414628421b02e0629742d1024c0167040000000000009f06557f0000a4010496f277e3064300000000000000000000003f57f9b868ffc3db0044742642ae059d414c28421a027540a342d102860163040000000000009f065b7f0000a4010496f277e3064300000000000000000000003f97f9b868ffc3db007275264256039d4153284417029e1f2f43d1024a016704000000000000a0064e7f0000a4010496f277e306430000000000000000000000db"));
+
+ verifyPositions(decoder, binary(
+ "2d41abfa2e4501004e02a5a0068609f96a009106260af96a00a006260af96a009106960af96a00a306a60af96a008f06b60af96a009106960cf96a00a03e0715f96affc300804000e6a23a4230ccc441001f47850200000000a0000000bd6542651a110d004b1000000000a401045a56bf4d02480000000000000000061623f96a00a0062623f96a00913ea728f96affc300804000e6a23a4230ccc441001f7f850200000000a0000000bd6542651a110d004a1000000000a401045a56bf4d02480000000000000000069639f96a00a006a639f96a00913e373cf96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a110d004a1000000000a401045a56bf4d024800000000000000003ed74ff96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a111b004a1000000000a401045a56bf4d01480000000000000000061650f96a00a0062650f96a00913e6763f96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a110d004a1000000000a401045a56bf4d01480000000000000000069666f96a00a006a666f96a00913e0777f96affc300804000e6a23a4230ccc441001f7f850200000000a0000000ad6534651a110d004a1000000000a401045a56bf4d0148000000000000000006067df96a00a006167df96a0091063687f96a00a3064687f96a008f065687f96a0091063689f96a00a03e978af96affc300804000e6a23a4230ccc441001f87850200000000a0000000ad6527651a110d004a1000000000a401045a56bf4d024800000000000000000e"));
+
+ verifyPositions(decoder, binary(
+ "be76619c834601003302a5e8327764726bff432fc52a420e2c93410028afd2070000000080024a0005040000000000008e06547f0000a401043cf21f390e54328764726bff432fc52a420e2c93410028afd2070000000080024c0005040000000000008e064f7f0000a401043cf21f390e54329764726bff432fc52a420e2c93410028afd2070000000080024e0002040000000000008d064f7f0000a401043cf21f390e5432a764726bff432fc52a420e2c93410028afd2070000000080024e0004040000000000008e06587f0000a401043cf21f390e5432b764726bff432fc52a420e2c93410028afd207000000008002460005040000000000008e06557f0000a401043cf21f390e5432c764726bff432fc52a420e2c93410028afd2070000000080024e0004040000000000008e06347f0000a401043cf21f390e5432d764726bff432fc52a420e2c93410028afd2070000000080024e0002040000000000008e06547f0000a401043cf21f390e5432e764726bff432fc52a420e2c93410028afd207000000008002540002040000000000008e06477f0000a401043cf21f390e5432f764726bff432fc52a420e2c93410028afd207000000008002540004040000000000008d064f7f0000a401043cf21f390e54320765726bff432fc52a420e2c93410028afd207000000008002540004040000000000008e064d7f0000a401043cf21f390e54321765726bff432fc52a420e2c93410028afd207000000008002540004040000000000008e06467f0000a401043cf21f390e544200a0003f3743c96bffc3db0060c81c42d885ab41002aaf060000000000d102380167040000000000008a064f7f0000a4010412a46b330033000000000000000000000025"));
+
+ verifyPositions(decoder, binary(
+ "ca07629c834601002702a58f3c278ff96a0bc000a0c00140bc3a42508bc541002a70a905000000009000c101a40103d904440e003000000000000000000000000000000000000001013c878ff96a0bc000a0c00140bc3a42508bc541002970a905000000009000c301a40103d904440e003000000000000000000000000000000000000001013cb7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c401a40103d904440e003500000000000000000000000000000000000001013cc7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c301a40103d904440e003500000000000000000000000000000000000001013cd7f2f96a0bc000a0c00114bc3a42a48fc5410029027e03000000009000c301a40103d904440e003000000000000000000000000000000000000001013c670dfa6a0bc000a0c001f1bb3a42418dc541002a484904000000009000c001a40103d904440e003a00000000000000000000000000000000000001013c770dfa6a0bc000a0c001f1bb3a42418dc5410028484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c470efa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c5711fa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000c101a40103d904440e003000000000000000000000000000000000000001013f00a0003cc795866b0bc000a0c00144bc3a423a90c541003697cb03000000008000cf01a40103d9040d0f0030000000000000000000000000000000000000010100"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java
new file mode 100644
index 000000000..be5877193
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class BceProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ BceProtocolEncoder encoder = new BceProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 1);
+ command.set(Command.KEY_DATA, "1");
+
+ verifyCommand(encoder, command, binary("79df0d86487000000600410aff00550048"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java
new file mode 100644
index 000000000..9a24ece1f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BlackKiteProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BlackKiteProtocolDecoder decoder = new BlackKiteProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01150003313131313131313131313131313131209836055605BA"));
+
+ verifyPositions(decoder, binary(
+ "0136000331313131313131313131313131313120523905563000010000000100000033000000003400004000004500004600005000005100009F76"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
new file mode 100644
index 000000000..9f3254824
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BlueProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BlueProtocolDecoder decoder = new BlueProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "aa00550000813f6f840b840380001032000000002001030040008005ee1938113b26f300000000000000140114082833044d27602112030002000000b70000020000000000000000650000001601f4000000000000e4"));
+
+ verifyPosition(decoder, binary(
+ "aa0055860080e3e79e0b840f800010320000000020010f0040008005ee197f113b26e800000000000000130c11091a2b005ac7a621120f0002000000b7000002000000000000001a3a0000000001f40000000000003f"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
new file mode 100644
index 000000000..f7197a05d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java
@@ -0,0 +1,73 @@
+package org.traccar.protocol;
+
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class BoxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BoxProtocolDecoder decoder = new BoxProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "H,BT,358281002435893,081028142432,F5813D19,6D6E6DC2"));
+
+ verifyNull(decoder, text(
+ "H,BT,N878123,080415081234,D63E6DD9,6D6E6DC2,8944100300825505377"));
+
+ verifyPosition(decoder, text(
+ "L,190416090826,G,21.46701,39.18655,0,280,86.62,53,21;A,0;D,0;T0,34.2;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,190416090926,G,21.46701,39.18655,0,280,86.62,7,20;A,0;D,0;T0,34.2;I,0;END,106,222,190416080509"));
+
+ verifyPosition(decoder, text(
+ "L,190227043304,G,25.68773,48.59788,71,53,261.42,1,23;A,0.03;D,0.06;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,081028142429,G,52.51084,-1.70849,0,170,0,1,0"));
+
+ verifyPosition(decoder, text(
+ "L,081028142432,G,52.51081,-1.70849,0,203,0,16,0"));
+
+ verifyNull(decoder, text(
+ "L,080528112501,AI1,145.56"));
+
+ verifyNull(decoder, text(
+ "E,1"));
+
+ verifyPosition(decoder, text(
+ "L,150728150130,G,24.68312,46.67526,0,140,0,3,20;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155815,G,24.68311,46.67528,0,140,0,6,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155833,G,24.68311,46.67528,11,140,0,52,23;A,0.79;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155934,G,24.68396,46.67489,0,282,0.12,1,21;A,1.27;D,1.23;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160033,G,24.68414,46.67485,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160133,G,24.68388,46.675,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160233,G,24.68377,46.67501,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160333,G,24.684,46.67488,0,282,0.12,1,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728155855,G,24.68413,46.67482,0,282,0.14,53,21;A,0;D,0;I,0"));
+
+ verifyPosition(decoder, text(
+ "L,150728160400,G,24.68413,46.67482,0,282,0.14,7,20;A,0;D,0;I,0;END,25,326,150728155814"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java
new file mode 100644
index 000000000..9f841fb8c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class C2stekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ C2stekProtocolDecoder decoder = new C2stekProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "PA$863083038046613$D#181123#162850#1#+37.92684#+23.75933#0.62#200.1#0.0#3768#000#9#00#sz-w1001#B0907839$AP"));
+
+ verifyPosition(decoder, text(
+ "PA$353990030327618$D#091117#020928#1#22.537222#114.020948#0.00#0.0#42.1#4183#011#1#101#Wsz-wl001#B0101940#C+3.0,-5.0,+2.0$AP"));
+
+ verifyNull(decoder, text(
+ "PA$863083030602124$20$AP"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java
new file mode 100644
index 000000000..7640484d4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java
@@ -0,0 +1,57 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CalAmpProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CalAmpProtocolDecoder decoder = new CalAmpProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "8308352648068863398f01070102039c5cfc4dcd5cfc4dcd19913f5dcce1291e000033fa0000005801110800019aff9d6f0e13003e0b02000000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "83051633033459010101028afd59ae7c1459ae7c140b06bbce2c01520e0000d916000001b900450900005affa50f091f00260d040000000f24000001b90000000000003714"));
+
+ verifyPosition(decoder, binary(
+ "83092701131797081078220107010200dc583d4d3f583d4d3f19c70502cd1d512d00005f180000008500ec0800101eff980f090100313102000000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "8305133303910501010102004557E5AB2457E3B3E01FD828DBFE9E3465000028C90000004201310704001EFFA12F0B22081BCA05000000000000000F87000E8E2F00EA029E0000082D"));
+
+ verifyPosition(decoder, binary(
+ "8305313301481601010102045557ea2eba57ea2eba1ebf06db005f2e5e0000220c00000000000006200000ff8f000a00000bca06000000000000000f1b000f35ef00ea02900000087000000060"));
+
+ verifyPosition(decoder, binary(
+ "8305454205067001010102008157a9bddc57a96aaa17cdb98fccc1a457000056ac00000000000007250000ff8f000e00082711570000000000ffff101b00003148000010680000000000000000000000050000000000000000000005b0000000000000289600000000000000000000069b00000000000008e400000000000000000000000000000003000000010000070f"));
+
+ verifyPosition(decoder, binary(
+ "83052132052924010101020001575c590300000000000000000000000000000000000000000000002c0000ff8f0000030801010000"));
+
+ verifyPosition(decoder, binary(
+ "830543321494860101010a0080560b5a5e0eadd0291becf3c500f005090f1f3305000003010040c0a600000000000000008b12a102"));
+
+ verifyPosition(decoder, binary(
+ "830543321494860101010a0c215608b6680ead5ada1bed88d300000049801f000500000300003cf33200000000000000008b0ce101"));
+
+ verifyNull(decoder, binary(
+ "830545321041830101010300010000333862000023c301000000004532104183ffffff353816051610691f420040163953294fffffffffffffffff8996604211639032949f4f54413a317c303b302c317c343b302c34004f5441535441543a302c302c302c302c302c222200564255533a342c322e302e302c343533323130343138332c5630312e30332e30312e34302c5630312e30332e30312e33312c2c0056494e2d494e464f3a56494e3d31464d5a5537324539355a4137303032362c4445562d5245474e3d55532c535256522d5245474e3d555300"));
+
+ verifyPosition(decoder, binary(
+ "8308353301059723580f01020102088952d994c352d994c4134fa767c4c482e20000c12700000d29006e1002019affc90f061d00060c0000"));
+
+ verifyPosition(decoder, binary(
+ "8308355233050116134f01020102445652d993e152d993e1124c728cc68f0647000023c00000000000000e02019affc90f071c0015020000"));
+
+ verifyPosition(decoder, binary(
+ "830545420185450101010200075517fb335516c5c40fb1aea4cf4cbf250000000000000000008900260015ffb10f001108110a0000"));
+
+ verifyPosition(decoder, binary(
+ "830543321494750101010A00085492798A0EC4F9E71BDA3B81005600040F1F33050000030000000076000000000000000000000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..c8db07ee3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CarTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CarTrackProtocolDecoder decoder = new CarTrackProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$$020040????????&A0000"));
+
+ verifyPosition(decoder, text(
+ "$$020040????????&A9955&B011939.000,A,4436.3804,N,02606.9434,E,0.00,0.00,190317,,,A*64|0.9|&C0100000000&D01830=?6&E00000001&Y00000000"));
+
+ verifyPosition(decoder, text(
+ "$$2222234???????&A9955&B102904.000,A,2233.0655,N,11404.9440,E,0.00,,030109,,*17|6.3|&C0100000100&D000024?>&E10000000"),
+ position("2009-01-03 10:29:04.000", true, 22.55109, 114.08240));
+
+ verifyPosition(decoder, text(
+ "$$2222234???????&A9955&B102904.000,A,2233.0655,N,11404.9440,E,0.00,,030109,,*17|6.3|&C0100000100&D000024?>&E10000000&Y00100020"));
+
+ verifyPosition(decoder, text(
+ "$$2222234???????&A9955&B102904.000,A,2233.0655,N,11404.9440,E,0.00,,030109,,*17|6.3|&C0100000100&D000024?>&E10000000"));
+
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java
new file mode 100644
index 000000000..412901d90
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java
@@ -0,0 +1,46 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CarscopProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CarscopProtocolDecoder decoder = new CarscopProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "*170821223045UB00HSO"));
+
+ verifyPosition(decoder, text(
+ "*170821223121UB05ORANGE000731512061825V0000.0000N00000.0000E000.0040331309.62"));
+
+ verifyPosition(decoder, text(
+ "*170724163029UB05ORANGE000000010061825V0000.0000N00000.0000E000.0040331309.62"));
+
+ verifyNull(decoder, text(
+ "*160618233129UB00HSO"));
+
+ verifyNull(decoder, text(
+ "*160618232614UD00232614A5009.1747N01910.3829E0.000160618298.2811000000L000000"));
+
+ verifyNull(decoder, text(
+ "*160618232529UB05CW9999C00000538232529A5009.1747N01910.3829E0.000160618298.2811000000L000000"));
+
+ verifyPosition(decoder, text(
+ "*040331141830UB05123456789012345061825A2934.0133N10627.2544E000.0040331309.6200000000L000000"),
+ position("2004-03-31 06:18:25.000", true, 29.56689, 106.45424));
+
+ verifyPosition(decoder, text(
+ "*040331141830UB04999999984061825A2934.0133N10627.2544E000.0040331309.6200000000L000000"));
+
+ verifyPosition(decoder, text(
+ "*040331141830UA012Hi-jack061825A2934.0133N10627.2544E000.0040331309.6200000000L000000"));
+
+ verifyPosition(decoder, text(
+ "*150817160254UB05CC8011400042499160254A2106.8799S14910.2583E000.0150817158.3511111111L000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java
new file mode 100644
index 000000000..3c35398a6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java
@@ -0,0 +1,152 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class CastelProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CastelProtocolDecoder decoder = new CastelProtocolDecoder(null);
+
+ verifyAttribute(decoder, binary(
+ "40403a00043231334744503230313830323133343300000000a002000001000001012011004d414c43333831434d4b4d353637313438c8fc0d0a"),
+ Position.KEY_RESULT, "MALC381CMKM567148");
+
+ verifyAttributes(decoder, binary(
+ "404043000432313357503230313830303138323400000000004005f064d95c8365d95c9f2f0100c50200004006000000000000040003440068000000000100f3660d0a"));
+
+ verifyAttributes(decoder, binary(
+ "40403a00043231335750323031373030363135360000000000a00200000100000101201100344a474446364545374a4230373632363056ff0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040560004323133474c3230313630303033363400000000004002a122a05a5423a05abe0f2a000000000007f1f90014000000040001640011170003001e000505210b210c210d210f2101062b58ef02001a25950d0a"));
+
+ verifyAttributes(decoder, binary(
+ "404057000431303031313132353239393837000000000000004002C1F06952F0F169529C9111000000000069830000470000000400036401014C01030078000505210C210D210F21102101073BE8030064280AEB930D0A"));
+
+ verifyPosition(decoder, binary(
+ "40405f000536303331353030303335313200000000000000004001040212102a2f72b29302a0af8512b40787018e000000000043e4ae000000007ca0f7224d5049503632305f56312e312e30004d5049502d3632302056322e300072140d0a"));
+
+ verifyPosition(decoder, binary(
+ "24245000363137313135313243333133360000000000000040011b011207133ac49a390464514a15000000008e480c00000917000000000000ffafffaf00010000ffff7800ffffffffffffff003c0d0a"));
+
+ verifyPositions(decoder, binary(
+ "40408200033231334c32303137303031313039000000000000100136477b5964477b590400000000000000dc410f000000000204000709207910008304011c07110e110dd41a160714a95a0f00001e058c4944442d3231334c2056312e312e3120323031372d30352d3038004944442d3231334c2056312e312e300000006da10d0a"));
+
+ verifyPositions(decoder, binary(
+ "40408200033231334c323031373030303131370000000000001001000c6759a10d67590a9e1200000000000e3e0000000000020000000e4e791c000004010d0711060515083017086cd1181f000040067d4944442d3231334c2056312e312e3120323031372d30352d3038004944442d3231334c2056312e312e3000000066e30d0a"));
+
+ verifyAttributes(decoder, binary(
+ "404043000432313345503230313630303035383500000000004006a2021d5810031d58ae940400da050000f6040000070000000400076401680000000001001bd20d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d0000432313345503230313630303035383500000000001001831c1c58b1fc1c58ae94040012220000f60400005800000000000763016800008484004944445f3231335730315f532056312e302e37004944445f3231335730315f482056312e302e370032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f0121022101260127012861780d0a"));
+
+ verifyNull(decoder, binary(
+ "404029000432313345503230313630303035383500000000009001ffffffff0000b4fc1c582b6e0d0a"));
+
+ verifyPositions(decoder, binary(
+ "40406000043231334550323031363030303538350000000000400708000000831c1c58f4fb1c58ae94040012220000f604000058000000200007630168000084c401040b10090c3532db3f07f07f7520090100000101010e00000000c7920d0a"));
+
+ verifyNull(decoder, binary(
+ "404042000432313345503230313630303035383500000000001002831c1c58b7fc1c58ae94040012220000f604000058000000000007630168000084840072a20d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d0000432313345503230313630303035383500000000001001831c1c5805fe1c58ae94040012220000f60400005800000000000763016800008484004944445f3231335730315f532056312e302e37004944445f3231335730315f482056312e302e370032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f012102210126012701284eb10d0a"));
+
+ verifyAttributes(decoder, binary(
+ "40405700043231334e583230313630303131373700000000004002c458ce572159ce57a9e2020082030000500c00000f0000000400036401240e0403023c000505210c210d210f21102101075b14030121330269430d0a"));
+
+ verifyNull(decoder, binary(
+ "40407800043231334e583230313630303131373700000000004004fa52ce574b53ce57cad1020041020000050c00000d0000000400036401240b0503001b042105210c210d210f211021112113211c211f212121232124212c212d213021312133213e2141214221452149214a214c214f215021384e0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040a600043231334e583230313630303131373700000000004005fa52ce575053ce57cad102006b020000050c00000d0000000400036401240b050300001b042105210c210d210f211021112113211c211f212121232124212c212d213021312133213e2141214221452149214a214c214f215021015bd604301f500600000653000000bc0bffff78250000ff2d98642401000f8080e038000f0f0000000000000077b10d0a"));
+
+ verifyAttributes(decoder, binary(
+ "40404300043231334e583230313630303131373700000000004006fa52ce574e53ce57cad1020053020000050c00000d0000000400036401240b0503000000feec0d0a"));
+
+ verifyPosition(decoder, binary(
+ "40403600043231334e583230313630303033343600000000004009ad31a457050810061a35b29bf80ae6da83180300320bbe32580d0a40403600043231334e583230313630303033343600000000004009ad31a457050810061a35b29bf80ae6da83180300320bbe32580d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d400043535333133350000000000000000000000000000100196d499574bd899570000000000000000010000000000000000000000002410000000004944445f3231334730325f532056322e332e345f4e004944445f3231334730325f482056322e332e345f4e0032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f012102210126012701285b410d0a"));
+
+ verifyPosition(decoder, binary(
+ "24243f00676e6768656636313031313132393030313734002001840d0000d2deb556020602100b35360456cf09e6ebac0200000000030000000001abc10d0a"));
+
+ verifyPosition(decoder, binary(
+ "24243f00676e6768656636313031313132393030313734002001840d000000dfb556020602100b36298256cf0956ebac020000990c7f0000000001b4830d0a"));
+
+ verifyPositions(decoder, binary(
+ "4040590004313030303030303030303800000000000000000040010072f53f56c25240560000000078b00900000000009c3100000000030100011900030001090b0f080106c04fe40b4037310c0060e001ff018d01e05e0d0a"));
+
+ verifyPositions(decoder, binary(
+ "40405900043231334e5832303135303030303336000000000040010073dd735600df7356b9220000270b000000000000000000000400000000240e03000201120c0f0a19050c1e5808ca35530dd902540d9c010000e5300d0a"));
+
+ verifyPositions(decoder, binary(
+ "404055000431303031313132353239393837000000000000001002C1F0695230086A529C911100000000000F890000A60500000000036301014CFF000001190A0D0539191480D60488C5721800000000BF8A640D0A"));
+
+ verifyPositions(decoder, binary(
+ "40406000043130303131313235323939383700000000000000400705000000C1F0695249F469529C9111000000000069830000D80040000400036401014C04030001190A0D04201E1480D60488C5721800000000AF0101060F000F00EA1E0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "404057000431303031313132353239393837000000000000004002C1F06952F0F169529C9111000000000069830000470000000400036401014C01030078000505210C210D210F21102101073BE8030064280AEB930D0A"));
+
+ verifyPositions(decoder, binary(
+ "40405900043130303131313235323939383700000000000000400101C1F06952E7F069529C9111000000000069830000070000000400036401014C00030001190A0D0412041480D60488C57218000000009F01E803ED9A0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "4040B9000431303031313132353239393837000000000000004005C1F069521BF169529C9111000000000069830000130000000400036401014C0003000022032104210521062107210C210D210E210F2110211121132115211C211F21212124212E212F2130213121322133213C214221432144214521472149214A214C214D214E210100643B6232E803003E64280A3C24FE00010E010F00D5805A483C640000000000010000E02E000000066400000500000000A7710D0A"));
+
+ verifyAttributes(decoder, binary(
+ "404043000431303031313132353239393837000000000000004006C1F0695209F169529C91110000000000698300000D0000000400036401014C00030000009AF40D0A"));
+
+ verifyNull(decoder, binary(
+ "404086000431303031313132353239393837000000000000004004C1F0695200F169529C91110000000000698300000D0000000400036401014C00030022032104210521062107210C210D210E210F2110211121132115211C211F21212124212E212F2130213121322133213C214221432144214521472149214A214C214D214E219AE90D0A"));
+
+ verifyPositions(decoder, binary(
+ "40407F000431303031313132353239393837000000000000001001C1F06952FDF069529C91110000000000698300000C0000000000036401014C00030001190A0D04121A1480D60488C5721800000000AF4944445F3231364730325F532056312E322E31004944445F3231364730325F482056312E322E31000000DF640D0A"));
+
+ verifyPositions(decoder, binary(
+ "404044000c3631313135303030303935360000000000000000420600011e0a0f0b1312864fcd08c07a13030100640acf000004000a000000000000007ba083a66ad80d0a"));
+
+ verifyPosition(decoder, binary(
+ "40405c000c363131313530303030393536000000000000000040011c0a0f0e362dca53cd0860831303000000000300000000ff000000000000007ba083a650542d3639305f56312e312e320050542d3639302056312e32008a020d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040450004323132474c31313433303035303033000000000040082ca89b55a6a99b555c57000000000000c40200000b0000001400036401111f000302f5533bd653f10d0a"));
+
+ verifyNull(decoder, binary(
+ "40404d0004323132474c3131343330303530303300000000004007120000002ca89b55cba99b555c57000000000000c40200000b0000000000036401111f000102000101170000000068850d0a"));
+
+ verifyNull(decoder, binary(
+ "4040420004323132474c31313433303035303033000000000010022ca89b55cca99b555c57000000000000cf0200000b0000000000036401111f0000020013be0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040870004323132474c31313433303035303033000000000040052ca89b55e3a89b555c57000000000000c4020000040000001400036401111f0003000012042105210b210c210d210f211021112113211c2121212321242133213421422146214f212b50663603003ce9030dff060000600dffffc25865ffff9e02b43624000000003cbc0d0a"));
+
+ verifyNull(decoder, binary(
+ "4040d00004323132474c31313433303035303033000000000010013ec09b5596c29b555c57000000000000de0200000f0000000000036401111f000000004944445f3231334730325f532056322e322e36004944445f3231334730325f482056322e322e360032000110021003100410051006100710081009100a100b100c100d100e1011100111021103110411051106110711011202120312041201130213031301160216011701180218011b011c011d011e011f021f031f041f051f061f071f012102210126012701288a690d0a"));
+
+ verifyNull(decoder, binary(
+ "40404d0004323132474c3131343330303530303300000000004007050000003ec09b5564c29b555c57000000000000de0200000f0000002000036401111f0000020001010e00000000237e0d0a"));
+
+ verifyNull(decoder, binary(
+ "40401F00043130303131313235323939383700000000000000100303320D0A"));
+
+ verifyPositions(decoder, binary(
+ "40407F000431303031313132353239393837000000000000001001C1F06952FDF069529C91110000000000698300000C0000000000036401014C00030001190A0D04121A1480D60488C5721800000000AF4944445F3231364730325F532056312E322E31004944445F3231364730325F482056312E322E31000000DF640D0A"));
+
+ verifyPositions(decoder, binary(
+ "40405900043130303131313235323939383700000000000000400101C1F06952E7F069529C9111000000000069830000070000000400036401014C00030001190A0D0412041480D60488C57218000000009F01E803ED9A0D0A"));
+
+ verifyPositions(decoder, binary(
+ "40405900043335343034333035303834343134330000000000400100f61a7355c11b7355710000000b00000000000000000000000400000000240e0200020106060f100b2d5a78a7076ec0fb1d00008c065f010000ac220d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java
new file mode 100644
index 000000000..1864d9e84
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class CastelProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ CastelProtocolEncoder encoder = new CastelProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("40402000013132333435363738393031323334350000000000458301a94a0d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java
new file mode 100644
index 000000000..28236c87f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CautelaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CautelaProtocolDecoder decoder = new CautelaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "20,010907000000,14,02,18,16.816667,96.166667,1325,S,*2E"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java
new file mode 100644
index 000000000..914166a58
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CellocatorFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CellocatorFrameDecoder decoder = new CellocatorFrameDecoder();
+
+ verifyFrame(
+ binary("4D4347500BA9880B00C80A7E003800000000000806000001210A14000613000000000D4457A5F71442AC02E80300000100040707000011171408060E1C08000100000200020000FD"),
+ decoder.decode(null, null, binary("4D4347500BA9880B00C80A7E003800000000000806000001210A14000613000000000D4457A5F71442AC02E80300000100040707000011171408060E1C08000100000200020000FD")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java
new file mode 100644
index 000000000..523ad1d5c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CellocatorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CellocatorProtocolDecoder decoder = new CellocatorProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "4d4347500bde66220048165400cb0000000000080600000124161400061300050402095501aaf8218787fcac390100010000070700013133150f07131905001e000100000293001e00697e6f24148240040000000083400400000000844004000000008540040000000086400400000000874004000000008840040000000089400400000000814004000000008c4004000000008d4004000000008e4004000000009140040000000090400400000000804004000000008a400400000000974004000000008b4004000000009d4004000000009b400400000000da"));
+
+ verifyPosition(decoder, binary(
+ "4D4347500BA9880B00880A3900EE00000000000806000001210A140002B6001E0034419B1B1900400401000000014004000000000240046000000003400480000000044004DA0A0000054004000000000640045E000000074004310000000840042B000000094004870000000B4004B10B00000C4004590000000D40040000000010400465000000114004780C000012400465650700144004A4000000154004207F000016400400000000174004000000001E4004000000001F40040000000020400400000000214004000000002440040000000006130003040211D67DA4F7883AAF028403000001000007070001250A1004090E1905001E0001000062"));
+
+ verifyPosition(decoder, binary(
+ "4d4350470041420f000402021226d8a70221d801010000000001000000000000000000000000c4d90000000ca7a741ff0096dd15a40700000000000000001619130c01e307e4"));
+
+ verifyPosition(decoder, binary(
+ "4D434750008AD01500080103011804000000460020000000005E750000000000000000000000C34300040204DA4DA30367195703E803000000000000000001030F0802E10778"));
+
+ verifyPosition(decoder, binary(
+ "4D434750008AD01500080102011804000000360060000000005E750000000000000000000000C24300040204DA4DA30367195703E80300000000000000003B020F0802E107DF"));
+
+ verifyPosition(decoder, binary(
+ "4D4347500006000000081A02021204000000210062300000006B00E100000000000000000000E5A100040206614EA303181A57034E1200000000000000001525071403D60749"));
+
+ verifyPosition(decoder, binary(
+ "4d434750000101000008011f041804000000200100000000005e750000000000000000000000548500040204da4da30367195703e80300000000000000002014151007dd07f7"));
+
+ verifyPosition(decoder, binary(
+ "4d434750005e930100080102041804000000200f20000000005e7500000000000000000000005af400040204da4da30367195703e8030000000000000000021a111e08dd0760"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
new file mode 100644
index 000000000..616640116
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class CellocatorProtocolEncoderTest extends ProtocolTest {
+
+ @Ignore
+ @Test
+ public void testEncode() throws Exception {
+
+ CellocatorProtocolEncoder encoder = new CellocatorProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 0);
+ command.set(Command.KEY_DATA, "1");
+
+ verifyCommand(encoder, command, binary("4D434750000000000000000000000303101000000000000026"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java
new file mode 100644
index 000000000..a386b6b47
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java
@@ -0,0 +1,78 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CguardProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CguardProtocolDecoder decoder = new CguardProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "IDRO:354868050655283"));
+
+ verifyPosition(decoder, text(
+ "NV:190225 144543:55.776505:37.729000:0:10:0:0"));
+
+ verifyAttributes(decoder, text(
+ "BC:190225 144543:CSQ1:64:NSQ1:0:NSQ2:1:BAT1:100"));
+
+ verifyAttributes(decoder, text(
+ "BC:190225 142653:CSQ1:80:NSQ1:0:NSQ2:1:BAT1:3.53"));
+
+ verifyPosition(decoder, text(
+ "NV:170409 031456:56.808553:60.595476:0:NAN:0"));
+
+ verifyAttributes(decoder, text(
+ "BC:170409 031456:CSQ1:64:NSQ1:17:PWR1:0"));
+
+ verifyPosition(decoder, text(
+ "NV:161007 122043:55.812730:37.733689:3.62:NAN:244.05:143.4"));
+
+ verifyPosition(decoder, text(
+ "NV:161007 122044:55.812732:37.733670:3.97:NAN:260.95:143.9"));
+
+ verifyAttributes(decoder, text(
+ "BC:161007 122044:CSQ1:77:NSQ1:18:BAT1:100"));
+
+ verifyPosition(decoder, text(
+ "NV:160711 044023:54.342907:48.582590:0:NAN:0:110.1"));
+
+ verifyPosition(decoder, text(
+ "NV:160711 044023:54.342907:-148.582590:0:NAN:0:110.1"));
+
+ verifyAttributes(decoder, text(
+ "BC:160711 044023:CSQ1:48:NSQ1:7:NSQ2:1:BAT1:98:PWR1:11.7:CLG1:NAN"));
+
+ verifyAttributes(decoder, text(
+ "BC:160711 044524:CSQ1:61:NSQ1:18:BAT1:98:PWR1:11.7:CLG1:NAN"));
+
+ verifyNull(decoder, text(
+ "VERSION:3.3"));
+
+ verifyPosition(decoder, text(
+ "NV:160420 101902:55.799425:37.674033:0.94:NAN:213.59:156.6"));
+
+ verifyAttributes(decoder, text(
+ "BC:160628 081024:CSQ1:32:NSQ1:10:BAT1:100"));
+
+ verifyAttributes(decoder, text(
+ "BC:160628 081033:NSQ2:0"));
+
+ verifyPosition(decoder, text(
+ "NV:160630 151537:55.799913:37.674267:0.7:NAN:10.21:174.9"));
+
+ verifyAttributes(decoder, text(
+ "BC:160630 153316:BAT1:76"));
+
+ verifyAttributes(decoder, text(
+ "BC:160630 153543:NSQ2:0"));
+
+ verifyNull(decoder, text(
+ "PING"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java
new file mode 100644
index 000000000..146e13ae8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class CityeasyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CityeasyProtocolDecoder decoder = new CityeasyProtocolDecoder(null);
+
+ verifyNotNull(decoder, binary(
+ "545400853575570249020100033b3430342c34352c31303638312c31313632312c33352c31303638312c31313632322c32332c31303638312c32383938332c32332c31303638312c31313632332c32312c31303638312c32333338312c31372c31303638312c32323538332c31372c31303638312c32363434312c31330000000d352e0d0a"));
+
+ verifyNull(decoder, binary(
+ "54540019357557024902010002520704100000000bbe700d0a"));
+
+ verifyNull(decoder, binary(
+ "5454001735755702490201434a01000000000c24280d0a"));
+
+ verifyNull(decoder, binary(
+ "545400153520000000000100010000000111000D0A"));
+
+ verifyNull(decoder, binary(
+ "54540019357557024902000002520704300000000376390d0a"));
+
+ verifyPosition(decoder, binary(
+ "5454006135200000000001000332303134313131303039353430392C412C342C4E2C32322E3533373232382C452C3131342E3032323737342C302E312C312E392C35302E363B3436302C302C31303137332C343635322C34310000000B63130D0A"),
+ position("2014-11-10 09:54:09.000", true, 22.53723, 114.02277));
+
+ verifyPosition(decoder, binary(
+ "5454006135200000000001000432303134313131303039353330362C412C352C4E2C32322E3533373233352C452C3131342E3032323838312C302E322C312E362C35342E313B3436302C302C31303137332C343635322C343100000045EC620D0A"));
+
+ verifyPosition(decoder, binary(
+ "5454009035755702490200000332303135303732393033303834352c412c362c4e2c31322e3833353735362c452c37372e3638373039362c302e332c312e322c3931302e303b3430342c34352c31303638312c31313632312c34332c31303638312c31313632332c32312c31303638312c32323538332c32302c31303638312c32333338312c31380000000267370d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java
new file mode 100644
index 000000000..f0eede4bf
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class CityeasyProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ CityeasyProtocolEncoder encoder = new CityeasyProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+6");
+
+ verifyCommand(encoder, command, binary("5353001100080001680000000B60820D0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java
new file mode 100644
index 000000000..83722ef97
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ContinentalProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ContinentalProtocolDecoder decoder = new ContinentalProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "5356003216001eb48505025b4001e90f7f18ce0f00522200400001015b4001e9000e820100000c24000100014e0400736a7a"),
+ position("2018-07-06 23:57:29.000", true, -23.46609, -46.54497));
+
+ verifyPosition(decoder, binary(
+ "5356002A1100003039030243A68B5700FEB5AB00FD715F012700000143A68B57000E000000000C2F00000130"),
+ position("2005-12-19 10:28:39.000", true, -23.49027, -46.55138));
+
+ verifyPosition(decoder, binary(
+ "5356002a0d0010a12403025a9ea47f00feb48400fd6e63000c0000015a9ea480000e000100000c000000"));
+
+ verifyPosition(decoder, binary(
+ "5356002a0d0010a1240302581b944100febed800fd9fa30139001300581c73fa000e000000000d000001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java
new file mode 100644
index 000000000..7ffad8015
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CradlepointProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CradlepointProtocolDecoder decoder = new CradlepointProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "356526070063940,0,4337.19009,N,11612.34705,W,0.0,277.2,AT&T,,,-79,,-14.0,"));
+
+ verifyPosition(decoder, text(
+ "356526070063940,1,4337.19008,N,11612.34705,W,0.0,277.2,AT&T,,,-79,,-14.0,"));
+
+ verifyPosition(decoder, text(
+ "+14063964266,162658,4333.62404,N,11636.23469,W,0.0,,Verizon Wireless,LTE,-107,-74,-16,,100.68.169.178"));
+
+ verifyPosition(decoder, text(
+ "+12084014675,162658,4337.174385,N,11612.338373,W,0.0,,Verizon,,-71,-44,-11,,"));
+
+ verifyPosition(decoder, text(
+ "353547063544681,170515,3613.25,N,11559.14,W,0.0,,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "353547060558130,170519,4337.17,N,11612.34,W,0.0,294.7,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "+12084014675,162658,4337.174385,N,11612.338373,W,0.0,,Verizon,,-71,-44,-11,,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java
new file mode 100644
index 000000000..ddb5aca18
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DishaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DishaProtocolDecoder decoder = new DishaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$A#A#864161028848856#A#053523#010216#2232.7733#N#08821.1940#E#002.75#038.1#09#00.8#1800#0#000#0000#9999#11.7#285.7#0001*"));
+
+ verifyPosition(decoder, text(
+ "$A#A#864161028848856#A#182134#090116#2232.0191#N#08821.3278#E#001.74#231.4#04#01.5#1300#0#000#0000#9999#54.4#6407.7#0000*"),
+ position("2016-01-09 18:21:34.000", true, 22.53365, 88.35546));
+
+ verifyPosition(decoder, text(
+ "$A#A#353943046615971#A#064219#281113#1836.7267#N#07347.4177#E#000.00#280.4#09#00.8#3000#2#100#0000#8888#86.5#3919.1#0000*"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
new file mode 100644
index 000000000..e9d98d6d3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DmtHttpProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DmtHttpProtocolDecoder decoder = new DmtHttpProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"SerNo\":131693,\"IMEI\":\"356692063643328\",\"ICCID\":\"8944538523010771676\",\"ProdId\":33,\"FW\":\"33.4.1.27\",\"Records\":[{\"SeqNo\":125,\"Reason\":11,\"DateUTC\":\"2017-05-11 05:58:44\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":2,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14641,\"3\":2484,\"4\":26,\"5\":10868},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":0,\"13\":309,\"14\":9921,\"15\":3},\"FType\":7}]},{\"SeqNo\":128,\"Reason\":11,\"DateUTC\":\"2017-05-11 17:59:45\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":2,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14607,\"3\":2752,\"4\":26,\"5\":11062},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":1,\"13\":325,\"14\":10881,\"15\":3},\"FType\":7}]},{\"SeqNo\":130,\"Reason\":9,\"DateUTC\":\"2017-05-11 19:30:03\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":3,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14599,\"3\":2731,\"4\":27,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":2,\"13\":329,\"14\":11121,\"15\":3},\"FType\":7}]},{\"SeqNo\":131,\"Reason\":11,\"DateUTC\":\"2017-05-11 19:32:03\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14403,\"3\":2783,\"4\":27,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":2,\"13\":330,\"14\":11181,\"15\":3},\"FType\":7}]},{\"SeqNo\":133,\"Reason\":11,\"DateUTC\":\"2017-05-11 19:36:15\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14319,\"3\":2898,\"4\":23,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":3,\"13\":331,\"14\":11241,\"15\":3},\"FType\":7}]}]}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
new file mode 100644
index 000000000..8977cf194
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DmtProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DmtProtocolDecoder decoder = new DmtProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0255003300001b00003335333232393032373533393235310038393931353030303030303030313330343539340000000403041910780603"));
+
+ verifyPositions(decoder, false, binary(
+ "025504ab013d00c21a00004829900c0300154929900cbd163617b08a94c7fa003c07032c131a0302080300000000000300060f019b14037e0e0463000558140607213d00c31a0000ca29900c030015ca29900ca3033817bbb895c71401be0603b310190302080300000000000300060f019b14036d0e0463000558140607213d00c41a0000472a900c030015472a900c8d453817423e96c7fa000200040013270302080300000000000300060f019b1403840e0463000546140606213d00c51a0000c52a900c030015c52a900c184c3817c35296c724010400050016180302080300000000000300060f019b1403750e0463000547140606213d00c61a0000462b900c030015462b900cbd8a361703b495c710018c07085a10210302080300000000000300060f019b1403630e0463000546140606213d00c71a0000c52b900c030015c52b900cf6d63517455a94c7e9004c05035a10240302080300000000000300060f019b14036e0e0463000545140606213d00c81a00004b2c900c0300154b2c900c766d3517ddf093c7320107000d00102e0302080300000000000300060f019b1403750e046300054314060521"));
+
+ verifyPositions(decoder, false, binary(
+ "02551040000eaca40d00d2b8e562c51f9912f39a6bee00007e420091090903070100000000008b1065360000000000007fd401c4fcf2feffffffffffffffffee0000003f1b"));
+
+ verifyPositions(decoder, false, binary(
+ "02551080000eada40d00d2b8e58ac51f9912f39a6bee00007e42007e090709070000000000009010fc330000000000007fc201a0fc04ffffffffffffffffffe5000000c5d00eaea40d00d2b8e58ac51f9912f39a6bee00007e42007e09070207000000000000851008340000000000007fc201a0fc04ff0000000000000000e5000000c96d"));
+
+ verifyNull(decoder, binary(
+ "025500310038f90100333533333233303831363639373330003839363130313835303031383234383434363330002202010900000000"));
+
+ verifyNull(decoder, binary(
+ "0255220000"));
+
+ verifyPositions(decoder, false, binary(
+ "025504d80352000602000052185c0803001552185c0842ee19eaba2524682d000d060973112b0302080100000000000300060901421003e40604140007190b300000000c030000000de80100000ec90e00000f0700000052000702000069185c0803001569185c089ac019ea0ad223682300fb02047d152f03020801000000000003000609013f1003fc0604140007190b300000000c030000000de90100000ecb0e00000f0700000052000802000092185c0803001592185c0800a619eaa5e7226821009c0506880e250302080100000000000300060901411003f30604140007190b300000000c030000000dea0100000ef10e00000f07000000520009020000a9185c08030015a9185c0818ae19ea1e62226826001e05038e0e2203020801000000000003000609013f1003030704140007190b300000000c030000000deb0100000ef60e00000f0700000052000a020000c0185c08030015c0185c0893b619ea7fd321681a00640403860f1d0302080100000000000300060901401003ff0604140007190b300000000c030000000dec0100000ef80e00000f0700000052000b020000d7185c08030015d7185c08e08519eab7c921682300fd04035510270302080100000000000300060901401003ea0604140007190b300000000c030000000ded0100000efa0e00000f0700000052000c020000ee185c08030015ee185c08f61719ea61e221682c004c0503540f190302080100000000000300060901421003dd0604140007190b300000000c030000000dee0100000efc0e00000f0700000052000d02000005195c0803001505195c0836b518eac9f221683000fa0107740e2d03020801000000000003000609013f1003fe0604140007190b300000000c030000000def0100000efe0e00000f0700000052000e0200001d195c080300151d195c08d1b518ea2d6721682300980502870e1d0302080100000000000300060901411003ed0604140007190b300000000c030000000df00100000e000f00000f0700000052000f02000034195c0803001534195c086acd18ea742b2168400006020500132903020801000000000003000609013d10030d0704140007190b300000000c030000000df10100000e030f00000f070000005200100200004d195c080300154d195c08dfba18eab81721684e003000093b0e1e03020801000000000003000609013e1003130704140007190b300000000c030000000df20100000e050f00000f0700000052001102000065195c0803001565195c081db318ea871f216822000400080416250302080100000000000300060901401003060704140007190b300000000c030000000df30100000e090f00000f07000000"));
+
+ verifyPositions(decoder, false, binary(
+ "025504e9032f000d000000000000001501222700524553455420446172742033342e322e312e3920666c6167733d312057443d303f000e0000000000000015013214004e6f2041646d696e20706172616d7320666f756e64202d207573696e672064656661756c7473202b204175746f41504e37000f00000000000000090015000000000000000000000000000000000000000000020805000000000007000609012b1002400003700e37001000000000000000090015000000000000000000000000000000000000000000020801000000000007000609012b1002400003700e37001100000000000000090015000000000000000000000000000000000000000000020800000000000007000609012b1002400003700e37001200000000000000020015000000000000000000000000000000000000000000020800000000000006000609012b1002400003700e370013000000000000000f001500000000000000000000000000000000000000000002080000000000000200060901271002370003670e2e0014000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343700150000000000000017001500000000000000000000000000000000000000000002080000000000000200060901071002300003d60e2a00160000000000000015011d130054756d626c65722074726967676572656420636f735e32203c20302e0017000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343700180000000000000017001500000000000000000000000000000000000000000002080000000000000200060901071002300003f70e2a00190000000000000015011d130054756d626c65722074726967676572656420636f735e32203c203026001a000000000000001501190b0047534d3a20544350206261642053594e432063686172732e001b000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343a001c0000000000000017001500000000000000000000000000000000000000000002080000000000000200060c01c90f02300003e20f041f002a001d0000000000000015011d130054756d626c65722074726967676572656420636f735e32203c20302e001e000000000000001501211300526f6c6c20646574656374656420636f735e32203c203338333535333838343a001f0000000000000017001500000000000000000000000000000000000000000002080000000000000200060c01d80f02300003ff0f0418002a00200000000000000015011d130054756d626c65722074726967676572656420636f735e32203c2030"));
+
+ verifyNull(decoder, binary(
+ "025500310038f90100333533333233303831363639373330003839363130313435363839393333303030303835002202010900000000"));
+
+ verifyPositions(decoder, binary(
+ "0255043D003D004746000096D684020B001502D48402F043F4EC2A6909452B001F00050011230302080000000000000A00060F041D0001FE0F021E0005000003BF08"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java
new file mode 100644
index 000000000..107a2a435
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DwayProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ DwayProtocolDecoder decoder = new DwayProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "AA55,36,10024,1,171025,161055,36.0294,-79.7881,201, 2.5,111,1000,0000,00000,3578,0,0,0,D"));
+
+ verifyPosition(decoder, text(
+ "AA55,115,318,1,171024,195059,28.0153,-82.4761,3, 1.0,319,1000,0000,00000,4244,0,0,0,D"));
+
+ verifyPosition(decoder, text(
+ "AA55,117,318,1,171025,153758,28.0152,-82.4759,19, 0.6,319,1000,0000,10000,4242,0,0,0,D"));
+
+ verifyPosition(decoder, text(
+ "AA55,1,123456,1,140101,101132,22.5500,113.6770,75,70.5,320,1100,0011,1110,3950,33000,24000,12345678"));
+
+ verifyNull(decoder, text(
+ "AA55,HB"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..b26991ae7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java
@@ -0,0 +1,55 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EasyTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EasyTrackProtocolDecoder decoder = new EasyTrackProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "*ET,358155100054249,HB,A,100b06,053318,803a0b51,03d507c9,0017,0000,00400000,07,100,0000,1435,63"));
+
+ verifyNull(decoder, text(
+ "*ET,358155100054249,MQ"));
+
+ verifyNull(decoder, text(
+ "*ET,358155100054249,TX,A,100b06,053230"));
+
+ verifyPosition(decoder, text(
+ "*ET,358155100054249,HB,A,100b06,053212,803a0b20,03d507a2,0054,0000,40400000,06,100,0000,1435,44"));
+
+ verifyNull(decoder, text(
+ "*ET,135790246811221,GZ,0001,0005"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,0A090D,101C0D,00CF27C6,0413FA4E,0000,0000,00000000,20,4,0000,00F123"),
+ position("2010-09-13 16:28:13.000", true, 22.62689, 114.03021));
+
+ verifyNull(decoder, text(
+ "*ET,358155100048430,CC,0.0,V,100603,141817,80d77ae8,81ab1ffd,0000,6b08,40000000,19,99,0000,fa9,918"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,050915,0C2A27,00CE5954,04132263,0000,0000,01000000,20,4,0000,001254"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,0A090D,101C0D,00CF27C6,0413FA4E,0000,0000,00000000,20,4,0000,00F123,100"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,DW,A,0A090D,101C0D,00CF27C6,8413FA4E,0000,0000,00000000,20,4,0000,00F123,100"));
+
+ verifyPosition(decoder, text(
+ "*ET,358155100003016,HB,A,0d081e,07381e,8038ee09,03d2e9be,004f,0000,40c00000,0f,100,0000,00037c,29"));
+
+ verifyPosition(decoder, text(
+ "*ET,358155100003016,HB,A,0d081e,073900,8038ee2f,03d2e9fd,0114,0000,40c00000,12,100,0000,00037c,32"));
+
+ verifyPosition(decoder, text(
+ "*ET,135790246811221,HB,A,050915,0C2A27,00CE5954,04132263,0000,0000,01000000,20,4,0000,00F123,100,200"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
new file mode 100644
index 000000000..e24da3173
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
@@ -0,0 +1,118 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class EelinkProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EelinkProtocolDecoder decoder = new EelinkProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "454C0027E753035254407167747167670100180002035254407167747100200205020500010432000086BD"));
+
+ verifyAttribute(decoder, binary(
+ "6767070006000e0077035d"),
+ Position.KEY_IGNITION, true);
+
+ verifyAttributes(decoder, binary(
+ "676707006502df5c89fde800bc3fa8030302005555045b555555057a5555550b225555550c105c55550d115555550e7e5555550f4555555510017b5555112b5555551f01ed5555208005b0012100005555407ad000004237f5555589000000498a0000aef78b00000000"));
+
+ verifyAttribute(decoder, binary(
+ "676712003400e45c5b0ade02012e03702d87064546aa24066a1086018a0000002dc1a0ffffffff0afd074d000000000000000000000000fce0"),
+ Position.PREFIX_TEMP + 2, -50.0);
+
+ verifyAttribute(decoder, binary(
+ "6767120043000e5c37387c0304e4e1b4f8194fa800160013009408012e03702d8706453c6e5b066f115f05710000001b067f8d248d240313020500000000000000000000000001cc"),
+ Position.PREFIX_TEMP + 2, 28.75);
+
+ verifyPosition(decoder, binary(
+ "676714002414B05AD43A7D03026B92B10C395499FFD7000000000701CC00002495000014203604067B"));
+
+ verifyNotNull(decoder, binary(
+ "676714004F14B0E68CAFE58AA8E68AA5E8ADA621E5B9BFE4B89CE79C81E6B7B1E59CB3E5B882E58D97E5B1B1E58CBAE696B0E8A5BFE8B7AF3138EFBC88E8B79DE5AE87E998B3E5A4A7E58EA63230E7B1B3EFBC89"));
+
+ verifyPosition(decoder, binary(
+ "676780005a000001000000004c61743a4e33312e38333935352c4c6f6e3a5738322e36313334362c436f757273653a302e30302c53706565643a302e30306b6d2f682c4461746554696d653a323031372d31322d30322031313a32393a3433"));
+
+ verifyPosition(decoder, binary(
+ "676780005E5788014C754C754C61743A4E32332E3131313734330A4C6F6E3A453131342E3430393233380A436F757273653A302E30300A53706565643A302E31374B4D2F480A446174652054696D653A323031352D30392D31332032303A32313A3230"));
+
+ verifyPosition(decoder, binary(
+ "454C0050EAE2035254407167747167671200410021590BD93803026B940D0C3952AD0021000000000501CC0001A53F0170F0AB1305890F11000000000000C2D0001C001600000000000000000000000000000000"));
+
+ verifyNull(decoder, binary(
+ "676701000c007b03525440717505180104"));
+
+ verifyPosition(decoder, binary(
+ "6767120048000559c1829213059a7400008e277d000c000000000800cc00080d2a000034df3cf0b429dd82cad3048910320000000000007b7320d005ba0000000019a000000000000000000000"));
+
+ verifyPosition(decoder, binary(
+ "6767050020213b59c6aecdff41dce70b8b977d00000001fe000a36e30078fe010159c6aecd"));
+
+ verifyPosition(decoder, binary(
+ "676705002102b459ae7388fcd360d7034332b1000000028f000a4f64002eb101010159ae7388"));
+
+ verifyPosition(decoder, binary(
+ "676702001c02b259ae7387fcd360d6034332b2000000028f000a4f64002eb10101"));
+
+ verifyPosition(decoder, binary(
+ "6767050022001F59643640000000000000000000000001CC0000249500142000015964A6C0006E"));
+
+ verifyAttributes(decoder, binary(
+ "67670300040021006E"));
+
+ verifyPosition(decoder, binary(
+ "676705002200255964369D000000000000000000000001CC0000249500142000025964A71D006A"));
+
+ verifyAttributes(decoder, binary(
+ "67670300040028006A"));
+
+ verifyPosition(decoder, binary(
+ "676712002d066c592cca6803002631a60b22127700240046005c08020d000301af000da0fd12007f11ce05820000001899c0"));
+
+ verifyPosition(decoder, binary(
+ "676702002509f65868507603a1e92e03cf90fe000000019f000117ee00111e0120631145003101510000"));
+
+ verifyAttributes(decoder, binary(
+ "676712001e0092579714d60201f90001785003a301cd1a006a118504f2000000000000"));
+
+ verifyPosition(decoder, binary(
+ "676712003400505784cc0b130246479b07d05a06001800000000070195039f046100002cc52e6466b391604a4900890e7c00000000000006ca"));
+
+ verifyPosition(decoder, binary(
+ "676714002b00515784cc24130246479b07d05a06001800010000060195039f046100002cc52f6466b391604a49020089"));
+
+ verifyNull(decoder, binary(
+ "676701000c002603541880486128290120"));
+
+ verifyPosition(decoder, binary(
+ "676704001c01a4569ff2dd0517a0f7020b0d9a06011000d8001e005b0004450183"));
+
+ verifyPosition(decoder, binary(
+ "676705002200ba569fc3520517a0d8020b0f740f007100d8001e005b0004460101569fd162001f"));
+
+ verifyPosition(decoder, binary(
+ "676702002500bb569fc3610517a091020b116000001900d8001e005b00044601001f1170003200000000"));
+
+ verifyPosition(decoder, binary(
+ "676704001c00b7569fc3020517a2d7020b08e100000000d8001e005b0004460004"));
+
+ verifyNull(decoder, binary(
+ "676701000b001b035418804661834901"));
+
+ verifyAttributes(decoder, binary(
+ "6767030004001A0001"));
+
+ verifyAttributes(decoder, binary(
+ "6767070088001050E2281400FFFFFFFF02334455660333445566043344556605AA00000007334455660A334455660B334455660C4E2000000DAA0000000E334455660F3344556610AAAA000011334455661C334455661F334455662133445566423344556646334455664D334455665C334455665E33445566880000000089000000008A000000008B00000000"));
+
+ verifyPosition(decoder, binary(
+ "676702001b03c5538086df0190c1790b3482df0f0157020800013beb00342401"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java
new file mode 100644
index 000000000..616ca0b52
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class EelinkProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(new EelinkProtocolEncoder(null, false), command, binary("676780000f0000010000000052454c41592c3123"));
+
+ verifyCommand(new EelinkProtocolEncoder(null, true), command, binary("454c001eb41a0123456789012345676780000f0000010000000052454c41592c3123"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java
new file mode 100644
index 000000000..523d095f2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EgtsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EgtsFrameDecoder decoder = new EgtsFrameDecoder();
+
+ verifyFrame(
+ binary("0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2"),
+ decoder.decode(null, null, binary("0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2")));
+
+ verifyFrame(
+ binary("0100000b000300704300db0500006c27"),
+ decoder.decode(null, null, binary("0100000b000300704300db0500006c270100000b0003007143009d0600003c7e")));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java
new file mode 100644
index 000000000..2afb72e08
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java
@@ -0,0 +1,60 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EgtsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeWithObjectId() throws Exception {
+
+ EgtsProtocolDecoder decoder = new EgtsProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0100020b002300020001871c00020000010105190000ab0800006247396e615734366347467a63336476636d513daadf"));
+
+ verifyPositions(decoder, binary(
+ "0100020b004600010001b81800030001f299c0d80202101500c9c52f1100552e9c80e4ca7911f5805b00000000031800040001f299c0d80202101500cbc52f1100612e9c00dbca79116c803e00000000037c13"));
+
+ verifyPositions(decoder, binary(
+ "0100020b005e01030001ed180005000162c72c9a0202101500c4c52f1100477e9f0047c979010000ad000000000318000600017ee0710c0202101500c9c52f11003ee59f8061e97a0100801b00000000031800070001b6eeb6c00202101500c7c52f110077669d00b9707a116a015600000000031800080001b6eeb6c00202101500cdc52f11007c669d004e717a117a0158000000000318000900018b4685f70202101500c8c52f11006ee09f0027ca7c11650079000000000318000a0001f299c0d80202101500c9c52f1100552e9c80e4ca7911f5805b000000000318000b0001f299c0d80202101500cbc52f1100612e9c00dbca79116c803e000000000318000c0001731347010202101500c7c52f1100a3699a80db3c7a010000e5000000000318000d0001c85285f70202101500cbc52f1100e8979900f3497b114d0101000000000318000e0001aa4358810202101500cdc52f11002d689a80ab427a0100009300000000032b9f"));
+
+ verifyNull(decoder, binary("0100000b0003006c430004010000acfb"));
+
+ verifyPositions(decoder, binary(
+ "0100000b0086035ddd016d18004f049579b000001e2fc11002021015001e2fc1107ac3919f59cc5c7a0b0000000000003000180050049579b00000242fc1100202101500242fc1100fb5919f2dbf5c7a0b0000000000003000180051049579b00000312fc1100202101500312fc110b899919f94cf5c7a0b00000000000030001e0052049579b00000ba62a2120202120900000003000000000000150500025c00000013070003000000000000180053049579b000004e2fc11002021015004e2fc11087ba919f8dd45c7a0b0000000000003000180054049579b00000552fc1100202101500552fc1106ecb919f2aec5c7a0b00000000000030001e0055049579b00000d562a2120202120900000003000000000000150500025c00000013070003000000000000180056049579b000005c2fc11002021015005c2fc11059d9919f54fa5c7a0b0000000000003000180057049579b000006e2fc11002021015006e2fc110309f919fc2db5c7a0b0000000000003000180058049579b00000762fc1100202101500762fc1104690919f94cf5c7a0b00000000000030001e0059049579b00000f662a2120202120900000003000000000000150500025c000000130700030000000000001e005a049579b000001463a2120202120900000003000000000000150500025c0000001307000300000000000018005b049579b00000b32fc1100202101500b32fc110c491919fa2c65c7a0b00000000000030001e005c049579b000003363a2120202120900000003000000000000150500025c0000001307000300000000000018005d049579b00000ca2fc1100202101500ca2fc11089b9919fd5f95c7a0b00000000000030001e005e049579b000005163a2120202120900000003000000000000150500025c000000130700030000000000001e005f049579b000006f63a2120202120900000003000000000000150500025c00000013070003000000000000180060049579b00000f42fc1100202101500f42fc11087ba919f43db5c7a0b0000000000003000180061049579b000000730c11002021015000730c110f9c3919fe1c65c7a0b0000000000003000180062049579b000000930c11002021015000930c1106ecb919f3ab65c7a0b00000000000030001e0063049579b000008d63a2120202120900000003000000000000150500025c00000013070003000000000000140064049579b00000ac63a2120202120900000003000000000000150500025c000000ce53"));
+
+ verifyNull(decoder, binary("0100000b00100091030072000100060000000002020003009203000009"));
+
+ }
+
+ @Test
+ public void testDecodeWithAuth() throws Exception {
+
+ EgtsProtocolDecoder decoder = new EgtsProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0100010b002200c06401f21700c1640171360d00010101140071360d000238363539303500000000000000000047fc"));
+
+ verifyNull(decoder, binary(
+ "0100000b002400a0d601f01900030081030000000101011600030000004238363434393530333436343333373600014cdc"));
+
+ verifyPositions(decoder, binary(
+ "0100000b002700030e01211800030e8573890100845e980f0202101500f85d980fb37f50aae9653c2b193708317b00000001c51b"));
+
+ verifyPositions(decoder, binary(
+ "0100010b00a308c26401029808c3640171360d000202101800e19a7b0fcfb4c49a0bfdb87a911801b70000000010d90000180400021c0000120300000000101800f39a7b0f2fc9c39a9bf2b87a914001b50000000010da0000180400021c0000120300000000101800fa9a7b0fc663c39a21eeb87a914001b60000000010da0000180400021c0000120300000000101800069b7b0f56d8c29a26ebb87a919600ab0000000010da0000180400021c00001203000000001018000a9b7b0fb2c5c29a19f4b87a915a007d0000000010da0000180400021c0000120300000000101800089b7b0f68ccc29a21eeb87a9164008f0000000010da0000180400021c0000120300000000101800079b7b0fa0d1c29aa4ecb87a918200980000000010da0000180400021c00001203000000001018000b9b7b0f34c4c29ad3f7b87a915a00670000000010da0000180400021c00001203000000001018000f9b7b0f3dbec29aaf0fb97a91c8005e0000000010dc0000180400021c0000120300000000101800199b7b0f42bbc29a0855b97a9178006b0000000010db0000180400021c00001203000000001018001b9b7b0fc8b6c29a3c5db97a916e007e0000000010db0000180400021c00001203000000001018001a9b7b0fc4b9c29a8159b97a916e00750000000010db0000180400021c00001203000000001018001d9b7b0f94aec29a3363b97a916400930000000010db0000180400021c00001203000000001018001c9b7b0fcdb3c29a3760b97a916e008a0000000010db0000180400021c0000120300000000101800209b7b0f28a1c29af263b97a918200ba0000000010db0000180400021c00001203000000001018001f9b7b0f61a6c29af263b97a917800b30000000010db0000180400021c00001203000000001018001e9b7b0f58acc29af263b97a916400a50000000010db0000180400021c0000120300000000101800299b7b0ff26fc29ab561b97a916e00b20000000010d90000180400021c00001203000000001018002d9b7b0fd05bc29a3760b97a916e00bd0000000010d80000180400021c0000120300000000101800359b7b0f4f31c29abe5bb97a916400b50000000010d70000180400021c0000120300000000101800379b7b0f5d28c29abe5bb97a916e00b40000000010d60000180400021c0000120300000000101800369b7b0fd62cc29abe5bb97a916400bd0000000010d60000180400021c00001203000000001018003c9b7b0fca09c29a0358b97a918c00bc0000000010d50000180400021c0000120300000000101800419b7b0f38ebc19a0855b97a916e00b40000000010d60000180400021c0000120300000000101800449b7b0f4edcc19a8a53b97a916400c30000000010d60000180400021c0000120300000000101800469b7b0fded1c19acb52b97a916e00b70000000010d60000180400021c0000120300000000101800649b7b0f7a69c19a154cb97a810000bc0000000010d60000180400021c0000120300000000101800709b7b0fcc5cc19a114fb97a915000970000000010d70000180400021c0000120300000000101800729b7b0fda53c19a8a53b97a91a0007d0000000010d70000180400021c0000120300000000101800749b7b0fa24ec19a3c5db97a91a000650000000010d70000180400021c0000120300000000101800789b7b0fe74ac19aca7eb97a910e015c0000000010d80000180400021c00001203000000001018007f9b7b0f6e46c19a78e0b97a9190015c0000000010d80000180400021c0000120300000000101800869b7b0f3641c19a144eba7a9190015c0000000010d60000180400021c00001203000000001018008d9b7b0ffd3bc19a74b9ba7a9190015c0000000010d40000180400021c0000120300000000101800949b7b0fc536c19a1524bb7a9186015b0000000010d20000180400021c00001203000000001018009b9b7b0fce30c19af78dbb7a919a015c0000000010d00000180400021c0000120300000000101800a29b7b0f552cc19a93fbbb7a9172015d0000000010cd0000180400021c0000120300000000101800b09b7b0f2521c19a6ec0bc7a9186015b0000000010c90000180400021c0000120300000000101800a99b7b0f5e26c19a8759bc7a915e015b0000000010cb0000180400021c0000120300000000101800b79b7b0fa81fc19a1328bd7a9172015b0000000010c70000180400021c0000120300000000101800be9b7b0f6b1dc19ac686bd7a914a015c0000000010c60000180400021c0000120300000000101800c19b7b0fed1bc19ad2a9bd7a912201530000000010c60000180400021c0000120300000000101800c39b7b0f6223c19af4bdbd7a910401420000000010c60000180400021c0000120300000000101800c29b7b0fe91ec19a42b4bd7a910e014c0000000010c60000180400021c0000120300000000101800c59b7b0f502fc19a1acfbd7a91fa00300000000010c60000180400021c0000120300000000101800c49b7b0fdb27c19ae6c6bd7a91fa00390000000010c60000180400021c0000120300000000101800c79b7b0fb83fc19a8ad9bd7a91f000180000000010c60000180400021b0000120300000000101800c69b7b0fc536c19a52d4bd7a91f000250000000010c60000180400021b0000120300000000101800ca9b7b0fc85fc19a81dfbd7a910401030000000010c50000180400021b0000120300000000101800c89b7b0fe74ac19a86dcbd7a91fa000e0000000010c50000180400021b0000120300000000101800d29b7b0f06b7c19afbe3bd7a91a000100000000010c50000180400021c0000120300000000101800d59b7b0ff0c5c19a6beebd7a91b400410000000010c40000180400021c0000120300000000101800d49b7b0ff4c2c19af2e9bd7a91a000310000000010c40000180400021c0000120300000000101800d39b7b0f3ebcc19a79e5bd7a9196001e0000000010c40000180400021c0000120300000000101800d69b7b0f6dc7c19ae0f5bd7a91c800570000000010c40000180400021c000012030000000016b7"));
+
+ verifyPositions(decoder, binary(
+ "0100020B0025003A5701C91A003A5701CD6E68490202101700CBB4740F7617FD924364104F116A0000000000010300001EC2"),
+ position("2018-03-21 05:38:19.000", true, 51.67569, 55.59189));
+
+ verifyPositions(decoder, binary(
+ "0100020B0079000000011F6A001424951CA5CB0F23B5740F020210180023B5740F0A301994DA9C524C9128000A000000100082000011040018110300120900000003150100E803001B0700010000340900001B0700420000000000001B0700430000000000001B0700440000000000001B0700450000000000001B0700460000000000008020"));
+
+ verifyPositions(decoder, binary(
+ "0100020B00F200000001D66A001224951CA5CB0FFCB4740F0202101800FCB4740F502119943D9F524C9119805C000000100084000011040018110300120900000003150100E803001B0700410000000000001B0700420000000000001B0700430000000000001B0700440000000000001B0700450000000000001B0700460000000000006A001324951CA5CB0F05B5740F020210180005B5740F222519942D9E524C9100008B000000100083000011040018110300120900000003160100E803001B0700010000310900001B0700420000000000001B0700430000000000001B0700440000000000001B0700450000000000001B070046000000000000134E"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java
new file mode 100644
index 000000000..1c2f9492a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EnforaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EnforaProtocolDecoder decoder = new EnforaProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "000A08002020202020303131303730303030353730323637"));
+
+ verifyNull(decoder, binary(
+ "003B000502000000000820202020202030313130373030303035373032363720383A000000000D00508401358E640032B37700000367B00000A804"));
+
+ verifyPosition(decoder, binary(
+ "007100040200202020202020202020382020202020202031323334353637383930313233343520313320244750524D432C3232333135322E30302C412C333530392E3836303539342C4E2C30333332322E3734333838372C452C302E302C302E302C3032303631322C2C2C412A35320D0A"),
+ position("2012-06-02 22:31:52.000", true, 35.16434, 33.37906));
+
+ verifyPosition(decoder, binary(
+ "007600040200202020202020202020382020202020202030313138393230303036303831383920313320244750524D432C3137313834312E30302C412C333530392E3835323431302C4E2C30333332322E3735393131332C452C302E302C302E302C3137303731322C332E342C572C412A32350D0A00"));
+
+ verifyPosition(decoder, binary(
+ "006a000a081000202020202020202020333320202020202038363130373430323137313936353620204750524d432c3136313234382e30302c412c333433322e36393231312c532c30353833312e30323231372c572c302e3034382c2c3232303831342c2c2c412a3734"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java
new file mode 100644
index 000000000..b615e5062
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EsealProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EsealProtocolDecoder decoder = new EsealProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Normal,34,2017-08-31,08:14:40,15,A,25.708828N 100.372870W,10,0,Close,0.71,0:0:3:0,3.8,-73,E##"));
+
+ verifyPosition(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Startup,1,2017-08-31,02:01:19,3,V,0.000000N 0.000000E,0,0,Close,3.25,0:0:5:0,3.8,-93,E##"));
+
+ verifyNull(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Startup OK,1,180,30,30,16,1,E##"));
+
+ verifyNull(decoder, text(
+ "##S,eSeal,1000821,256,3.0.6,Startup OK,1,180,30,30,16,1,E##"));
+
+ verifyPosition(decoder, text(
+ "##S,eSeal,1000898,256,3.0.6,Normal,6,2017-09-06,23:48:39,3,V,0.000000N 0.000000E,0,0,Close,1.0,0:0:3:0,4.0,-81,E##"));
+
+ verifyNull(decoder, text(
+ "##S,eSeal,1000898,256,3.0.6,RC-NFC DEL ACK,,E##"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
new file mode 100644
index 000000000..7bf4f844e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class EsealProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ EsealProtocolEncoder encoder = new EsealProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_DISARM);
+
+ assertEquals("##S,eSeal,123456789012345,256,3.0.8,RC-Unlock,E##", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java
new file mode 100644
index 000000000..ed587e4f3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EskyFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EskyFrameDecoder decoder = new EskyFrameDecoder();
+
+ verifyFrame(
+ binary("454f3b303b3836313331313030363436313930383b523b363b3138303432303130343735313b322e39373839363b3130312e36353039313b302e37353b3332303b333339383b313b7c"),
+ decoder.decode(null, null, binary("454f3b303b3836313331313030363436313930383b523b363b3138303432303130343735313b322e39373839363b3130312e36353039313b302e37353b3332303b333339383b313b7c")));
+
+ verifyFrame(
+ binary("454c3b313b3836343930363032393139363632363b3137303832323134333432363b"),
+ decoder.decode(null, null, binary("454c3b313b3836343930363032393139363632363b3137303832323134333432363b")));
+
+ verifyFrame(
+ binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333"),
+ decoder.decode(null, null, binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333")));
+
+ verifyFrame(
+ binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333"),
+ decoder.decode(null, null, binary("454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333454f3b303b3836343930363032393139363632363b523b302b3137303830383135353335322b302e30303030302b302e30303030302b302e30302b302b3078312b302b302b302b31323333")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
new file mode 100644
index 000000000..da7df0ab2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class EskyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ EskyProtocolDecoder decoder = new EskyProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "EO;0;861311006461908;R;6;180420104751;2.97896;101.65091;0.75;320;3398;1;|"));
+
+ verifyNull(decoder, text(
+ "EL;1;864906029196626;170822143426;"));
+
+ verifyPosition(decoder, text(
+ "EO;0;864906029196626;R;7+170822143646+-26.10806+27.94600+0.40+0+0x1+0+102540+0+1242"));
+
+ verifyPosition(decoder, text(
+ "EO;0;864906029196626;R;0+170808155352+0.00000+0.00000+0.00+0+0x1+0+0+0+1233"));
+
+ verifyPosition(decoder, text(
+ "ET;1;014682000989425;R;0+171216001250+33.34405+-111.96682+0.00+0+0x1+0+25598+0+1257+0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java
new file mode 100644
index 000000000..c5b3364cf
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ExtremTracProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ExtremTracProtocolDecoder decoder = new ExtremTracProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GPRMC,862106020628733,050859.000,A,1404.8573,N,08710.9967,W,0.00,0,080117,0,,00C8,00218,99,,,,,,0.00"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,10000000001,092313.299,A,2238.8947,N,11355.2253,E,0.00,311.19,010307,0,,"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,00000000000,092244.000,A,0000.0000,S,00000.0000,E,0.00,0.00,101016,0,,8000,0"));
+
+ verifyNull(decoder, text(
+ "$GPRMC,092313.299,A,2238.8947,N,11355.2253,E,0.00,311.19,010307,0,,1111,1111"));
+
+ verifyNull(decoder, text(
+ "$GPRMC,092313.299,A,2238.8947,N,11355.2253,E,0.00,311.19,010307,0,,"));
+
+ verifyNull(decoder, text(
+ "$GPRMC,100936.000,A,0000.0000,S,00000.0000,E,0.00,0.00,101016,0,,8000,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java
new file mode 100644
index 000000000..6b39b6c17
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FifotrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FifotrackFrameDecoder decoder = new FifotrackFrameDecoder();
+
+ verifyFrame(
+ binary("24243132362c3836393436373034393239303738372c324138432c4130312c2c3139303431333135333235342c412c2d31352e3132373836382c33392e3236323530362c302c3136322c3431352c38303937323234332c302c303030302c30302c302c3634337c337c353141467c424632462c3141337c3446367c3833327c302c312c2a3534"),
+ decoder.decode(null, null, binary("24243132362c3836393436373034393239303738372c324138432c4130312c2c3139303431333135333235342c412c2d31352e3132373836382c33392e3236323530362c302c3136322c3431352c38303937323234332c302c303030302c30302c302c3634337c337c353141467c424632462c3141337c3446367c3833327c302c312c2a35340d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
new file mode 100644
index 000000000..88a460854
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class FifotrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FifotrackProtocolDecoder decoder = new FifotrackProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "$$116,869270049149999,5,A01,4,190925080127,V,-15.804260,35.061506,0,0,1198,0,0,900000C0,02,0,650|10|12C|B24,18B|4C8|72,1,*01"));
+
+ verifyAttribute(decoder, buffer(
+ "$$123,869467049296388,B996,A01,2,190624131813,V,22.333746,113.590670,0,124,-1,26347,0,0004,00,0,460|0|2694|5A5D,174|0|0|0,B48CEB,*77"),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyAttribute(decoder, buffer(
+ "$$125,869467049296388,548,A01,38,190619025856,A,22.333905,113.590261,0,12,60,16666,0,0000,00,0,460|0|2694|13F8,1A2|4C1|0|0,B4A067,*7A"),
+ Position.KEY_DRIVER_UNIQUE_ID, "11837543");
+
+ verifyNull(decoder, buffer(
+ "$$79,868345037864709,382,D05,190220085833,22.643210,114.018176,1,1,1,13152,23FFD339*25"));
+
+ verifyNull(decoder, binary(
+ "2424313036332c3836383334353033373836343730392c312c4430362c32343434424438362c302c313032342cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00b0148a705c8cd00479e6917ef7b5003c9ec29b90bf5a00457c366a4620806801921f41da999cf02801ebf4a4e73cf14002b153f2d4a5cb0c8506802261cf4a50b8a007053d718a4c1cf340099c526ecd007fffd07f6a55c86140126e19e69acdcd0037a9a4a002909a004eb4e030334001a4ce280141cd2138a004ed4982074e6800ed49de801698793401ffd18cf4a65002af5a4ce1a8026cf14d278a008f760d20ebf350031cf6149183bb8a009c03de901f9a801c0e78a31400b9c518a004c5140094b8a00fffd28b1462800c518a00414b400b8a00e68016814001a2800a5eb40062908cd002628a0028a00fffd3998e4734b1b7c981400c3d79a7829b7ef73e98a0069f6a4c50034a926a551b47340037a1e4d424734012c43820529001e72680060bfc34a1f6f02800618e6a3c9cd003c336304d0091d680187ad211401fffd47f34a48079a0091946327d2a173e9400a290d002f6a4c7ad00205cf4a7f3b680131c52639a00304521140098a42c68010138a00e28014034d391401fffd58c9a69e6801a3341a004dc69439140085b3da909cd001b69369cf14013019148cb40028229dcd0014b4005142a3739"));
+
+ verifyPosition(decoder, buffer(
+ "$$105,866104023179743,AB,A00,,161007085534,A,54.738791,25.271918,0,350,151,0,17929,0000,0,,246|1|65|96DB,936|0*0B"));
+
+ verifyPosition(decoder, buffer(
+ "$$103,866104023179743,5,A00,,161006192841,A,54.738791,25.271918,0,342,200,0,4265,0000,0,,246|1|65|96DB,9C4|0*75"));
+
+ verifyPosition(decoder, buffer(
+ "$$103,866104023179743,4,A00,,161006192810,V,54.738791,25.271918,0,158,122,0,4235,0000,0,,246|1|65|96DB,9C5|0*69"));
+
+ verifyPosition(decoder, buffer(
+ "$$135,866104023192332,29,A01,,160606093046,A,22.546430,114.079730,0,186,181,0,415322,0000,02,2,460|0|27B3|EA7,A2F|3B9|3|0,940C7E,31.76|30.98*46"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java
new file mode 100644
index 000000000..b60313956
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class FifotrackProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ FifotrackProtocolEncoder encoder = new FifotrackProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REQUEST_PHOTO);
+
+ assertEquals("##24,123456789012345,1,D05,3*9F\r\n", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java
new file mode 100644
index 000000000..9e5a45a83
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FlespiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FlespiProtocolDecoder decoder = new FlespiProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("[{\"position.speed\":0,\"position.latitude\":53.90573,\"time.valid.status\":true,\"timestamp\":1506956075,\"position.satellites\":10,\"message.buffered.status\":false,\"business.mode.status\":true,\"gps.status\":true,\"position.longitude\":27.455848,\"position.direction\":0,\"ident\":\"605630\"},{\"siren.status\":false,\"business.mode.status\":true,\"position.satellites\":8,\"timestamp\":1506695785,\"led.status\":false,\"position.latitude\":53.905569,\"position.longitude\":27.455986,\"position.speed\":0,\"gradual.stop.status\":false,\"position.direction\":262.643854,\"hardware.version.enum\":223,\"vehicle.mileage\":160,\"message.buffered.status\":false,\"blinkers.status\":false,\"ident\":\"605630\",\"position.altitude\":233.48,\"immobilizer.status\":false}]")));
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("[{\"geofence.inside.status\":false,\"position.valid\":false,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":10.11223,\"refrigerator.sensor.temperature#1\":62.5,\"gnss.antenna.cut.status\":true,\"din\":3,\"ain#3\":0,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":0,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":66.7,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":0,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":68.2,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":68.9,\"ident\":\"605630\",\"timestamp\":946684840,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8165,\"external.powersource.voltage\":15.298,\"gnss.enum\":\"glonass\",\"position.longitude\":20.88774,\"battery.voltage\":4.088,\"refrigerator.sensor.temperature#5\":71.3,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false},{\"geofence.inside.status\":false,\"position.valid\":true,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":57.986744,\"refrigerator.sensor.temperature#1\":74.1,\"gnss.antenna.cut.status\":false,\"ain#3\":0,\"position.hdop\":21.1,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":219,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":70.5,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":5,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":71.3,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":69.3,\"ident\":\"605630\",\"timestamp\":1392272112,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8174,\"external.powersource.voltage\":15.303,\"gnss.enum\":\"glonass\",\"position.longitude\":56.207576,\"battery.voltage\":3.934,\"refrigerator.sensor.temperature#5\":68.1,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false}]")));
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("[{\"ain#1\":1,\"ain#2\":0,\"ain#3\":0,\"ain#4\":0,\"alarm.event.trigger\":true,\"custom.SOS\":1,\"custom.dparam\":3.141593,\"custom.ign\":1,\"custom.iparam\":-55,\"custom.tparam\":\"lorem\",\"din\":722,\"dout\":1048576,\"ident\":\"namo:namo\",\"position.altitude\":300,\"position.direction\":0,\"position.hdop\":1.1,\"position.latitude\":53.90821,\"position.longitude\":27.524165,\"position.satellites\":7,\"position.speed\":0,\"timestamp\":1508508510.013227}]")));
+ }
+
+} \ No newline at end of file
diff --git a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java
new file mode 100644
index 000000000..28ecaf646
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FlexCommProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FlexCommProtocolDecoder decoder = new FlexCommProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "7E00865067022408382201705302358271024932258006712785200700022601010224100040002C5002A2210001000000010012342107"));
+
+ verifyPosition(decoder, text(
+ "7E27865067022408382201705241211301024932197006712794000910022481008234100040002C5002A2200011000000006306941827"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java
new file mode 100644
index 000000000..3f268fde9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class FlextrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FlextrackProtocolDecoder decoder = new FlextrackProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "-1,LOGON,7000000123,8945000000"));
+
+ verifyNull(decoder, text(
+ "-1,LOGON,1080424008,8945020110126633198"));
+
+ verifyPosition(decoder, text(
+ "-2,UNITSTAT,20060101,123442,1080424008,N0.00.0000,E0.00.0000,0,0,0,4129,-61,2,23866,0,999,A214,63,2EE2,3471676"));
+
+ verifyPosition(decoder, text(
+ "-2,UNITSTAT,20050205,181923,7000004634,N55.46.0812,E009.21.1665,122,198,6,3934,-81,01A8,23802,213,55,37FD,45,0055,12878"),
+ position("2005-02-05 18:19:23.000", true, 55.76802, 9.35278));
+
+ verifyPosition(decoder, text(
+ "-2,UNITSTAT,20050205,181923,7000004634,N55.46.0812,E009.21.1665,122,198,6,3934,-81,01A8,23802,213,55,37FD,45,0055,12878"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java
new file mode 100644
index 000000000..837b36b64
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FoxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FoxProtocolDecoder decoder = new FoxProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"10\" data=\"51,A,010416,085317,4444.4158,N,02025.4466,E,1,182,,1110111111110111 141 0 0 0 0 0 10010000 10142,018C81851800009B\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"1092,V,010101,000004,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,47664\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"31,V,110316,125952,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,65 60 60 60 60 60 60 60 65 65 55 55 60 60 60 60 60 60 60 60 55 55 60 55 65 60 60 60 60 60 60 55\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"0,V,110316,130154,0000.0000,N,00000.0000,E,0,0,,1111111111111111 123 0 0 0 0 0 00000000 47664,47664 0\"/></fox>"));
+
+ verifyPosition(decoder, text(
+ "<fox><gps id=\"90\" data=\"0,A,110316,131834,4448.8355,N,02028.2021,E,0,217,,1111111111111111 123 0 0 0 0 0 00000000 50020,50020 0\"/></fox>"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java
new file mode 100644
index 000000000..b2fc1fd8d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FreedomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FreedomProtocolDecoder decoder = new FreedomProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "IMEI,353358011714362,2014/05/22, 20:49:32, N, Lat:4725.9624, E, Lon:01912.5483, Spd:5.05"),
+ position("2014-05-22 20:49:32.000", true, 47.43271, 19.20914));
+
+ verifyPosition(decoder, text(
+ "IMEI,353358011714362,2014/05/22, 20:49:32, N, Lat:4725.9624, E, Lon:01912.5483, Spd:5.05"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java
new file mode 100644
index 000000000..a84c4e357
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FreematicsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FreematicsProtocolDecoder decoder = new FreematicsProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "1#EV=2,TS=1871902,ID=ESP32305C06C40A24*AC"));
+
+ verifyNull(decoder, text(
+ "0#EV=1,TS=23930,ID=ID1C6606C40A24,SK=TEST_SERVER_KEY*49"));
+
+ verifyPositions(decoder, text(
+ "1#0:102560,20:0;0;0,24:425,10:4285580,A:-35.803696,B:175.748413,C:0.22,D:0.41,F:5,0:103174,20:0;0;0,24:423,10:4285660,A:-35.803696,B:175.748413,C:0.22,D:0.41,F:5,30:88193792*21"));
+
+ verifyPositions(decoder, text(
+ "1#0:49244,20:0;0;0,24:423,0:50779,20:0;0;0,24:425,30:32924444*38"));
+
+ verifyNotNull(decoder, text(
+ "1#0:47607,20:0;0;0,24:423,0:48732,20:0;0;0,24:428,10:4280140,A:0.000000,B:0.000000,C:0.00,D:18520000.00,F:2,30:32924444*BA"));
+
+ verifyPositions(decoder, text(
+ "1#0:68338,10D:79,30:1010,105:199,10C:4375,104:56,111:62,20:0;-1;95,10:6454200,A:-32.727482,B:150.150301,C:159,D:0,F:5,24:1250*7A"));
+
+ verifyPositions(decoder, text(
+ "1#0=68338,10D=79,30=1010,105=199,10C=4375,104=56,111=62,20=0;-1;95,10=6454200,A=-32.727482,B=150.150301,C=159,D=0,F=5,24=1250*7A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java
new file mode 100644
index 000000000..4f4972895
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class GalileoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GalileoFrameDecoder decoder = new GalileoFrameDecoder();
+
+ assertEquals(
+ binary("011780011102e603383633353931303238393630323437043200801c"),
+ decoder.decode(null, null, binary("011780011102e603383633353931303238393630323437043200801c")));
+
+ assertEquals(
+ binary("01d48304320010020520a5829f58300f50dc8a024c0965013300000000344102350740003a41e14b426610431b4459fa672a4500004601a050364c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e293000000043200100105202d829f58300f50dc8a024c0965013300000000344102350740003a41d04b426110431b445702882a4500004601a050374c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29400000004320010000520b5819f58300f50dc8a024c0965013300000000344102350740003a419e4b426a10431c4456fab72a4500004601a050434c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010ff04203d819f58300f50dc8a024c0965013300000000344102350740003a41874b426310431c4454fe572a4500004601a050334c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29600000004320010fe0420c5809f58300f50dc8a024c0965013300000000344102350840003a41a24b426710431c4457fea72a4500004601a050214c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fd04204d809f58300f50dc8a024c0965013300000000344102350840003a41a34b426310431c4455f6772a4500004601a0502e4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29900000004320010fc0420d57f9f58300f50dc8a024c0965013300000000344102350840003a41bd4b426510431d4458fe672a4500004601a0501f4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fb04205d7f9f58300f50dc8a024c0965013300000000344102350840003a41b54b426310431d4456fa772a4500004601a0502d4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010fa0420e57e9f58300f50dc8a024c0965013300000000344102350840003a41b24b426210431e4454fa872a4500004601a050fe4b510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29000000004320010f904206d7e9f58300f50dc8a024c0965013300000000344102350a40003a41af4b426710431f4458fea72a4500004601a0500a4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e28900000067c5"),
+ decoder.decode(null, null, binary("01d48304320010020520a5829f58300f50dc8a024c0965013300000000344102350740003a41e14b426610431b4459fa672a4500004601a050364c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e293000000043200100105202d829f58300f50dc8a024c0965013300000000344102350740003a41d04b426110431b445702882a4500004601a050374c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29400000004320010000520b5819f58300f50dc8a024c0965013300000000344102350740003a419e4b426a10431c4456fab72a4500004601a050434c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010ff04203d819f58300f50dc8a024c0965013300000000344102350740003a41874b426310431c4454fe572a4500004601a050334c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29600000004320010fe0420c5809f58300f50dc8a024c0965013300000000344102350840003a41a24b426710431c4457fea72a4500004601a050214c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fd04204d809f58300f50dc8a024c0965013300000000344102350840003a41a34b426310431c4455f6772a4500004601a0502e4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29900000004320010fc0420d57f9f58300f50dc8a024c0965013300000000344102350840003a41bd4b426510431d4458fe672a4500004601a0501f4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fb04205d7f9f58300f50dc8a024c0965013300000000344102350840003a41b54b426310431d4456fa772a4500004601a0502d4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010fa0420e57e9f58300f50dc8a024c0965013300000000344102350840003a41b24b426210431e4454fa872a4500004601a050fe4b510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29000000004320010f904206d7e9f58300f50dc8a024c0965013300000000344102350a40003a41af4b426710431f4458fea72a4500004601a0500a4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e28900000067c5")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java
new file mode 100644
index 000000000..74612caab
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GalileoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GalileoProtocolDecoder decoder = new GalileoProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "011801018202130338363833343530333230343234323604640010a406207caa9f5b300c830a7901ca0ec802330000000034b802350540003e41703f422b1043234504004600e09000000000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d4d3140000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000f400000000f500000000f600000000f700000000f800000000f9000000008960"));
+
+ verifyPositions(decoder, binary(
+ "017583018202120338363833343530333230363635373304520010384520c850975b300cc03a910107cbf9023365000607341300350640012a41236a4215104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4993b0500d64100d70000d8be02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000018202120338363833343530333230363635373304520010394520c950975b300cab3a91010ecbf902336000be06341300350640012a41266a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d49b3b0500d64100d70000d8bc02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103a4520ca50975b300c953a910113cbf9023358008f06341300350640012a41206a4215104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d49e3b0500d64100d70000d8ba02d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103b45204251975b300c6d3a91011dcbf9023300008a06341300350640013a41726a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4a33b0500d64800d70000d80003d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f3000000000182021203383638333435303332303636353733045200103c4520bb51975b300c6d3a91011dcbf9023300008a06341300350640013a41816a4216104329450400460020500000510000520000530000540000550000c000000000c100000000c44bc500c6ffc700c800c900ca00cb00d4a33b0500d64800d70000d80003d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000e007"));
+
+ verifyNull(decoder, binary(
+ "07e10300ffd8ffe000114a464946000101010000000000000affe1011445786966000049492a000800000005000e010200140000004a0000000f0102000a0000005e000000100102000a0000006800000032010200130000007200000025880400010000008600000000000000494d45492033353336313230383730353035393247616c696c656f536b7971717a6d202020202020323031383a30383a30392030363a35393a303100060001000200020000004e0000000200050003000000d40000000300020002000000450000000400050003000000ec000000050001000100000000000000060005000100000004010000000000008e7f930240420f0000000000010000000000000001000000a11aaa0140420f00000000000100000000000000010000003300000001000000ffdb0084000d09090b09080d0b0a0b0e0d0d0f131f1413111113261b1d171f2d28302f2d282c2b3238483d323544362b2c3f553f444a4d505150303c585f584e5e484f504d010d0e0e131013251414254d342c344d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4dffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00f41237526768c119a0811803c8e29a7eb40075151ba9e2801436060d0df779ef408685c50092db6801e63c9c83834c772a3140c818163522924fa5310367b9a8f3ce2806382734e2bc7340c68031c52ab1e86811ffd0edcf0335170fd7b5333023151c83d4ba"));
+
+ verifyPositions(decoder, false, binary(
+ "01560003383636303530303338343337353836044701e000000000e13c494e414c4c3a696e303d31313230362c696e313d302c696e323d302c696e333d302c696e343d302c696e353d302c4163633d3536363932343732353bfdef"));
+
+ verifyPositions(decoder, false, binary(
+ "012a0003383633353931303233353137333732046600e000000000e1104f555428332e2e3029203d2031313130bb29"));
+
+ verifyPositions(decoder, binary(
+ "0144030338363832303430303132363939333404320010ee0f20f5a86c57300570172f03bc7dfd023363002604343e00351c40092a414a6842af0e432445000046030050246b51666a524c055300000338363832303430303132363939333404320010ed0f20f4a86c57300570172f03b47dfd023363000d05343e00351140090a41c56742a60e432445000046030050b56a514f6a521b045300000338363832303430303132363939333404320010ec0f20e6a86c57300b34172f03287efd023300000000344900350d40290a41562742030b43234500004603205023455190445295005300000338363832303430303132363939333404320010eb0f20e4a86c57300b34172f03287efd023300000000344900350d40290b41000042bd0b432345000046032050dc31518c315200005300000338363832303430303132363939333404320010ea0f20c7a86c57300b34172f03287efd023300000000344900350d40a90b41000042050d43234500004600205000005100005200005300000338363832303430303132363939333404320010e90f204fa86c57300b34172f03287efd023300000000344900350d40a90b41000042ff0c43244500004600205000005100005200005300000338363832303430303132363939333404320010e80f20d7a76c57300b34172f03287efd023300000000344900350d40a90b41000042fd0c43244500004600205000005100005200005300000338363832303430303132363939333404320010e70f205fa76c57300b34172f03287efd023300000000344900350d40a90b41000042fd0c43254500004600205000005100005200005300000338363832303430303132363939333404320010e60f20e7a66c57300b34172f03287efd023300000000344900350d40a90b41000042fd0c43264500004600205000005100005200005300000338363832303430303132363939333404320010e50f206fa66c57300468172f03907cfd023300007a0a343600352b40a90b41000042030d43274500004600205000005100005200005300000338363832303430303132363939333404320010e40f2051a66c5730048c172f03ac7cfd02335300980a341600352b40a12b41000042040d43274500004600e0500000510000520000530000abde"));
+
+ verifyNull(decoder, binary(
+ "011380033836383230343030313534393038370432008590"));
+
+ verifyPositions(decoder, binary(
+ "01cf030446ba10630320a7054c533008f86c8e0310062c043347049e02344000350940013241506b428f10432244aeea572045f9004604a0500000510000529a6b5300000446ba10712420ce1c4b533009b4f06703043df4033381037b0a343800350a40093241db6b428f10432544c05ef81f45f9004604a050000051000052886b5300000446ba10702420c11c4b53300a54f16703c450f403336e034e0a343900350840093241dd6b428f1043254491eaf71f45f9004604a050000051000052c26b5300000446ba106f2420b31c4b53300cecf267033865f403336a03300a343800350740093241e66b429010432544b446582045f9004604a050000051000052f76b5300000446ba106e2420a61c4b53300c9cf467038878f403337b03370a343800350740093241b56b428f10432544ba46f81f45f9004604a050000051000052c66b5300000446ba106d2420991c4b53300bc8f56703508cf403338d036e0a343700350840093241d66b428f10432544b4ea572045f9004604a050000051000052846b5300000446ba106c24208c1c4b533008c8f5670370a0f403338703920a343a00350e40093241c76b428f10432544c0fef71f45f9004604a0500000510000528d6b5300000446ba106b24207f1c4b533009a4f5670338b4f403337603920a343c00350a40093241d06b428f104325449146a81f45f9004604a0500000510000528a6b5300000446ba106a2420721c4b53300b9cf56703ecc7f403337103810a343a00350840093241ca6b428f10432544d12e582045f9004604a050000051000052996b5300000446ba10692420651c4b53300a64f6670358dbf403337a03490a343900350840093241e56b429010432544aed2f71f45f9004604a050000051000052b26b5300000446ba10682420581c4b5330094cf86703e0eef4033381030c0a343a00350940093241f96b428f10432544cb2e182145f9004604a050000051000052926b5300000446ba106724204b1c4b533009f8fa67032802f503337b03fc09343b00350a40093241d86b428f10432544c0ea772145f9004604a0500000510000529e6b5300000446ba106624203e1c4b533009a0fd67036815f503338403fd09343c00350a40093241a86b428f10432544ae2e582045f9004604a050000051000052a86b5300000446ba10652420311c4b53300944006803b028f503338003ff09343d00350940093241dc6b428e10432544a8fea71f45f9004604a050000051000052e26b5300000446ba10642420241c4b533008f0026803083cf503338b03f909343c00350d40093241d36b428f10432544c0eaa71f45f9004604a050000051000052ab6b530000ff3f"));
+
+ verifyPositions(decoder, binary(
+ "011e8304320010270220dbd2f051300a90cf740328ac59033300000000347600351240012a41e92e42500f431f440006c814450f00460020500000510000520000530000540000550000560000570000580000600000610000620000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d471020000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f30000000004320010260220bdd2f051300590cf740328ac59033300000000347600351440090a41f02e427b0f431f44ff0db814450f00460000500000510000520000530000540000550000560000570000580000600000610000620000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d471020000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000043200102502208ed2f051300ed8d0740304ac5903330000000034a500350a40012a41ec2e422d0f431f440016b814450f00460020500000510000520000530000540000550000560000570000580000600000610000620000a000a100a200a300a400a500a600a700a800a900aa00ab00ac00ad00ae00af00b00000b10000b20000b30000b40000b50000b60000b70000b80000b90000c000000000c100000000c200000000c300000000c400c500c600c700c800c900ca00cb00cc00cd00ce00cf00d000d100d200d44d020000d60000d70000d80000d90000da0000db00000000dc00000000dd00000000de00000000df00000000f000000000f100000000f200000000f300000000622e"));
+
+ verifyPositions(decoder, binary(
+ "01d48304320010020520a5829f58300f50dc8a024c0965013300000000344102350740003a41e14b426610431b4459fa672a4500004601a050364c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e293000000043200100105202d829f58300f50dc8a024c0965013300000000344102350740003a41d04b426110431b445702882a4500004601a050374c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29400000004320010000520b5819f58300f50dc8a024c0965013300000000344102350740003a419e4b426a10431c4456fab72a4500004601a050434c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010ff04203d819f58300f50dc8a024c0965013300000000344102350740003a41874b426310431c4454fe572a4500004601a050334c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29600000004320010fe0420c5809f58300f50dc8a024c0965013300000000344102350840003a41a24b426710431c4457fea72a4500004601a050214c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fd04204d809f58300f50dc8a024c0965013300000000344102350840003a41a34b426310431c4455f6772a4500004601a0502e4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29900000004320010fc0420d57f9f58300f50dc8a024c0965013300000000344102350840003a41bd4b426510431d4458fe672a4500004601a0501f4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29700000004320010fb04205d7f9f58300f50dc8a024c0965013300000000344102350840003a41b54b426310431d4456fa772a4500004601a0502d4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29500000004320010fa0420e57e9f58300f50dc8a024c0965013300000000344102350840003a41b24b426210431e4454fa872a4500004601a050fe4b510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e29000000004320010f904206d7e9f58300f50dc8a024c0965013300000000344102350a40003a41af4b426710431f4458fea72a4500004601a0500a4c510000520000530000c000000000c100000000c200000000c300000000d80000dd00000000e28900000067c5"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java
new file mode 100644
index 000000000..6a68752bb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class GalileoProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ GalileoProtocolEncoder encoder = new GalileoProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "status");
+
+ verifyCommand(encoder, command, binary("01200003313233343536373839303132333435040000e000000000e1067374617475731f64"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
new file mode 100644
index 000000000..7e1ccb79d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
@@ -0,0 +1,53 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class GatorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeId() {
+
+ assertEquals("3512345006", GatorProtocolDecoder.decodeId(12, 162, 50, 134));
+
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GatorProtocolDecoder decoder = new GatorProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "242480002600341cad190917022021812497260280594200000000c047010000135400009bb600ff00b90d"));
+
+ verifyAttributes(decoder, binary(
+ "2424800026364101b31608041108380273453415301532000000008000010000122800000124000000c40d"));
+
+ verifyNull(decoder, binary(
+ "242421000658e3d851150d"));
+
+ verifyAttributes(decoder, binary(
+ "242480002658e3d851a60101c662bc00000000000000000000000000470007a30b0c00b10fc900ff00460d"));
+
+ verifyNull(decoder, binary(
+ "242421000643e30282070d"));
+
+ verifyPosition(decoder, binary(
+ "24248000260009632d141121072702059226180104367500000000c04700079c0c34000ad80b00ff000a0d"),
+ position("2014-11-21 07:27:02.000", true, 59.37697, 10.72792));
+
+ verifyPosition(decoder, binary(
+ "24248100230CA23285100306145907022346901135294700000000C04001012C0E1100000021CB0D"));
+
+ verifyPosition(decoder, binary(
+ "2424800023c2631e00111220104909833268648703804100000000c0470000000b4e00000000550d"),
+ position("2011-12-20 10:49:09.000", true, -33.44773, -70.63402));
+
+ verifyPosition(decoder, binary(
+ "24248000260009632d141121072702059226180104367500000000c04700079c0c34000ad80b00ff000a0d"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java
new file mode 100644
index 000000000..373c8c49e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GenxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GenxProtocolDecoder decoder = new GenxProtocolDecoder(null);
+
+ decoder.setReportColumns("28,2,3,4,13,17,10,23,27,11,7,8,46,56,59,70,74,75,77,89,90,93,99,107,112,113,114,176,175,178,181,182");
+
+ verifyPosition(decoder, text(
+ "000036004133,11/05/2017 00:03:45,45.54767,-73.75547,0,0,63,569.35,118,ON,10687,0,12,O,9,3669.000,95.0,0.0,1,107.9464,0.0065,583.752,43,0.00,28.26,7.60,NA,U,UUU,0,-95.0,U"));
+
+ decoder.setReportColumns("1,2,3,4");
+
+ verifyPosition(decoder, text(
+ "000036004130,08/31/2017 17:24:13,45.47275,-73.65491,5,19,117,1.14,147,ON,1462,0,6,N,0,0.000,-95.0,-1.0,0,0.0000,0.0000,0.000,0,0.00,0.00,0.00,NA,U,UUU,0,-95.0,U"));
+
+ verifyPosition(decoder, text(
+ "000036004130,08/31/2017 17:24:37,45.47257,-73.65506,3,0,117,1.14,124,ON,1489,0,5,N,0,0.000,-95.0,-1.0,0,0.0000,0.0000,0.000,0,0.00,0.00,0.00,NA,U,UUU,0,-95.0,U"));
+
+ decoder.setReportColumns("1,2,3,4,13,17,10,23,27,11,7,8,46,56,59,70,74,75,77,89,90,93,99,107,112,113,114,176,175,178,181,182");
+
+ verifyPosition(decoder, text(
+ "000036035855,04/16/2017 21:19:07,45.46485,-73.65424,24,32,61:213,342.51,157,ON,20984,0,12,O,18,0.000,95.0,24.0,1990,64.0894,0.0219,316.009,71,0.00,16.78,5.10,NA,U,UUU,0,-95.0,U"));
+
+ verifyPosition(decoder, text(
+ "000036004129,10/20/2017 00:54:27,43.44638,-79.68616,36,310,6,4954.40,321,ON,35377,0,12,O,13,0.000,85.6,36.0,1573,451.2514,0.0012,5260.953,0,0.00,122.48,33.17,NA,U,UUU,0,-95.0,U"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java
new file mode 100644
index 000000000..ffafcd7b1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java
@@ -0,0 +1,56 @@
+package org.traccar.protocol;
+
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gl100ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl100ProtocolDecoder decoder = new Gl100ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "+RESP:GTLGL,359464030492644,1,2,1,0,0.4,0,299.7,1,5.455551,51.449776,20160311083229,0204,0016,03EC,BD94,00,0036,0102090501"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359464030439249,1,0,61,1,0.0,346,-2.7,2,-80.392825,26.122424,20151214000354,0310,0260,72BC,35F5,00,04B6,0102070407"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,135790246811220,1,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102070202"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,135790246811220,2,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,1,-3.6,145,30.0,2,121.354442,31.221940,20090101000100,0460,0000,18d8,6141,00,11F0,0102070202"));
+
+ verifyNull(decoder, text(
+ "AT+GTHBD=HeartBeat,359231030000010,20090101000000,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTSOS,359231030000010,0,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"),
+ position("2009-01-01 00:00:00.000", false, 31.22207, 121.35434));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTRTL,359231030000010,0,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTEST,359231030000010,0,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTSZI,359231030000010,0,3,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTLBC,359231030000010,02132523415,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359231030000010,1,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359231030000010,2,0,0,1,4.3,92,70.0,1,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,0,0,1,-3.6,145,30.0,2,121.354442,31.221940,20090101000100,0460,0000,18d8,6141,00,11F0,0102120204"));
+
+ verifyPosition(decoder, text(
+ "+RESP:GTTRI,359464030073766,1,0,0,0,1.7,254,-27.8,3,30.474475,50.488383,20131107155511,0255,0003,6995,4761,00,0071,0103090402"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java
new file mode 100644
index 000000000..e6fb98340
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gl200BinaryProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl200BinaryProtocolDecoder decoder = new Gl200BinaryProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "2b4556542d00fc1fbf0063450102020956325403000343056437f8220700000200000000010000160100f2007eff75a1f0025c6b1a07e1080108241a02680003189c1ac500000000000002100800000000000000000007e1080108241a19e24e4e0d0a"));
+
+ verifyPositions(decoder, binary(
+ "2b5253506400fc1fbf058e450102020956325403000343056438ed2205010e61c6f0ff75a1b4025c6af959803d8ba07ffe17dea03f7e1fdda0007df7dfa03e7e3fd0a0befdf7cea001fddfd8a000fdefdca042fd9fe1a0427d6fe9a0017db7dca0407d47e7a0027d67e5bfc0fd77eca03ffd8fe4bfff7dcfddbffd7dbfdebffdfddfe2bfbe7e0fe1bf7f7e67e2bf7bfed7e2bf7c7f5fe2bffbffc7e3a12880a7daa0b9013fe3a0f801b7dfa0bd81efe1a03f8207e0a03e8217e4a07e023fe9a0bd824feca03a02affda07b02d004a07f02e007a00002d808a041830001a003834fefa00402b7eebf8382a7ebbfc28267e9bf81821fe3bf0181d7e3bf01016fe9bf010117edbf4080c7f6bf7f8087fabfbf805ff9a0fa8097fca23401300aa0b4016019a13a817026a13b81883ea0be81a83fa0bd81b03ba00101d83abfc0039874bfc081b835bfbf819834bfc081982fa01004702ea00502500da002827802bfc0825fffbf41821fffbf4081d801bf3f816802bec180fffebec20077fdbf80002801bfc0000800a000000800e0e0a202804ffba14a8127eea0460107e4a0cc809fd9a0c4004fcda0c2004fcaa080007fbfa0410067bebfc100c7b6a03f8037c1bfbf004fc6a03f0057c6a0410027c5a081001fbaa0418017baa001001fb8a0007fe7bca000ffdfb7a0817fc7b7a040ffbfb3a0407fb7b4a0407fb7b1bf807fbfb3a0007fb7b5a0007fb7b1a0007fb7b2a0007fbfb3a0407fafaba000ffb7ada0017f97aba040ff7faca001ff77b6bf3fff67b3bf007f87bea082ff47b4bfc27f17c1bfffff3fc2bebdff9fcabe3effbfe0bf3cff47e9a03c002ff0a1740097e9a1f8813fe1a12f01f7fca0fa028ff8a07f02a7fea041829007a00302bff8bf810287f2a0080257e1a0050207dbbfc481cfd3a044819fcda043015fc3a043810fc2a0c680a7b2a0448027b0a0857fa7aea0c37f67a2a0017ee7a7a0407f0f9fa000ff079fa03ffeffa1a03ffeffa2a07fff17a0a03fff1fa0a03fff2fa2a03fff3fa0a07fff47a2a0007f579fa03fff4f9da03fff679ca0007f679ea000ff4f9ca07fff5f9ca0007f579cbfc07f5f9fbf407f6fa6bf807f6fabbfc07f7fadbf807f87b2a0407f87b0a0407f77aba000ff77afbfc07f77aea03fff7fada07fff7faca000ff7fada0007f77abbfc0ff77b0a000ff7faea042ff2faba0037ee7ada0437e57a1a0037e27abbfc1fdf7bcbf827defc5bf01fe0fcebf017e3fd5bfc17e2fdca0ff7e27d7a13cfe27cba0bd7e0fa7a07d7e6fb9a07d7eb7b3a07efedfaea03ffef7afa0c07eefa7a07f7f07a3a03fff0fa3a03f7f27a2a03f7f37a4bfff7f6fa3bfff7f5fa3a07eff979fa03f7fbfa2a03f7fdfa1bfbf8017a5bfbf0037adbeff004fb8bfbf004fc1beff804fcbbf7f8047d5bf408027debf408007e8a03e804fdebf7e8027f2bf00ffefffa0400017efa0418017f1a041002feca0410017edbf007fbffea0007fdff6a1018027e4bf81ffc7f6a1008017dca0c10087bea0018097baa083ffc7cda0837fafc7a102ff0fc8a0c27effb9a0c2fe87c1a143fbf78ea07f7dcfc0a0bd7dffb3a03d7e47b1a03ffe77afa07ffe77ada0007e77aebfc07e77afbfc07e8faea03ffea7afbfbf7ecfb4bfbcfeffbbbf7bff8fb8bf7d0027bebffb809fc7bffa00ffc9bf78813fd9a03d8197e1a03b81d7e6a07e01efdda00081bfdda00101bfdba03f81a7dfbfc001afdbbfbd81a7f1bfbe0187eebf80814feea0028127e7a081813fe2a0010147e6a03e8147f4a0408167eca040817fe7a0018157e4bfc8011fdaa08b002fd1a009ffa7d5a009fe57f6a04b7df7f4a0097e07eca0027df7edbf807e2feca000fe3feca07ffe37f0a0407e0ff7a040fde7f0a0007de7eea000fdcff4a001fddffaa000fdcff8a0027dcffda07f7dbff3a03f7dc7f402680003189c355300000109000002120700000000000000000007e1080108290019e63b5c0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "2B5253500300FC1FFF0064450102020867623130302D446F642F442105007018217345005F010100000001100045073C4D4101DB86BD07E106130B2B0F0460000018770013000000030000000106020F2300002714301107E106130B2B1003424EFB0D0A"));
+
+ verifyPositions(decoder, binary(
+ "2b5253500700fc1fbf005d4501020209563254030003430564377e42071001000000000000007eff75a151025c6a8107e10801081a2a02680003189c1ac500000000000002100700000000000000000007e1080108241019e17ebe0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "2b494e4601fd7f0076676231303000000045010202090104020500004100054007e107150b061d0000003f010e02580000000000d0312a1013648935103226313921591f1200000000000302680003189c1ac3001b02680003189c1ac4000d02680003189c1ac5001207e107150b0d3704f658060d0a"));
+
+ verifyPosition(decoder, binary(
+ "2b4556540c00fc1fbf005c4501010108563254030003430564312a41090100000000003f007dff75a11a025c6a7807e1070a14041202680003189c1ac500000000000000000000000000000000000007e1070b041134054e5c6e0d0a"));
+
+ verifyNull(decoder, binary(
+ "2b41434b017f244501010108676231303000000000ffff07e1070b03112d054dfe030d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java
new file mode 100644
index 000000000..e90c6495a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class Gl200FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl200FrameDecoder decoder = new Gl200FrameDecoder();
+
+ assertEquals(
+ binary("2b41434b017f244501010108676231303000000000ffff07e1070b03112d054dfe030d0a"),
+ decoder.decode(null, null, binary("2b41434b017f244501010108676231303000000000ffff07e1070b03112d054dfe030d0a")));
+
+ assertEquals(
+ binary("2b4556540c00fc1fbf005c4501010108563254030003430564312a41090100000000003f007dff75a11a025c6a7807e1070a14041202680003189c1ac500000000000000000000000000000000000007e1070b041134054e5c6e0d0a"),
+ decoder.decode(null, null, binary("2b4556540c00fc1fbf005c4501010108563254030003430564312a41090100000000003f007dff75a11a025c6a7807e1070a14041202680003189c1ac500000000000000000000000000000000000007e1070b041134054e5c6e0d0a")));
+
+ assertEquals(
+ binary("2b524553503a47545354522c3430303330302c3836323336353033303134323238392c474c3530302c302c302c302c33392e342c39332c312c302e332c31372c3130352e382c32352e3934343234302c34342e3430333733362c32303137303532393134303533302c303232362c303030312c353643322c373038342c2c2c2c32303137303532393136303533302c30324441"),
+ decoder.decode(null, null, binary("2b524553503a47545354522c3430303330302c3836323336353033303134323238392c474c3530302c302c302c302c33392e342c39332c312c302e332c31372c3130352e382c32352e3934343234302c34342e3430333733362c32303137303532393134303533302c303232362c303030312c353643322c373038342c2c2c2c32303137303532393136303533302c3032444124")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
new file mode 100644
index 000000000..12e64cd6d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -0,0 +1,404 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Gl200TextProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gl200TextProtocolDecoder decoder = new Gl200TextProtocolDecoder(null);
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,423031,355154083021002,Bolt4G,0,0,0,0,1,1.0,0.2,0,245.3,-85.630193,42.975280,20190729185934,310,410,500b,B0E320F,31,-1,100,20190729185934,0010$"));
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTCTN,440200,866427030007379,NOKIA3,0,0,2,,9,1,0.1,174,48.7,-1.061812,51.435270,20190717080549,0234,0015,0025,145A,,,0000,20190717081008,1D3B$"),
+ Position.KEY_BATTERY_LEVEL, 9);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTHBM,4B0101,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"),
+ Position.KEY_ALARM, Position.ALARM_BRAKING);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTPFA,F50201,866425030235982,GL300M,20190208124849,0BD4$"),
+ Position.KEY_ALARM, Position.ALARM_POWER_OFF);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTPNA,F50201,866425030235982,GL300M,20190208124909,0BD5$"),
+ Position.KEY_ALARM, Position.ALARM_POWER_ON);
+
+ verifyAttributes(decoder, buffer(
+ "+BUFF:GTSTC,410301,864802030022424,,,0,,,,,,,0228,0002,4EE8,1BFF489,00,20181207134332,EC90$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1A0900,860599000306845,G3-313,0,0,4,1,2.1,0,426.7,8.611466,47.681639,20181214134603,0228,0001,077F,4812,25.2,1,5.7,34,437.3,8.611600,47.681846,20181214134619,0228,0001,077F,4812,25.2,1,4.4,62,438.2,8.611893,47.681983,20181214134633,0228,0001,077F,4812,25.2,1,4.8,78,436.6,8.612236,47.682040,20181214134648,0228,0001,077F,4812,25.2,83,20181214134702,0654$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,4B0201,867995030004314,,00,1,C07FFFFF,,2,H982769,30263.00,1266,69,82,H0,P69.20,,0,2037.40,243.93,45.56,74.33,13115,,C00,,0,,,0,68.3,99,42.5,-69.099503,18.445614,20190417233605,0370,0002,5A3D,EB42,00,20190417183607,BD76$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,270703,867162025056839,gv300w,0,1,E07FFFFF,,2,H9307659,368713.50,1291,90,91,,P82.40,,61,10.10,6.76,3.34,524.08,,,0000,,00,,,007FFFFF,,,,,,,,,,,,,,,,,,,,,0000,2,0,,,0,88.6,104,117.6,-116.886007,32.543697,20181031202959,0334,0020,5234,7FCC3D0,00,20181031203002,9F50$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,310701,863286023712855,,00000004,28378,10,1,1,0.0,294,358.4,14.271475,50.110771,20181111185001,0230,0003,94D4,3B30,00,14.5,,,,110000,2,0,C03FFFFF,,0,H46400,12310.70,0,0,83,,,,0,,0.53,3.43,,,,40,,0,,,20181111185252,2DFF"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310701,863286023712855,,10,0,003FFFFF,,2,H46358,12305.50,601,0,83,,P53.00,,0,2749.15,0.19,2.80,,,,40,,0,,,20181110103016,2945$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310701,863286023712855,,10,0,203FFFFF,,2,H46358,12305.50,601,0,83,,P53.00,,0,2749.15,0.19,2.80,,,,40,,0,,,007FFFFF,,,,,,0,,,134,37,6,0.19,,0.00,0,,,,,,0,0,0,20181110112126,299F$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310701,863286023712855,,10,0,E03FFFFF,,2,H46358,12305.50,601,0,83,,P53.00,,0,2749.15,0.19,2.80,,,,40,,0,,,007FFFFF,,,,,,0,,,134,37,6,0.19,,0.00,0,,,,,,0,0,0,0,0.0,312,358.4,14.271460,50.110796,20181110103130,0230,0003,94D4,3B30,00,20181110105348,2969$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1F0301,862193022001432,WF0GXXGBBGBM26503,,14900,41,1,1,11.6,74,356.0,14.120023,50.167894,20181104080703,0230,0003,9B14,5891,00,74.1,,,,83,220000,799,7.3,,20181104080703,099B$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,4B0201,867995030001575,,10,0,C03FFFFF,,0,H0,,,,,,,,,,0.00,0.03,,,,0,,0,,,0,10.0,310,404.3,14.096743,50.143363,20181102110535,0230,0003,9B14,5066,00,20181102112101,03E0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTSTR,440502,866427030112088,GL530,0,0,2,,100,3,0.6,0,127.5,2.413963,48.877096,20180704180102,0208,0001,0310,E625,,,0000,20180704180100,004C$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLSW,300500,860599002636595,,0,0,0,0.0,0,2886.5,-78.467145,-0.165335,20180518221815,,,,,,20180518221817,B6FD$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLSW,300500,860599002636595,,0,1,0,0.0,0,2886.5,-78.467145,-0.165335,20180518221818,,,,,,20180518221819,B6FF$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTTSW,1A0100,135790246811220,,1,0,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20100214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLSW,1A0100,135790246811220,,0,1,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20100214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIGF,270302,867162025085234,,3519,0,0.0,92,111.2,-116.867638,32.450321,20180327070835,0334,0020,2B24,52CC3DE,00,,243.1,20180327070837,2A98$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTDIS,270302,867162025086950,,,21,1,1,0.0,81,117.8,-116.862025,32.453497,20180309084516,0334,0020,2B24,52CA916,00,1286.2,20180309084517,357E$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIGL,270302,867162025085234,,,01,1,1,0.0,92,111.2,-116.867638,32.450321,20180327070838,0334,0020,2B24,52CC3DE,00,243.1,20180327070839,2A9A$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,310603,863286023345490,,00000002,,10,1,2,0.3,0,155.7,8.000000,52.000000,20171215213040,0262,0002,1450,9F13,00,1130.3,00539:27:19,,,110000,2,1,28FFD5239115034E,1,,20171215213041,27C7$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,250C02,868789023691057,,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023335723,gv65,00,1,C03FFFFF,,0,,719601.00,,,,,,,,274.99,179.02,95.98,84761.00,,,0,,0,,,0,0.0,216,29.8,-2.155296,51.899400,20180209172714,0234,0010,53F3,8D38,00,20180211002128,E94E$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310201,153759012347650,gv65,0,1,C03FFFFF,,2,H89394,63.14,200,0,87,,P43.60,0,0,17.53,11.61,5.92,0.00,0,0,4002,0,1,0.76,35.00,0,,,,0,0,,0000,0000,0000,0000,00,20040101000052,05A6$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023346480,gv65,00,1,C03FFFFF,,2,H2843820,373.76,1440,44,77,M23,P35.00,1810,,59.48,42.68,16.80,15.42,,,610,,0,,,0,42.7,263,27.2,-2.156478,51.899989,20171021151805,0234,0010,15D6,9AD2,00,20171021151807,0B28$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023346480,gv65,02,1,C03FFFFF,,0,H2843820,373.80,0,4,75,M12,,1800,,59.49,42.69,16.80,15.42,,,0,,0,,,0,0.7,75,24.3,-2.155148,51.899400,20171021151837,0234,0010,15D6,9AD2,00,20171021152355,0B2E$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,380603,869606020025833,gv65,00000002,12003,10,1,1,0.0,172,24.6,-81.931875,26.577439,20171002045352,0310,0260,72BD,8E5B,00,1052.1,01383:52:12,0,100,210700,2,1,28FF4560A3150483,1,05B0,20171002045402,9548$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,04040E,861074023747143,gv200,41,8959301000648637556f,24,0,1,0,1,4.4,0,1,0,0,20170912221854,0,00,01,-0500,1,20170912193448,1D5B$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,210102,354524044950583,,42,89011702272048900184,11,99,0,,,4.08,0,1,1,0,0,20170831170831,87,0.00,,,,20170831171010,0064$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,360701,864251020253807,LSGTC58UX7Y067312,GV500,0,70FFFF,LSGTC58UX7Y067312,1,12309,983A8140,0,0,33,nan,,0,0,0,,10,0,,0,4.4,0,83.7,36.235142,49.967324,20170829112348,0255,0001,2760,9017,00,690.1,20170829112400,3456$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060502,861074023620928,,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,280500,A1000043D20139,GL300VC,41,,31,0,0,,,3.87,0,1,1,,,20170802150751,70,,48.0,,,20170802112145,03AC$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,2D0300,A1000043D20139,1G1JC5444R7252367,,11,,31,0,1,12986,,4.16,0,2,,,20170802145640,,,,,,+0000,0,20170802145643,CD5A$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTMPN,450102,865084030001323,gb100,0,1.6,0,-93.1,121.393023,31.164105,20170619103113,0460,0000,1806,2142,00,20170619103143,0512$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTTRI,862370030005908,1,0,99,1,0.0,354,18.5,18.821100,-34.084002,20170607152024,0655,0001,00DD,1CAE,00,0103010100,20170607172115,3E7D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060800,861074023677175,,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSWG,110100,358688000000158,,1,0,2.1,0,27.1,121.390717,31.164424,20110901073917,0460,0000,1878,0873,,20110901154653,0015$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTTMP,110100,358688000000158,,2,60,1,1,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,00,80,20110214093254,000F$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,110100,358688000000158,,41,0,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,,20110214093254,0022$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTBPL,110100,358688000000158,,3.53,0,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,,20110214093254,001F$"));
+
+ verifyNotNull(decoder, buffer(
+ "+BUFF:GTIGL,060228,862894020180553,,,00,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,20170407081824,9606$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTFRI,060228,862894020180553,,14827,10,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,01070:43:13,13,180,0,220101,,,,20170407081824,9607$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060502,861074023376992,,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060502,861074023689626,,25202,10,1,1,0.0,0,2744.1,-78.261047,0.023452,20170401211940,,,,,,0.0,00079:19:15,,,51,110000,,,,20170401212003,4DA7$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,210100,,,,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,06020B,862170010196747,,00000000,,10,1,2,1.8,0,-2.5,117.198440,31.845219,20120802061037,0460,0000,5663,0358,00,0.0,,,,0,410000,20120802061040,0012$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,060502,861074023692562,,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,210102,354524044925825,,1,1,1,29,2.8,0,133.7,-90.203063,32.265473,20170318005208,,,,,10800,4,20170318005208,0002$"));
+
+ verifyPositions(decoder, false, buffer(
+ "+RESP:GTFRI,210102,354524044925825,,1,1,1,,,,,,,,310,410,51bc,ca1dae6,10800,1,20170318214333,0002$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTGSM,400201,862365030025161,STR,0234,0015,003a,62a2,16,,0234,0015,003a,56a2,14,,0234,0015,003a,062a,13,,0234,0015,003a,32d9,11,,0234,0015,003a,56a0,11,,,,,,,,0234,0015,003a,7489,17,,20170219200048,0033$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTGSM,400201,862365030025161,STR,0234,0015,003a,56a2,18,,0234,0015,003a,77bc,14,,0234,0015,003a,32d9,12,,0234,0015,003a,062a,12,,0234,0015,003a,62a2,11,,0234,0015,003a,56a0,10,,0234,0015,003a,7489,15,,20170219080049,0030$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTGSM,400201,862365030034940,STR,0234,0030,0870,2469,19,,0234,0030,0870,35ee,18,,0234,0030,0870,16ac,12,,0234,0030,0870,16b2,11,,0234,0030,0870,360f,6,,0234,0030,0870,165d,6,,0234,0030,0870,35ef,17,,20170215220049,008D$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTR,400201,862365030034940,GL500,0,0,2,21.1,86,0,1.6,0,5.8,0.622831,51.582688,20170215090422,0234,0030,0870,35EF,,,,20170215220049,008C$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,2C0402,867162020000816,,0,0,1,2,0.3,337,245.7,-82.373387,34.634011,20170215003054,,,,,,63,20170215003241,3EAB$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTWIF,210102,354524044608058,,4,c413e200ff14,-39,,,,c413e2010e55,-39,,,,c8d3ff04a837,-43,,,,42490f997c6d,-57,,,,,,,,100,20170201020055,0001$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTWIF,210102,354524044484948,,1,08626693fb98,-36,,,,,,,,97,20170119071300,05E3$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,210102,A100004D9EF2AE,,41,,8,99,0,17.7,21,3.58,0,1,1,0,0,20161216135038,4,,,,,20161216135038,00AB$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTSTR,400201,862365030034957,GL500,0,0,2,23.1,5,2,0.2,0,36.0,0.623089,51.582744,20161129174625,0234,0015,03C3,3550,,,,20161129174625,0026$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTSTR,400201,862365030034957,GL500,0,1,2,21.8,100,0,,,,,,,0234,0015,03C3,3550,,,,20161129174009,0023$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,210102,A10000499AEF9B,,41,,0,0,0,15.0,9,3.87,0,1,1,0,0,20161101140211,72,,,,,20161101140211,00A3$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTNMR,210102,A10000499AEF9B,,0,0,1,9,0.0,0,288.0,-76.902364,39.578828,20161101134145,,,,,00,73,20161101134145,009F$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,210102,A10000499AEF9B,,0,1,1,9,0.5,0,288.0,-76.902364,39.578828,20161101134124,,,,,00,73,20161101134123,009D$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTRTL,210102,A10000499AEF9B,,0,0,1,10,0.2,0,305.4,-76.902274,39.578517,20161101155001,,,,,00,73,20161101155001,00A6$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,110100,358688000000158,,41,898600810906F8048812,18,99,0,33.23,1,4.19,1,1,1,0,0,20110714104934,100,,,,,20110714104934,0014$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,080100,135790246811220,,16,898600810906F8048812,16,0,1,11870,,4.1,0,0,0,,20090214013254,,12340,,00,00,+0800,0,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,040100,135790246811220,,16,898600810906F8048812,16,0,1,,0,4.4,0,0,0,0,20090214013254,13000,00,00,+0800,0,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,060100,135790246811220,,16,898600810906F8048812,16,0,1,12000,,4.4,0,0,0,0,20090214013254,0,1300,2000,00,00,+0800,0,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,1A0800,860599000773978,GL300,41,89701016426133851978,17,0,1,26.6,,3.90,1,1,0,0,0,20161003184043,69,1,44,,,20161004040811,022C$"));
+
+ verifyAttributes(decoder, buffer(
+ "+BUFF:GTINF,1A0800,860599000773978,GL300,41,89701016426133851978,23,0,1,204.7,,3.84,1,1,0,0,0,20161006072548,62,1,38,,,20161006082343,0C98$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,360100,864251020141408,3VWGW6AJ0FM237324,gv500,,10,1,1,0.0,0,2258.4,-99.256948,19.555800,20160929214743,0334,0020,0084,65AC,00,0.0,,,,100,410000,0,nan,,20160929214743,13BA$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,360201,864251020186064,4T1BE46KX7U018210,,0,19FFFF,4T1BE46KX7U018210,1,14283,983901C0,799,36,18,,33792,0,0,0,,,38,,6,53557,0,0.0,0,219.5,-76.661456,39.832588,20160507132153,20160507132154,0230$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,360201,864251020186064,1G1JC5444R7252367,,12802,10,1,0,0.0,0,219.5,-76.661456,39.832588,20160507132235,,,,,,20460.9,00080:03:37,,,100,210000,791,,56,20160507132239,0233$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1F0101,135790246811220,1G1JC5444R7252367,,,00,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,210100,,,50,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1F0101,135790246811220,1G1JC5444R7252367,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,92,80,210100,,,50,20090214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,060228,862894020178276,,21,0,0.0,0,411.3,-63.169745,-17.776330,20160319132220,0736,0003,6AD4,5BAA,00,20160319092223,1FBD$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTNMR,210102,A10000458356CE,,0,1,1,9,0.0,8,190.7,-85.765865,42.894837,20160316123202,,,,,60,30,20160316123202,0137$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIDA,060228,862894020178276,,,01C68011010000C7,1,1,0,0.0,0,413.0,-63.169675,-17.776349,20160317222129,0736,0003,6AD4,32CF,00,34.9,,,,,20160317182130,1626$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIGN,060228,862894020180553,,9860,0,0.2,189,420.0,-63.158195,-17.800608,20160309022951,0736,0003,6AD4,3471,00,,881.2,20160308222956,129A$"));
+
+ verifyPosition(decoder, buffer(
+ "+BUFF:GTIGF,060228,862894020180553,,1958,0,0.0,240,390.3,-63.089213,-17.764712,20160309122854,0736,0003,6AB8,5A23,00,,936.8,20160309082858,1368$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060228,862894020180553,,,10,1,1,20.0,147,329.7,-62.899703,-17.720434,20160309113548,0736,0003,6AAE,3381,00,913.3,,,,0,220101,,,,20160309073554,132B$"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,060402,862894021808798,,,10,1,1,0.0,349,394.3,-63.287717,-17.662410,20160116234031,0736,0003,6ABA,8305,00,3326.8,,,,94,220100,,,,20160116194035,4D83"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,2C0204,867162020003125,GL300W,0,0,2,1,1.7,205,2867.0,-78.481127,-0.206828,20160215210433,0740,0000,7596,5891C,0.0,1,1.7,205,2867.0,-78.481127,-0.206828,20160215210503,0740,0000,7596,5891C,0.0,88,20160215210506,1E78$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,060228,862894020178276,,15153,10,1,1,0.0,0,431.7,-63.169571,-17.776235,20160210153458,0736,0003,6AD4,80EF,00,34.9,00117:31:26,13442,15163,0,210101,,,,20160210113503,38EE$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,110100,A5868800000015,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,00,80,20110214013254,000C"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTFRI,210102,A10000458356CE,,0,1,1,15,1.4,0,190.6,-85.765763,42.894896,20160208164505,4126,210,0,18673,00,92,20160208164507,00A6"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,060402,862894021808798,,,10,1,1,0.0,349,394.3,-63.287717,-17.662410,20160116234031,0736,0003,6ABA,8305,00,3326.8,,,,94,220100,,,,20160116194035,4D83"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTIDA,06020A,862170013895931,,,D2C4FBC5,1,1,1,0.8,0,22.2,117.198630,31.845229,20120802121626,0460,0000,5663,2BB9,00,0.0,,,,,20120802121627,008E$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTINF,1F0101,135790246811220,1G1JC5444R7252367,,16,898600810906F8048812,16,0,1,12000,,4.2,0,0,,,20090214013254,,,,,,+0800,0,20090214093254,11F0$"));
+
+ verifyPositions(decoder, false, buffer(
+ "+RESP:GTFRI,120113,555564055560555,,1,1,1,,,,,,,,0282,0380,f080,cabf,6900,79,20140824165629,0001$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,0F0106,862193020451183,,,10,1,1,0.0,163,,-57.513617,-25.368191,20150918182145,,,,,,21235.0,,,,0,210100,,,,20150918182149,00B8$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,1F0109,864251020135483,,gv500,0,78FFFF,,1,12613,,,,,,,,,,,,,,1286,0,0.0,0,17.1,3.379630,6.529701,20150813074639,0621,0030,51C0,A2B3,00,0.0,20150813074641,A7E6$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTOBD,1F0109,864251020135483,4T1BE46KX7U018210,gv500,0,78FFFF,4T1BE46KX7U018210,1,13411,981B81C0,787,3,43,,921,463,1,10,0300030103030304001200310351035203530354,20,55,,1286,0,6.5,74,21.6,3.379710,6.529714,20150813074824,0621,0030,51C0,A2B3,00,0.0,20150813074828,A7E9$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,1A0401,860599000508846,,41,0,0.0,84,107.5,-76.657998,39.497203,20150623160622,0310,0260,B435,3B81,,20150623160622,0F54$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1A0401,860599000508846,,0,0,1,1,134.8,154,278.7,-76.671089,39.778885,20150623154301,0310,0260,043F,7761,,99,20150623154314,0F24$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,1A0200,860599000165464,CRI001,0,0,1,2,,41,,-71.153137,42.301634,20150328020301,,,,,280.3,55,20150327220351,320C"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,02010D,867844001675407,,0,0,1,2,0.0,0,28.9,8.591011,56.476397,20140915213209,0238,0001,03CB,2871,,97,20140915213459,009A"));
+
+ verifyNull(decoder, buffer(
+ "+RESP:GTINF,359464030073766,8938003990320469804f,18,99,100,1,0,+2.00,0,20131018084015,00EE,0103090402"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,04040C,359231038939904,,,10,1,2,0.0,117,346.0,8.924243,50.798077,20130618122040,0262,0002,0299,109C,00,0.0,,,,,,,,,20130618122045,00F6"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTSTT,04040C,359231038939904,,42,0,0.0,117,346.0,8.924243,50.798077,20130618125152,0262,0002,0299,109C,00,20130618125154,017A"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,020102,000035988863964,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,020102,135790246811220,,0,0,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTDOG,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTLBC,020102,135790246811220,,+8613800000000,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTGCR,020102,135790246811220,,3,50,180,2,0.4,296,-5.4,121.391055,31.164473,20100714104934,0460,0000,1878,0873,00,,20100714104934,000C"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTFRI,07000D,868487001005941,,0,0,1,1,0.0,0,46.3,-77.039627,38.907573,20120731175232,0310,0260,B44B,EBC9,0015e96913a7,-58,,100,20120731175244,0114"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTHBM,0F0100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTHBM,0F0100,135790246811220,,,11,1,1,24.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,02010C,867844001274144,,0,0,1,1,18.0,233,118.1,7.615551,51.515600,20140106130516,0262,0007,79E6,B956,,72,20140106140524,09CE$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,02010C,867844001274649,,0,0,1,1,0.0,0,122.5,7.684216,51.524512,20140106233722,0262,0007,79EE,1D22,,93,20140107003805,03C4$"));
+
+ verifyPositions(decoder, buffer(
+ "+BUFF:GTFRI,210101,863286020016706,,,10,1,1,,,,49.903915,40.391669,20140818105815,,,,,,,,,,,210100,,,,,000C$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,240100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,80,,,,,,20090214093254,11F0$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTFRI,240100,135790246811220,,,10,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,,,,,20090214093254,11F0$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTSTT,280100,A1000043D20139,,42,0,0.1,321,228.6,-76.660884,39.832552,20150615120628,0310,0484,00600019,0A52,,20150615085741,0320$"));
+
+ verifyNotNull(decoder, buffer(
+ "+RESP:GTRTL,280100,A1000043D20139,,0,0,1,1,0.1,321,239.1,-76.661047,39.832501,20150615114455,0310,0484,00600019,0A52,,87,20150615074456,031E$"));
+
+ verifyAttributes(decoder, buffer(
+ "+BUFF:GTBPL,1A0800,860599000773978,GL300,3.55,0,0.0,0,257.1,60.565437,56.818277,20161006070553,,,,,204.7,20161006071028,0C75$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTTEM,1A0102,860599000000448,,3,33,0,5.8,0,33.4,117.201191,31.832502,20130109061410,0460,0000,5678,2079,,20130109061517,0091$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTJDR,0A0102,135790246811220,,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTJDS,0A0102,135790246811220,,2,0,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTSOS,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0$"));
+
+ verifyAttributes(decoder, buffer(
+ "+RESP:GTVER,1A0800,860599000773978,GL300,GL300,0A03,0103,20161007041531,10F8$"));
+
+ verifyNull(decoder, buffer(
+ "+ACK:GTHBD,1A0401,135790246811220,,20100214093254,11F0"));
+
+ verifyAttributes(decoder, buffer(
+ "+ACK:GTRTO,1A0800,860599000773978,GL300,VER,FFFF,20161006053520,0C19"));
+
+ verifyAttributes(decoder, buffer(
+ "+ACK:GTJDC,0A0102,135790246811220,,0016,20090214093254,11F0"));
+
+ verifyAttributes(decoder, buffer(
+ "+ACK:GTGEO,1A0102,135790246811220,,0,0008,20100310172830,11F0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java
new file mode 100644
index 000000000..eac948cc4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java
@@ -0,0 +1,61 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GlobalSatProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GlobalSatProtocolDecoder decoder = new GlobalSatProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "GSh,131826789036289,3,M,ea04*3d"));
+
+ decoder.setFormat0("TSPRXAB27GHKLMmnaictuvw*U!");
+
+ verifyPosition(decoder, text(
+ "GSb,GTR-388,358173053992353,0000,5,8080,3,270419,113326,E01020.6223,N6323.1937,129,0.01,154,10,0.8,12380mV,3128mV,0,0,11,242,02,10EB,120FC1B*5a!"));
+
+ decoder.setFormat0("SORPZAB27GHKLMN*U!");
+
+ verifyPosition(decoder, text(
+ "GSr,011412001878820,4,5,00,,1,250114,105316,E00610.2925,N4612.1824,0,0.02,0,1,0.0,64*51!"));
+
+ verifyPosition(decoder, text(
+ "GSr,357938020310710,,4,04,,1,170315,060657,E00000.0000,N0000.0000,148,0.00,0,0,0.0,11991mV*6c!"));
+
+ decoder.setFormat0("TSPRXAB27GHKLMnaicz*U!");
+
+ verifyPosition(decoder, text(
+ "GSr,1,135785412249986,01,I,EA02,3,230410,153318,E12129.2839,N2459.8570,0,1.17,212,8,1.0,12.3V*55"));
+
+ verifyPosition(decoder, text(
+ "GSr,GTR-128,012896009148443,0040,5,0080,3,190813,185812,W11203.3661,N3330.2104,344,0.24,78,9,0.8,60%,0,0,12,\"310,410,0bdd,050d,02,21\",\"310,410,0bdd,0639,24,7\"*79"));
+
+ verifyPosition(decoder, text(
+ "$355632004245866,1,1,040202,093633,E12129.2252,N2459.8891,00161,0.0100,147,07,2.4"));
+
+ verifyPosition(decoder, text(
+ "$355632000959420,9,3,160413,230536,E03738.4906,N5546.3148,00000,0.3870,147,07,2.4"));
+
+ verifyPosition(decoder, text(
+ "$353681041893264,9,3,240913,100833,E08513.0122,N5232.9395,181.3,22.02,251.30,9,1.00"));
+
+ decoder.setFormat0("SPRXYAB27GHKLMmnaefghiotuvwb*U!");
+
+ verifyPosition(decoder, text(
+ "GSr,GTR-128,013227006963064,0080,1,a080,3,190615,163816,W07407.7134,N0440.8601,2579,0.01,130,12,0.7,11540mV,0,77,14,\"732,123,0744,2fc1,41,23\",\"732,123,0744,2dfe,05,28\",\"732,123,0744,272a,15,21\",\"732,123,0744,2f02,27,23\"*3b!"));
+
+ verifyPosition(decoder, text(
+ "$80050377796567,0,13,281015,173437,E08513.28616,N5232.85432,222.3,0.526,,07*37"),
+ position("2015-10-28 17:34:37.000", true, 52.54757, 85.22144));
+
+ verifyPosition(decoder, text(
+ "$80050377796567,0,18,281015,191919,E08513.93290,N5232.42141,193.4,37.647,305.40,07*37"),
+ position("2015-10-28 19:19:19.000", true, 52.54036, 85.23222));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java
new file mode 100644
index 000000000..f9ea95a4f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GlobalstarProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GlobalstarProtocolDecoder decoder = new GlobalstarProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
+ "<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"17/02/2019 21:56:15 GMT\" messageID=\"2a471778dda31005850dc52bb93ae81a\">",
+ "<stuMessage>",
+ "<esn>0-2654816</esn>",
+ "<unixTime>1550440592</unixTime>",
+ "<gps>N</gps>",
+ "<payload length=\"9\" source=\"pc\" encoding=\"hex\">0x00337BA619B7250A00</payload>",
+ "</stuMessage>",
+ "</stuMessages>")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java
new file mode 100644
index 000000000..91aca50c8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GnxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GnxProtocolDecoder decoder = new GnxProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GNX_MIF,865733022354161,143,0,172642,180316,172642,180316,1,13.034581,N,080.234521,E,0,05396274,ROUTE_2#########,Deo ############,GNX04008,B0*"));
+
+ verifyPosition(decoder, text(
+ "$GNX_LOC,865733022352132,095,0,102134,280914,102134,280914,1,18.765432,N,073.752811,W,032,165.32,12,25,0,A,E,2,000099.9,000099.5,GNX01001,12*"));
+
+ verifyNull(decoder, text(
+ "$GNX_LOC,865733022354161,139,0,142838,160316,142825,160316,0,000000000,N,0000000000,E,000,0.00,00,48,0,e,C,2,000000.0,000000.0,GNX04008,BB*"));
+
+ verifyPosition(decoder, text(
+ "$GNX_DIO,863071015071563,110,1,155627,121214,151244,121214,1,08.878321,N,076.643154,E,0,0,0,0,0,0,GNX01001,B1*"));
+
+ verifyNull(decoder, text(
+ "$GNX_DIO,865733022354161,112,1,142849,160316,142714,160316,0,000000000,N,0000000000,E,0,0,0,0,0,0,0,GNX04008,1A*"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
new file mode 100644
index 000000000..2ad080219
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
@@ -0,0 +1,100 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class GoSafeProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GoSafeProtocolDecoder decoder = new GoSafeProtocolDecoder(null);
+
+ verifyAttribute(decoder, text(
+ "*GS06,356449068350122,013519070819,,SYS:G6S;V3.37;V1.1.8,GPS:A;12;N23.169866;E113.450728;0;255;54;0.79,COT:18779;,ADC:12.66;0.58,DTT:4084;E1;0;0;0;1,IWD:0;1;ad031652643fff28;23.2;1;1;86031652504fff28;24.3;2;1;e603165252a5ff28;24.2;3;1;bb0416557da6ff28;24.0#"),
+ Position.PREFIX_TEMP + 3, 24.0);
+
+ verifyAttribute(decoder, text(
+ "*GS06,351535058659335,081234310719,,SYS:G6S;V3.37;V1.1.8,GPS:A;10;N23.169758;E113.450640;0;323;47;0.82,COT:18539;,ADC:10.81;4.07,DTT:4000;E0;0;0;0;1,IWD:0;1;9f00000655705d28;22.5#"),
+ Position.PREFIX_TEMP + 0, 22.5);
+
+ verifyAttribute(decoder, text(
+ "*GS06,359568052580548,091946150719,1C,SYS:G3C;V1.40;V1.0.4,GPS:A;5;S25.750200;E28.204858;0;0;1337;1.68,COT:,ADC:13.12;4.06,DTT:4004;C6;0;0;10000000;0$091948150719,,SYS:G3C;V1.40;V1.0.4,GPS:A;5;S25.750200;E28.204858;0;0;1337;1.68,COT:,ADC:12.96;4.06,DTT:4004;C6;0;0;0;1#"),
+ Position.KEY_EVENT, 0x1C);
+
+ verifyPositions(decoder, text(
+ "*GS06,860078024226974,101437211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;7;N3.052302;E101.787216;16;137;48;1.58,COT:4261733103,ADC:22.86;0.58;0.01,DTT:4004;E1;0;0;0;3$101439211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;8;N3.052265;E101.787200;12;152;46;1.31,COT:4261733103,ADC:22.98;0.58;0.01,DTT:4004;E1;0;0;0;3$101441211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;8;N3.052247;E101.787232;8;131;46;1.34,COT:4261733103,ADC:23.13;0.58;0.01,DTT:4004;E1;0;0;0;3$101510211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;8;N3.052150;E101.787152;0;131;40;0.97,COT:4261733160,ADC:22.88;0.58;0.01,DTT:4000;E1;0;0;0;1$101540211218,,SYS:G3SC;V3.36;V1.1.8,GPS:A;7;N3.052150;E101.787152;0;131;40;0.97,COT:4261733160,ADC:22.91;0.58;0.00,DTT:4000;E1;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,359568052570135,090349191018,,SYS:G3C;V1.40;V1.0.4,GPS:A;10;S26.112722;E28.078766;0;23;1581;0.80,COT:,ADC:10.86;3.79,DTT:4000;E7;0;0;0;1$090419191018,,SYS:G3C;V1.40;V1.0.4,GPS:A;10;S26.112722;E28.078766;0;23;1581;0.80,COT:,ADC:10.85;3.79,DTT:4000;E7;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,359913060650380,152248050718,,SYS:G3C;V1.38;V05,GPS:A;10;N31.914370;E35.914640;0;0,COT:689,ADC:0.18;3.55,DTT:4025;E6;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,359913060650380,101019050718,,SYS:G3C;V1.38;V05,GPS:L;6;N31.916576;E35.908480;0;0,GSM:1;4;416;3;627A;A84B;-66,COT:188,ADC:4.31;3.88,DTT:4005;E6;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,860078024287174,070120310318,,SYS:G3SC;V3.32;V1.1.8,GPS:A;9;N23.169946;E113.450568;0;0;23;0.86,COT:65;20,ADC:4.27;3.73;0.01;0.02,DTT:4004;E0;0;0;0;1,IWD:0;0;000000000000#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,860078024213915,032544190318,,SYS:G3SC;V3.32;V1.1.8,GPS:A;7;N3.052417;E101.787112;0;0;94;1.38,COT:686;0-0-0,ADC:16.25;4.09,DTT:4000;E0;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS06,351535058659335,062728190318,,SYS:G6S;V3.32;V1.0.5,GPS:A;10;N23.169806;E113.450760;0;0;81;0.77,COT:0,ADC:0.00;0.16,DTT:80;E0;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS26,356449061046586,082522030117,,SYS:G737IC;V1.13;V1.0.5,GPS:V;5;N42.594136;W70.723832;0;0;8;2.06,GSM:;;310;260;C76D;9F1D;-85,ADC:3.86,DTT:3918C;;0;0;0;1,#"));
+
+ verifyPositions(decoder, text(
+ "*GS56,357330051092344,123918301116,10,GPS:L;9;N47.582920;W122.238720;0;0;102;0.99,GSM:0;0;310;410;A7DB;385C;-86,COT:76506,ADC:0.82;3.77,DTT:2184;;0;0;10000;0$000000000000,86,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.75,DTT:0;;0;0;40;0$000000000000,86,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.74,DTT:0;;0;0;40;0$000000000000,93,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.73,DTT:8000;;0;0;80000;0$000000000000,13,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:11.09;3.79,DTT:2004;;0;0;80000;0$000000000000,90,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:11.13;3.79,DTT:23004;;0;0;10000;0$000000000000,,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,GSM:5;2;310;410;A7DB;385C;-89,COT:76506,ADC:14.12;3.81,DTT:23184;;0;0;0;6#"));
+
+ verifyPositions(decoder, text(
+ "*GS26,356449061139936,022918011216,,SYS:G737IC;V1.13;V1.0.5,GPS:A;9;N42.651728;W70.623520;0;0;48;1.50,ADC:4.08,DTT:3900C;;0;0;0;1,#"));
+
+ verifyNotNull(decoder, text(
+ "*GS56,356449063230915,052339180916,,SYS:G7S;V1.08;V1.2,GPS:V;4;N24.730006;E46.637816;14;0;630,GSM:;;420;4;5655;507A;-70,COT:75242;2-8-17,ADC:13.22;0.08,DTT:23004;;0;0;0;1#"));
+
+ verifyNotNull(decoder, text(
+ "*GS56,356449063230915,052349180916,,SYS:G7S;V1.08;V1.2,GPS:V;6;N24.730384;E46.637620;47;56;607,GSM:;;420;4;5655;507A;-70,COT:75290;2-8-27,ADC:13.24;0.08,DTT:23004;;0;0;0;1#"));
+
+ verifyNotNull(decoder, text(
+ "*GS56,356449063230915,052444180916,,SYS:G7S;V1.08;V1.2,GPS:V;6;N24.730384;E46.637620;47;56;607,GSM:;;420;4;5655;F319;-102,COT:75290;2-9-27,ADC:13.00;0.08,DTT:23004;;0;0;0;1$052449180916,,SYS:G7S;V1.08;V1.2,GPS:V;6;N24.730384;E46.637620;47;56;607,GSM:;;420;4;5655;F319;-102,COT:75290;2-9-27,ADC:13.13;0.08,DTT:23004;;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS16,356449062643845,141224290316,,SYS:G79;V1.13;V1.0.2,GPS:V;5;N24.694972;E46.680736;46;334;606;1.43,GSM:;;420;4;5655;4EB8;-57,COT:330034,ADC:13.31;3.83,DTT:27004;;0;0;0;1,OBD:064101000400000341057E04410304000341510104411001C203410F4B0341112904411F01AB0641010004000014490201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF03410D21,FUL:28260"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,145425130316,,GPS:V;0;N0.000000;E0.000000;0;0;0;0.00;0.00,GSM:1;3;416;3;A8C;2820;-81;416;3;A8C;281F;-83;416;3;A8C;368A;-87;416;3;A8C;368B;-89;416;3;A8C;2C26;-103;416;3;A8C;3689;-107;416;3;A8C;2D83;-107"));
+
+ verifyPosition(decoder, text(
+ "*GS02,358696043774648,GPS:230040;A;S1.166829;E36.934287;0;0;170116,STT:20;0,MGR:32755204,ADC:0;11.2;1;28.3;2;4.1,GFS:0;0"));
+
+ verifyNull(decoder, text(
+ "*GS02,358696043774648"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535058709775,100356130215,,SYS:G79W;V1.06;V1.0.2,GPS:A;6;N24.802700;E46.616828;0;0;684;1.35,COT:60,ADC:4.31;0.10,DTT:20000;;0;0;0;1"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,074558291015,,GPS:A;9;N31.935942;E35.867092;;345;921;1.03;1.59,GSM:1;3;416;3;A8C;368B;-78;416;3;A8C;2820;-73;416;3;BB8;2CBE;-76;416;3;A8C;368A;-76;416;3;A8C;2C26;-79,OBD:04410C122003410D0F03411C0103410547037F011203411100"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,083515281015,,GPS:A;9;N31.959502;E35.908316;;108;890;1.05;1.79,GSM:1;4;416;3;AF0;A3A6;-59;416;3;AF0;A3A3;-50;416;3;AF0;A3A4;-56;416;3;AF0;A3A5;-62;416;3;AF0;B195;-76,OBD:04410C194603410D2303411C0103410583037F011203411115"));
+
+ verifyNull(decoder, text(
+ "*GS16,351535058709775"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,103441131015,,GPS:A;8;N31.960122;E35.921652;27;99;847;1.33;2.41,GSM:1;4;416;3;AF0;9C73;-61;416;3;AF0;9C89;-68,OBD:04410C0DA403410D0B03411C010341057A037F011203411100$103453131015,,GPS:A;8;N31.959976;E35.922144;6;0;835;1.33;2.41,GSM:1;4;416;3;AF0;9C73;-67;416;3;AF0;9C89;-64;416;3;AF0;B389;-83,OBD:04410C0D8E03410D0B03411C010341057D037F011203411100$103503131015,,GPS:A;9;N31.959870;E35.922284;11;127;830;1.33;2.41,GSM:1;4;416;3;AF0;9C73;-67;416;3;AF0;9C89;-64;416;3;AF0;B389;-83,OBD:04410C0D8E03410D0B03411C010341057D037F011203411100$103513131015,,GPS:A;9;N31.959742;E35.922516;10;106;830;1.37;2.91,GSM:1;4;416;3;AF0;9C73;-67;416;3;AF0;9C89;-64;416;3;AF0;B389;-83,OBD:04410C0D1003410D0603411C010341057E037F011203411100$103553131015,,GPS:A;8;N31.959564;E35.923308;6;0;836;1.41;2.43,GSM:1;4;416;3;AF0;9C73;-65;416;3;AF0;B389;-71;416;3;AF0;9C89;-74,OBD:04410C0DAE03410D0403411C010341057C037F011203411100#"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,155750220815,,SYS:G79;V1.10;V1.0.2,GPS:A;4;N31.944198;E35.846644;0;0;923;9.47;1.00,COT:155133,ADC:12.21;0.10,DTT:20002;;0;0;0;1#"));
+
+ verifyPositions(decoder, text(
+ "*GS16,351535059439208,070034220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945970;E35.859848;29;65;922;1.14;1.68,COT:147528,ADC:14.07;0.11,DTT:27006;;0;0;0;3,OBD:04410C1ECD03410D2D03411C010341057A037F011203411107$070035220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945934;E35.859908;29;86;922;1.14;1.68,COT:147528,ADC:13.94;0.15,DTT:27006;;0;0;0;3,OBD:04410C1ECD03410D2D03411C010341057A037F011203411107$070037220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945844;E35.859952;29;123;922;1.14;1.68,COT:147625,ADC:13.75;0.11,DTT:27006;;0;0;0;3,OBD:04410C0FE803410D1803411C010341057C037F011203411100$070038220815,,SYS:G79;V1.10;V1.0.2,GPS:A;8;N31.945808;E35.859940;29;145;923;1.14;1.68,COT:147625,ADC:14.00;0.11,DTT:27006;;0;0;0;3,OBD:04410C0FE803410D1803411C010341057C037F011203411100#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java
new file mode 100644
index 000000000..23762f572
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java
@@ -0,0 +1,40 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GotopProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GotopProtocolDecoder decoder = new GotopProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ ""));
+
+ verifyNull(decoder, text(
+ "353327020412763,CMD-X"));
+
+ verifyPosition(decoder, text(
+ "867688038677542,ALM-D1,V,DATE:190709,TIME:185214,LAT:50.0422838N,LON:014.4504646E,Speed:004.3,081-20,05.68"));
+
+ verifyPosition(decoder, text(
+ "013226009991924,CMD-T,A,DATE:130802,TIME:153721,LAT:25.9757433S,LOT:028.1087816E,Speed:000.0,X-X-X-X-81-26,000,65501-00A0-4B8E"));
+
+ verifyPosition(decoder, text(
+ "353327020115804,CMD-T,A,DATE:090329,TIME:223252,LAT:22.7634066N,LOT:114.3964783E,Speed:000.0,84-20,000"),
+ position("2009-03-29 22:32:52.000", true, 22.76341, 114.39648));
+
+ verifyPosition(decoder, text(
+ "353327020115804,CMD-T,A,DATE:090329,TIME:223252,LAT:22.7634066N,LOT:114.3964783E,Speed:000.0,1-1-0-84-20,000"));
+
+ verifyPosition(decoder, text(
+ "353327020412763,CMD-F,V,DATE:140125,TIME:183636,LAT:51.6384466N,LOT:000.2863866E,Speed:000.0,61-19,"));
+
+ verifyPosition(decoder, text(
+ "013949008891817,CMD-F,A,DATE:150225,TIME:175441,LAT:50.000000N,LOT:008.000000E,Speed:085.9,0-0-0-0-52-31,000,26201-1073-1DF5"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java
new file mode 100644
index 000000000..ce21f733f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class Gps056FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gps056FrameDecoder decoder = new Gps056FrameDecoder();
+
+ assertEquals(
+ binary("242435314750534c5f30323836323436323033333738323934361905110f160b0b7710584e1cbd1b9b4500005b100300fb0a071700ffff23"),
+ decoder.decode(null, null, binary("242435314750534c5f30323836323436323033333738323934361905110f160b0b7710584e1cbd1b9b4500005b100300fb0a071700ffff230030")));
+
+ assertEquals(
+ binary("242432354c4f474e5f3131383632343632303333373832393436322e3123"),
+ decoder.decode(null, null, binary("242432354c4f474e5f3131383632343632303333373832393436322e3123")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java
new file mode 100644
index 000000000..a6d0c024a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gps056ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gps056ProtocolDecoder decoder = new Gps056ProtocolDecoder(null);
+
+ verifyNull(decoder, buffer(
+ "$$25LOGN_118624620337829462.1#"));
+
+ verifyPosition(decoder, binary(
+ "242435314750534C5F30323836333037313031353034353834391D0A0E091A0A0B1112C34E1E23230A45FF00000000000000000000000023"));
+
+ verifyAttributes(decoder, binary(
+ "2424323853594E435F313138363330373130313530343538343900000000000023"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
new file mode 100644
index 000000000..63a6e06d0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
@@ -0,0 +1,264 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Gps103ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gps103ProtocolDecoder decoder = new Gps103ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "imei:760112011448012,001,2001151918,,F,191833.000,A,6136.6174,N,2126.9901,E,0.00,202.6,-0.1,1,,,,20;"));
+
+ verifyAttribute(decoder, text(
+ "imei:868683023212255,tracker,190205084503,,F,064459.000,A,4915.1221,N,01634.5655,E,3.91,83.95;"),
+ "course", 83.95);
+
+ verifyPosition(decoder, text(
+ "imei:864180034124375,vt14,190116192753,,F,172750.000,A,3649.2186,N,00235.8411,W,0.00,0,,0,0,51.93%,,+22;"));
+
+ verifyNull(decoder, text(
+ "imei:864180034124375,vr,0c00fa011ea05a03d726977103ad0034c98ef49e6d303fffd1c8361303f2dbb0fa530d8ca3930be3e94f4110145c7029a507a0a00028f4a70514c05c500503170334b400531971cad002ab7634f001a4027949c8e541ea47f853bca2f961ba427ab1e290089197711c485e4f6e82ad0d1ee8f25573eed4af60284f6935bb7ef2307f1dc3f4355bbf3f90a607ffd2e5fcc6c60310be80f152dbca54ed53147fed3ae7fa1fe54809e45330fdedc993d073b7f2a6340a00d8cabeb9a4980c68900e5cb1f6e29aab9e00fc334012a5a5c32e5227c7b21a9a2d35dc02cdb49edb189fe545ec058fec7565e667cfa08b1fccd34686e4f12003dd7ffaf4b980957440bd595beb53269bb082a517dd570693604cb6299cb1663f5a9d608e3e42807d71cd002d206028011e50aa49381513494011349cd2092803ffd3d031f154ef11bc86d870781f8679a90302540adc545c55a00e28c8a602e47ad2e46680133cd19e6800cd1cf4a0063293ce695188e0d20265e58559305c3ffcb2948eca10d20278adb50518890c40f5ed9a5fece941ccb29cfd19ff00c2a6e860d6471c7da1cffb3b507eb9aaafa3dc4a4909b7fdf6c9fd28b88fffd4ca5d06e7b94152af87a53f7a7403d949a8e60265f0eaff0014ee7e8807f5a99340857abcadf881fd2973013a6936f1f48d4ffbc01fe95652dd63fba00fa521926d1de8e07a51600dc3d01d00;"));
+
+ verifyPosition(decoder, text(
+ "imei:868683026321020,T:+11,181217080050,,F,080047.000,A,3227.3057,N,11649.4754,W,0.00,0,,0,0,0.00%,,+11;"));
+
+ verifyAttribute(decoder, text(
+ "imei:868683026321020,tracker,181217080106,,F,080102.000,A,3227.3057,N,11649.4754,W,0.00,0,,0,0,0.00%,0,+11;"),
+ Position.PREFIX_TEMP + 1, 11);
+
+ verifyPosition(decoder, text(
+ "imei:861359038609986,Equipo 1,---,------,----,214734,241018,26,1,-33.42317,-70.61930,067,229,0674,1.00,08,0,1,---,*"));
+
+ verifyPosition(decoder, text(
+ "imei:861359038609986,Equipo 1,---,------,----,214812,241018,14,0,-33.42317,-70.61930,000,000,0000,99.9,00,0,1,---,*"));
+
+ verifyNull(decoder, text(
+ "imei:123451234512345,L,*"));
+
+ verifyAttributes(decoder, text(
+ "imei:868683027758113,OBD,180905200218,,,,0,0,0.39%,70,9.41%,494,0.00,P0137,P0430,,;"));
+
+ verifyAttribute(decoder, text(
+ "imei:353451044508750,001,0809231929,13554900601,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,30.1,65.43,1,0,10.5%,0.0%,28;"),
+ "fuel1", 10.5);
+
+ verifyPosition(decoder, text(
+ "imei:864180036029895,acc on,180508145653,,F,065645.000,A,4729.1497,N,01904.2342,E,0.00,0,,1,,0.00%,,;"));
+
+ verifyNotNull(decoder, text(
+ "imei:864895030279986,ac alarm,180404174252,,L,,,296a,,51f7,,,"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710048977327,OBD,180301094003,5000000,0.00,0.00,98,18,68.63%,55,25.10%,1368,14.24,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:862106025092216,OBD,170605095949,195874,,370.8,808,066,30.0%,+87,13.0%,02444,14.3,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:868683027825532,OBD,170613203014,,,,0,0,0.00%,0,0.00%,0,0.00,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:862106025092216,OBD,170612165656,196043,,145803.9,,000,0.0%,+,0.0%,00000,12.6,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:862106025092216,OBD,170605095949,195874,,370.8,808,066,30.0%,+87,13.0%,02444,14.3,,,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,DTC,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,30.1,,1,0,10.5%,P0021,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,oil1,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,,,51.6,41.7,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,oil2,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,,,51.6,41.7,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,oil 51.67,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,T:+28.0,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,bonnet alarm,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353451044508750,footbrake alarm,0809231929,,F,055403.000,A,2233.1870,N,11354.3067,E,0.00,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:862106021237716,ac alarm,1611291645,,F,204457.000,A,1010.2783,N,06441.0274,W,0.00,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049057798,OBD,161003192752,1785,,,0,54,96.47%,75,20.00%,1892,0.00,P0134,P0571,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049090138,OBD,160629022949,51442,0.00,15.88,5632,122,40.39%,95,0.00%,2101,13.80,,,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:359710049090138,tracker,160629022948,,F,182949.000,A,4043.8839,N,11328.8029,W,65.26,271.82,,1,0,31.37%,51442,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049042014,001,160615040011,,F,040011.000,A,2833.0957,N,07711.9465,E,0.01,215.33,,0,,,,;"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049028435,OBD,160316053657,70430,,,0,49,60.00%,46,19.22%,859,0.00,U1108,,,;"));
+
+ verifyPosition(decoder, text(
+ "359769031878322imei:359769031878322,tracker,1602160718,2,F,221811.000,A,1655.2193,S,14546.6722,E,0.00,,"));
+
+ verifyNull(decoder, text(
+ "imei:865328021049167,OBD,141118115036,,,0.0,,000,0.0%,+,0.0%,00000,,,,,"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049032874,OBD,160208152900,13555,,,45,0,24.71%,35,13.73%,1230,14.13,U1108,,,"));
+
+ verifyAttributes(decoder, text(
+ "imei:359710049064398,OBD,160101035156,17887,0.00,17.06,0,0,0.00%,0,0.00%,16383,10.82,,,,"));
+
+ verifyPosition(decoder, text(
+ "imei:868683020235846,rfid,160202091347,49121185,F,011344.000,A,0447.7273,N,07538.9934,W,0.00,0,,0,0,0.00%,,"));
+
+ verifyNotNull(decoder, text(
+ "imei:359710049075097,help me,,,L,,,113b,,558f,,,,,0,0,,,"));
+
+ verifyNotNull(decoder, text(
+ "imei:359710041100000,tracker,000000000,,L,,,fa8,,c9af,,,,,0,0,0.00%,,"));
+
+ verifyNotNull(decoder, text(
+ "imei:863070016871385,tracker,0000000119,,L,,,0FB6,,CB5D,,,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710045559474,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;"),
+ position("2015-10-30 00:01:01.000", true, 54.72306, 25.21512));
+
+ verifyPosition(decoder, text(
+ "imei:359710049092324,tracker,151027025958,,F,235957.000,A,2429.5156,N,04424.5828,E,0.01,27.91,,0,0,,,;"),
+ position("2015-10-26 23:59:57.000", true, 24.49193, 44.40971));
+
+ verifyPosition(decoder, text(
+ "imei:865328021058861,tracker,151027041419,,F,011531.000,A,6020.2979,N,02506.1940,E,0.49,113.30,,0,0,0.0%,,;"),
+ position("2015-10-27 01:15:31.000", true, 60.33830, 25.10323));
+
+ // Log on request
+ verifyNull(decoder, text(
+ "##,imei:359586015829802,A"));
+
+ // Heartbeat package
+ verifyNull(decoder, text(
+ "359586015829802"));
+
+ // No GPS signal
+ verifyNull(decoder, text(
+ "imei:359586015829802,tracker,000000000,13554900601,L,;"));
+
+ verifyPosition(decoder, text(
+ "imei:869039001186913,tracker,1308282156,0,F,215630.000,A,5602.11015,N,9246.30767,E,1.4,,175.9,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710040656622,tracker,13/02/27 23:40,,F,125952.000,A,3450.9430,S,13828.6753,E,0.00,0"));
+
+ verifyPosition(decoder, text(
+ "imei:359710040565419,tracker,13/05/25 14:23,,F,062209.000,A,0626.0411,N,10149.3904,E,0.00,0"));
+
+ verifyPosition(decoder, text(
+ "imei:353451047570260,tracker,1302110948,,F,144807.000,A,0805.6615,S,07859.9763,W,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:359587016817564,tracker,1301251602,,F,080251.000,A,3223.5832,N,11058.9449,W,0.03,"));
+
+ verifyPosition(decoder, text(
+ "imei:359587016817564,tracker,1301251602,,F,080251.000,A,3223.5832,N,11058.9449,W,,"));
+
+ verifyPosition(decoder, text(
+ "imei:012497000208821,tracker,1301080525,,F,212511.000,A,2228.5279,S,06855.6328,W,18.62,268.98,"));
+
+ verifyPosition(decoder, text(
+ "imei:012497000208821,tracker,1301072224,,F,142411.077,A,2227.0739,S,06855.2912,,0,0,"));
+
+ verifyPosition(decoder, text(
+ "imei:012497000431811,tracker,1210260609,,F,220925.000,A,0845.5500,N,07024.7673,W,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:100000000000000,help me,1004171910,,F,010203.000,A,0102.0003,N,00102.0003,E,1.02,"));
+
+ verifyPosition(decoder, text(
+ "imei:353451040164707,tracker,1105182344,+36304665439,F,214418.000,A,4804.2222,N,01916.7593,E,0.37,"));
+
+ verifyPosition(decoder, text(
+ "imei:353451042861763,tracker,1106132241,,F,144114.000,A,2301.9052,S,04909.3676,W,0.13,"));
+
+ verifyPosition(decoder, text(
+ "imei:359587010124900,tracker,0809231929,13554900601,F,112909.397,A,2234.4669,N,11354.3287,E,0.11,321.53,"));
+
+ verifyPosition(decoder, text(
+ "imei:353451049926460,tracker,1208042043,123456 99008026,F,124336.000,A,3509.8668,N,03322.7636,E,0.00,,"));
+
+ // SOS alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,help me,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Low battery alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,low battery,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Geo-fence alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,stockade,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Move alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,move,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ // Over speed alarm
+ verifyPosition(decoder, text(
+ "imei:359586015829802,speed,0809231429,13554900601,F,062947.294,A,2234.4026,N,11354.3277,E,0.00,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070010423167,tracker,1211051840,,F,104000.000,A,2220.6483,N,11407.6377,,0,0,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070010423167,tracker,1211051951,63360926,F,115123.000,A,2220.6322,N,11407.5313,E,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070010423167,tracker,1211060621,,F,062152.000,A,2220.6914,N,11407.5506,E,15.85,347.84,"));
+
+ verifyPosition(decoder, text(
+ "imei:863070012698733,tracker,1303092334,,F,193427.000,A,5139.0369,N,03907.2791,E,0.00,,"));
+
+ verifyPosition(decoder, text(
+ "imei:869039001186913,tracker,130925065533,0,F,065533.000,A,5604.11015,N,9232.12238,E,0.0,,329.0,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710041641581,acc alarm,1402231159,,F,065907.000,A,2456.2591,N,06708.8335,E,7.53,76.10,,1,0,0.03%,,"));
+
+ verifyPosition(decoder, text(
+ "imei:359710041641581,acc alarm,1402231159,,F,065907.000,A,2456.2591,N,06708.8335,E,7.53,76.10,,1,0,0.03%,,"));
+
+ verifyPosition(decoder, text(
+ "imei:313009071131684,tracker,1403211928,,F,112817.000,A,0610.1133,N,00116.5840,E,0.00,,,0,0,0.0,0.0,"));
+
+ verifyPosition(decoder, text(
+ "imei:866989771979791,tracker,140527055653,,F,215653.00,A,5050.33113,N,00336.98783,E,0.066,0"));
+
+ verifyPosition(decoder, text(
+ "imei:353552045375005,tracker,150401165832,61.0,F,31.0,A,1050.73696,N,10636.49489,E,8.0,,22.0,"));
+
+ verifyPosition(decoder, text(
+ "imei:353552045403597,tracker,150420050648,53.0,F,0.0,A,N,5306.64155,E,00700.77848,0.0,,1.0,;"));
+
+ verifyPosition(decoder, text(
+ "imei:353552045403597,tracker,150420051153,53.0,F,0.0,A,5306.64155,N,00700.77848,E,0.0,,1.0,;"));
+
+ verifyPosition(decoder, text(
+ "imei:359710047424644,tracker,150506224036,,F,154037.000,A,0335.2785,N,09841.1543,E,3.03,337.54,,0,0,45.16%,,;"));
+
+ verifyPosition(decoder, text(
+ "imei:865328023776874,acc off,150619152221,,F,072218.000,A,5439.8489,N,02518.5945,E,0.00,,,1,1,0.0,0.0,23.0,;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
new file mode 100644
index 000000000..23a05fcee
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Gps103ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodePositionPeriodic() throws Exception {
+
+ Gps103ProtocolEncoder encoder = new Gps103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 300);
+
+ assertEquals("**,imei:123456789012345,C,05m", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ Gps103ProtocolEncoder encoder = new Gps103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "H,080");
+
+ assertEquals("**,imei:123456789012345,H,080", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java
new file mode 100644
index 000000000..d35666b56
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java
@@ -0,0 +1,52 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GpsGateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GpsGateProtocolDecoder decoder = new GpsGateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$FRCMD,0097,_SendMessage,,7618.51990,S,4002.26182,E,350.0,1.08,0.0,250816,183522.000,0*7F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,356406061385182,_SendMessage,,5223.88542,N,11440.45866,W,951.2,0.027,,220716,153507.00,1*5F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,353067011068246,_SendMessage,,1918.1942,N,09906.3696,W,2246.5,000.0,295.9,150416,213147.00,1,Odometer=*70"));
+
+ verifyNull(decoder, text(
+ "$FRCMD,862950025974620,_Ping,voltage=4*4F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,862950025974620,_SendMessage, ,2721.5781,S,15259.145,E,61,0.00,61,080316,092612,1,SosButton=0,voltage=4*60"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,,user1,8IVHF*7A"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,,354503026292842,VGZTHKT*0C"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,IMEI,1234123412341234,*7B"));
+
+ verifyNull(decoder, text(
+ "$FRLIN,,saab93_device,KLRFBGIVDJ*28"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,154403.000,A,6311.64120,N,01438.02740,E,0.000,0.0,270707,,*0A"),
+ position("2007-07-27 15:44:03.000", true, 63.19402, 14.63379));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,074524,A,5553.73701,N,03728.90491,E,10.39,226.5,160614,0.0,E*75"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,154403.000,A,6311.64120,N,01438.02740,E,0.000,0.0,270707,,*0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java
new file mode 100644
index 000000000..d6ba11cb7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+
+public class GpsMarkerProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GpsMarkerProtocolDecoder decoder = new GpsMarkerProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GM23D863071014445404T260816142611N55441051E037325071033063C0530304#"));
+
+ verifyNull(decoder, text(
+ "$GM300350123456789012T100511123300G25000001772F185200000000000000005230298#"));
+
+ verifyPosition(decoder, text(
+ "$GM200350123456789012T100511123300N55516789E03756123400000035230298#"),
+ position("2011-05-10 12:33:00.000", true, 55.86132, 37.93539));
+
+ verifyPosition(decoder, text(
+ "$GM1350123456789012T1005111233N55516789E03756123400000035200298#"));
+
+ verifyPosition(decoder, text(
+ "$GM203863071014445404T150715202258N55481576E03729275300000040530301#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java
new file mode 100644
index 000000000..438d0fb3a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GpsmtaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GpsmtaProtocolDecoder decoder = new GpsmtaProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "3085a94ef459 1446536867 49.81621 24.054207 1 0 22 0 10 12 24 0 0"));
+
+ verifyPosition(decoder, text(
+ "864528021249771 1446116686 49.85073 24.004438 0 217 6 338 00 59 27 0 0"));
+
+ verifyNotNull(decoder, text(
+ "359144048138856 1442932957 -49.85064 -24.003979 1 0 40 0 10 110 26 0 0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java
new file mode 100644
index 000000000..7c4bb06c2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class GranitFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GranitFrameDecoder decoder = new GranitFrameDecoder();
+
+ assertEquals(
+ binary("2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"),
+ decoder.decode(null, null, binary("2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a45330d0a")));
+
+ assertEquals(
+ binary("2b525243427e1a000d0a34757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"),
+ decoder.decode(null, null, binary("2b525243427e1a000d0a34757c57b8b03c38d279b4e61e9bd7006b000000001c00002a45330d0a")));
+
+ assertEquals(
+ binary("4f4b"),
+ decoder.decode(null, null, binary("4f4b0d0a2b525243427e1a000d0a34757c57b8b03c38d279b4e61e9bd7006b000000001c00002a45330d0a")));
+
+ assertEquals(
+ binary("2b444441547e84003e290401d01690737c57b8903c383c7fa0e5081b64006b000000001c0000b8803c388e7fe7e5102197006c000000001c0000b8813c38ad7f02e6042035006c000000001d0000b8813c38bf7f13e6001d1e006c000000001d0000b8813c38bf7f13e6001d00006c000000001d0000b8903c38977f34e6091065006c000000001e000014002a3932"),
+ decoder.decode(null, null, binary("2b444441547e84003e290401d01690737c57b8903c383c7fa0e5081b64006b000000001c0000b8803c388e7fe7e5102197006c000000001c0000b8813c38ad7f02e6042035006c000000001d0000b8813c38bf7f13e6001d1e006c000000001d0000b8813c38bf7f13e6001d00006c000000001d0000b8903c38977f34e6091065006c000000001e000014002a39320d0a")));
+
+ assertEquals(
+ binary("2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a4346"),
+ decoder.decode(null, null, binary("2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a43460d0a")));
+
+ assertEquals(
+ binary("2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a3231"),
+ decoder.decode(null, null, binary("2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a32310d0a")));
+
+ assertEquals(
+ binary("4552524f522057524f4e4720434845434b53554d5f31"),
+ decoder.decode(null, null, binary("4552524f522057524f4e4720434845434b53554d5f310d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java
new file mode 100644
index 000000000..fc75b2a53
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java
@@ -0,0 +1,49 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class GranitProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ GranitProtocolDecoder decoder = new GranitProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "2b444441547e8400c500040130050c43495808002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000014002a37420d0a"));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a00c5008443495808002839aee315020000000064000000000000002a37410d0a"),
+ position("2016-12-08 11:27:00.000", false, 57.00888, 40.97143));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a00c500ec904858b842283997e30002000000005e000000000d00002a32390d0a"),
+ position("2016-12-07 22:45:00.000", true, 57.00853, 40.97105));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a00c500009148580800283997e30002000000005f000000000000002a33410d0a"));
+
+ verifyPositions(decoder, binary(
+ "2b444441547e84003b6d0401b10e9217445800b051398f35d34a313b000072000000010b000080b051398f35d34a313b000072000000010b0000f0b051390f33314c303b900371000000010b0000f0b05139cd31e54c2f3cd0016f000000010b0000f0b051396831204d303d950071000000010b0000f0b051397530aa4d323c610171000000010b00000a002a30420d0a"));
+
+ verifyPosition(decoder, binary(
+ "2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"));
+
+ verifyPositions(decoder, binary(
+ "2b444441547e84003e290401d01690737c57b8903c383c7fa0e5081b64006b000000001c0000b8803c388e7fe7e5102197006c000000001c0000b8813c38ad7f02e6042035006c000000001d0000b8813c38bf7f13e6001d1e006c000000001d0000b8813c38bf7f13e6001d00006c000000001d0000b8903c38977f34e6091065006c000000001e000014002a3932"));
+
+ verifyPositions(decoder, binary(
+ "2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a4346"));
+
+ // +IDNT: Navigator.04x Firmware version 0712GLN *21
+ verifyAttributes(decoder, binary(
+ "2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a3231"));
+
+ // ERROR WRONG CHECKSUM_1
+ verifyAttributes(decoder, binary(
+ "4552524f522057524f4e4720434845434b53554d5f31"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java
new file mode 100644
index 000000000..f9b8f6509
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java
@@ -0,0 +1,43 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gt02ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt02ProtocolDecoder decoder = new Gt02ProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "6868150000035889905895258400831c07415045584f4b210d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68682d0000035889905895258400951c1f415045584572726f723a20506172616d65746572203120284f4e2f4f4646290d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68680f0504035889905831401700df1a00000d0a"));
+
+ verifyAttributes(decoder, binary(
+ "6868130504035889905831401700001a040423261e290d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68681905040358899058314017000e1a010a2623211b2722252329000d0a"));
+
+ verifyAttributes(decoder, binary(
+ "68681a060303588990500037252de91a010a171a191b171915191e10000d0a"));
+
+ verifyPosition(decoder, binary(
+ "68682500000123456789012345000110010101010101026B3F3E026B3F3E000000000000000000010D0A"),
+ position("2001-01-01 01:01:01.000", true, -22.54610, -22.54610));
+
+ verifyAttributes(decoder, binary(
+ "6868110603035889905101276600001a0402292d0d0a"));
+
+ verifyPosition(decoder, binary(
+ "68682500a403588990510127660001100e09060a1d1b00ade1c90b79ea3000011b000000000000050d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java
new file mode 100644
index 000000000..cf6d1cfd7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gt06FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt06FrameDecoder decoder = new Gt06FrameDecoder();
+
+ verifyFrame(
+ binary("787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A"),
+ decoder.decode(null, null, binary("787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A")));
+
+ verifyFrame(
+ binary("78780d0103563140414198583c0d0a"),
+ decoder.decode(null, null, binary("78780d0103563140414198583c0d0a")));
+
+ verifyFrame(
+ binary("787800691709261259400700cc0400d376714600d37a3d5000d37a3c5000d393505a00d3765d5a00d376735a00d32e6b640d0a"),
+ decoder.decode(null, null, binary("787800691709261259400700cc0400d376714600d37a3d5000d37a3c5000d393505a00d3765d5a00d376735a00d32e6b640d0a")));
+
+ verifyFrame(
+ binary("7878121011091c0b1e2e98058507f80097a6ac03344a0d0a"),
+ decoder.decode(null, null, binary("7878121011091c0b1e2e98058507f80097a6ac03344a0d0a")));
+
+ verifyFrame(
+ binary("787808171709281135331491827b75594dc8d719a9708452cad719a9708550cad719a97086521491827b75574cac9e17b308085dc8d71939633947cad71939633a480700cc0400d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a0d0a"),
+ decoder.decode(null, null, binary("787808171709281135331491827b75594dc8d719a9708452cad719a9708550cad719a97086521491827b75574cac9e17b308085dc8d71939633947cad71939633a480700cc0400d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a0d0a")));
+
+ verifyFrame(
+ binary("787808134606020002044dc5050d0a"),
+ decoder.decode(null, null, binary("787808134606020002044dc5050d0a")));
+
+ verifyFrame(
+ binary("78781f1210020e14061dcc0476fcd0003e3faf3e14b20000000000000000044ef6740d0a"),
+ decoder.decode(null, null, binary("78781f1210020e14061dcc0476fcd0003e3faf3e14b20000000000000000044ef6740d0a")));
+
+ verifyFrame(
+ binary("78780d010352887071911998000479d00d0a"),
+ decoder.decode(null, null, binary("78780d010352887071911998000479d00d0a")));
+
+ verifyFrame(
+ binary("78782516000000000000c000000000000000000020000900fa0210ef00fb620006640301000468030d0a"),
+ decoder.decode(null, null, binary("78782516000000000000c000000000000000000020000900fa0210ef00fb620006640301000468030d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
new file mode 100644
index 000000000..672711f22
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -0,0 +1,330 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Gt06ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt06ProtocolDecoder decoder = new Gt06ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "787805120099abec0d0a"));
+
+ verifyNull(decoder, binary(
+ "78780D01086471700328358100093F040D0A"));
+
+ verifyAttributes(decoder, binary(
+ "7878711213081f081d0fc6017ba3fa0ac62a923e550e02080503f300b26d000000004b20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202030300017c7470d0a"));
+
+ verifyAttributes(decoder, binary(
+ "797900B2700000000102003500010400330012000000000000000000000000000000000000003400061354A48DFF00003400061154A48E56000011000A000000000000000000000001000803537601000282180002000802140743044211890003000A89340752000038689636001800020182002B000116002C000454A4FF350009000100000A0001010028000100002E000400000000002A00010000290004000000000030000A000101680014016802D00000B38F0D0A"));
+
+ verifyAttribute(decoder, binary(
+ "78780c95130a071223200100013ad10d0a"),
+ Position.KEY_ALARM, Position.ALARM_GENERAL);
+
+ verifyAttribute(decoder, binary(
+ "78783c3400000000130a1906011105029b4d450b1828d5001c00000000009e7d014e140fc000004e990000000004c301a442210000020101c001c0000591aa0d"),
+ Position.PREFIX_ADC + 1, 4.48);
+
+ verifyAttribute(decoder, binary(
+ "797900a87000000001020035000101003300125d7e3a180600d504b598f708814b3a001d1500340006125d7e39dc000011000a012e02620000000000000001000803537601000129800002000803102608593397620003000a89012608522933976266001800020172002b000114002c00045d7df3c70009000106000a000109002800010d002e00040000f25d002a000111002900040000017e0030000a000100b4000a00b402d0000591250d0a"),
+ Position.KEY_ALARM, Position.ALARM_REMOVING);
+
+ verifyPosition(decoder, binary(
+ "797900a87000000001020035000100003300125d62bf3a0800e804b5994308814a87001d5d00340006115d62bf29000011000a012e02620000000000000001000803537601000129800002000803102608593397620003000a8901260852293397626600180002017d002b000116002c00045d6278ea0009000108000a00010b002800010b002e00040000f0c1002a00010000290004000000be0030000a000100b4000a00b402d00006c5490d0a"));
+
+ verifyAttribute(decoder, binary(
+ "797900149b03023539303042343843454238410300139ba40d0a"),
+ Position.KEY_DRIVER_UNIQUE_ID, "5900B48CEB");
+
+ verifyPosition(decoder, binary(
+ "787821121303120b2524c70138e363085b549003d43301940057d200cd52c000006aa1ca0d0a"));
+
+ verifyAttribute(decoder, binary(
+ "7878251613020C12141AC5027951430C2A16F60014000900000000001A00007C550300020002F1E70D0A"),
+ Position.KEY_ALARM, Position.ALARM_REMOVING);
+
+ verifyNotNull(decoder, binary(
+ "7878006919012105090303028f01007549e05a00bc9c5c5a007a8d1a5a0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878001719011910543607028f0100bc2e695a00bcb3635a00bc27c56400bc447b6400bc46c96400bc33ce6400bc64ca640d0a"));
+
+ verifyAttributes(decoder, binary(
+ "797900de8c120b1502121b013137333d302c3232333d312c3238333d30303030303030302c3436333d30303030303035362c3437333d30303030303030662c3438333d30303030303031312c3242333d30303030303030302c3244333d30303030313839632c3335333d30303030313661382c3336333d30303032386163382c3339333d30303030303230612c3330333d30303030303561612c3439333d30303030303030302c3441333d4b4e4146323431434d4b353031343235352c3341333d30303030303338352c3530333d30303030c0041df0940f89c06700000000c800910d0a"));
+
+ verifyAttributes(decoder, binary(
+ "79790020940A035985708053908307060104900402788950301217070401538F0003E8210D0A"));
+
+ verifyPosition(decoder, binary(
+ "78783c3439000000120a0902093a07031f9e690529be2e00155500000016214901a30308b70000b3fb004aa82b059401a3422100000001000000007d9370b90d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7878079404eb001c705d0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78781219028f0a29e10036f12003040102000875dc0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "79790008940000ed0289d6860d0a"));
+
+ verifyNull(decoder, binary(
+ "797900a59404414c4d313d34353b414c4d323d44353b414c4d333d35353b535441313d34303b4459443d30313b534f533d303538353036313536372c2c3b43454e5445523d3b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c303b49434349443d38393937313033313031303038393539303432463b4d4f44453d4d4f44452c312c3138303b0008f65e0d0a"));
+
+ verifyPosition(decoder, binary(
+ "78782222120616083817c5050cc8c801a819d600152400e8011dbf003332000000004862500d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780B23C00122040001000818720D0A"));
+
+ verifyNotNull(decoder, binary(
+ "78782EA4110C0C02281BF6026C18720C38D22800149C1181CC00010000260E000000000615F8012C05041102FF001058FD0D0A"));
+
+ verifyNotNull(decoder, binary(
+ "78787aa2110c0e06372c813601040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c01040000591200000000009d7c0104ff02001801eb4039d10000000000000004fabeb50d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782727110c0b0e170f850450059107f461ae001c7e0a81360104cb8a00bef32806030c02ff000316b10d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782322110c070f1b0270000000000000000000040081360104cb8a00bef3000000000523030d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787880a2110b161010140136040000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c01020000591200000000009d7c0102ff033c1e04ddc28aa6001801eb4039c90000000000000014db84730d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782ba701cc000000919100000000090617032b3836313832323038343735363200000000000000000100049fb60d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787819a501cc0000009191000000000906170304050400010005f44d0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78782ca3110b10081336f000000000000000000004003901cc0000009191000000000906170304050400010003e0940d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878A3A2110B0603201501CC010000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F804000000254E000000000615F80400FF08F483CD4DF4C0D750BD5F8BC5CECFB0D59DAFB459CDA8574E8424C6CC50BD5F6C7E1CC9B0D59D8AA718C90087363040E0C83496727B4DE4C7002919670D0A"));
+
+ verifyNotNull(decoder, binary(
+ "78786CA1110B0413093801CC01000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201000025FC000000000618C10201FF0002000541D70D0A"));
+
+ verifyPosition(decoder, binary(
+ "787822220F0C1D023305C9027AC8180C46586000140001CC00287D001F71000001000820860D0A"));
+
+ verifyAttributes(decoder, binary(
+ "797900262100000000020043006f006d006d0061006e00640020006500720072006f0072002100236e850d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A"));
+
+ verifyNotNull(decoder, binary(
+ "787800691710231108500200cc080c4e2fa5640c4e2fa66e0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800171710231108290200cc080c4e2fa5640c4e2fa5640d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800691710231109200400cc080c4e2fa55a0c4ec0025a0c4e2fa6640c583918640d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800691710231111210700cc080c4e2fa55a0c4ec0025a0c4e39295a0c583918640c4e2fa6640c4e2fa4640c4ec854640d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800171710231112510600cc080c4e2fa55a0c4e2fa55a0c4e2fa55a0c4e2fa55a0c4e2fa55a0c4e2fa55a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "7878121011091c0b1b2999058508040097a89e0034520d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78780869170928113413ac9e17b30808514494fcf6e148596cb0ce2c67bd4a6eb0ce2c67bd4b0018e7d4333e55ec086be7f2df5fe48d8c94fc6657e48d8cb8f378510600cc0400d37a3d4600d37a3c5000d37a3b6400d376716400d305ac6400d393506e0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787808171709281135331491827b75594dc8d719a9708452cad719a9708550cad719a97086521491827b75574cac9e17b308085dc8d71939633947cad71939633a480700cc0400d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a00d37a3d5a0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78783b281108111002050136041bcf0000bf09000000000000000000000000000000000000000000000000000000000000000000000000ff00020007d3280d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878412c11030b011c1f013604cb8a00b17754cb8a00bef357cb8a00b73f5fcb8900b0e25fcb8900b6655fcb8a00b74960cb8a00b178620701001801eb40393800bbbde10d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878412c11030b012629013604cb8a00b17757cb8a00b73f5bcb8a00b7495ecb8900b0e25fcb8a00b1b9620000000000ff0000000000ffff01001801eb40393e00c0e6340d0a"));
+
+ verifyPosition(decoder, binary(
+ "787822221106160a1016c60278019407c7783800040001940504700046fc01030100065f570d0a"));
+
+ verifyAttributes(decoder, binary(
+ "797900143311070609020b00000000a0030046000109e4610d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979003e32110707083819000901fe0a060f006a1e3f24000000000000000000000000000000000000000000000000000000000000000000000000000000012116ba0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979007632110706090217000901fe0a060f006a1c2024060f0053a429060f006a1d21060f0053a720060f006f151d0000000000000000000000003844d9e7f7e1773d60e327a9e442405cf28628b9c640a42bb0fc0d0244d855a38c220a4c802aa8da7dab50b0e235ef32dd5348ee0ce77a52540000010a205a0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979006f210000000001426174746572793a352e3536562c4e4f524d414c3b20475052532a4c696e6b2055702047534d205369676e616c204c6576656c3a5374726f6e673b204750533a4f46463b2020204c4f434b3a4f46463b204254204d41433a4234413832383034343436323b007260880d0a"));
+
+ verifyPosition(decoder, binary(
+ "7979004a321106170c1b180cc900a875580b7ab4f00010350901fe0a007c0009112424007c000912240081004efe2100c500100f1200000000000000000000000000000000000000000000bc7c900d0a"));
+
+ verifyNotNull(decoder, binary(
+ "79790045321106170c1b13000901fe0a007c0009112424007c000912230081004efe1e00c500100f120000000000000000000000000000000000000710bef565574e37000000b26f140d0a"));
+
+ verifyNull(decoder, binary(
+ "787811010863586038760942a0010000010aa4000d0a"));
+
+ verifyNull(decoder, binary(
+ "78781f3511061a0b24330503107d06084889cb01000100000cfa20e3acd301333fcb0d0a"));
+
+ verifyPosition(decoder, binary(
+ "78783c340000000011061809130c0903107d2408488a5800144c00000000000001940b00b1000047ff000000000500018f42210000000100050003010b69450d0a"));
+
+ verifyPosition(decoder, binary(
+ "78783c34000000001106190336070903107d51084889b900152e0000000043b101940b00b10000480100000000050001a3422100000001000300011bdc7b5f0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780a13c40604000201298f5b0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78781f12110616091835d0024bb93007d3fb783dd4c501940500f2006c8504a6e0370d0a"));
+
+ verifyPosition(decoder, binary(
+ "787822221106160a1016c60278019407c7783800040001940504700046fc01030100065f570d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878661500000000004459443d537563636573732100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010009e82b0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979000894000000000338ba0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "79790020940a03516080825457290502194200448892980691312079088572f50004d4350d0a"));
+
+ verifyPosition(decoder, binary(
+ "7979007121000000000143757272656e7420706f736974696f6e214c61743a4e35342e3733393333322c4c6f6e3a4532352e3237333237302c436f757273653a3132362e35332c53706565643a302e303030302c4461746554696d653a323031372d30352d3236202031303a32373a3437000bbee30d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979003F940D110315102A202141494F494C2C30322C3030382E3239302C3032392E3630302C3531394A2C303430302C3030382E3433302C302C30302C4142001678EA0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "79790039940d2141494f494c2c30322c3030322e3732302c3032392e3530302c3532344a2c303130302c3030332e3430302c302c30302c393309ad72000d0a"));
+
+ verifyNull(decoder, binary(
+ "79790005840016BB1A0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "797900089400000002e852d70d0a"));
+
+ verifyAttributes(decoder, binary(
+ "7979000794050000c9b63e0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "78783b18100c0f1201010195271784005ab63617840002fa47178400ff8f4817840019f3491784005ab54b178400ff8e4c17840019f24cff0002012287c80d0a"));
+
+ verifyPosition(decoder, binary(
+ "7878251610051b0f1c34c5022515d504b5dcd20738080902d4022bdf009cba5006640201006759680d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787866150000000000416c726561647920696e20746865207374617465206f66206675656c20737570706c7920746f20726573756d652c74686520636f6d6d616e64206973206e6f742072756e6e696e672100000000000000000000000000000000000001001981e50d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78782d152500000000437574206f666620746865206675656c20737570706c793a2053756363657373210002013b898a0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "787829152100000000526573746f7265206675656c20737570706c793a2053756363657373210002014077ce0d0a"));
+
+ verifyNull(decoder, binary(
+ "78780D01012345678901234500018CDD0D0A"));
+
+ verifyNull(decoder, binary(
+ "78780d0103534190360660610003c3df0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780a13440604000201baaf540d0a"));
+
+ verifyAttributes(decoder, binary(
+ "787825160F0C1D0A2B21C8027AC8040C46581000146F0901CC00287D001F714804040301001C84CF0D0A"));
+
+ verifyPosition(decoder, binary(
+ "78781f120f0a140e150bc505e51e780293a9e800540000f601006e0055da00035f240d0a"),
+ position("2015-10-20 14:21:11.000", true, 54.94535, 24.01762));
+
+ verifyPosition(decoder, binary(
+ "787823120f081b121d37cb01c8e2cc08afd3c020d50201940701d600a1190041ee100576d1470d0a"));
+
+ verifyPosition(decoder, binary(
+ "78781F120B081D112E10CC027AC7EB0C46584900148F01CC00287D001FB8000380810D0A"));
+
+ verifyPosition(decoder, binary(
+ "787819100B031A0B1B31CC027AC7FD0C4657BF0115210001001CC6070D0A"));
+
+ verifyPosition(decoder, binary(
+ "787821120C010C0F151FCF027AC8840C4657EC00140001CC00287D001F720001000F53A00D0A"));
+
+ verifyPosition(decoder, binary(
+ "787825160B051B093523CF027AC8360C4657B30014000901CC00266A001E1740050400020008D7B10D0A"));
+
+ verifyPosition(decoder, binary(
+ "787819100e010903230ec803ae32a60653cded00180000020072feb70d0a"));
+
+ verifyPosition(decoder, binary(
+ "7878471e0e03110b0511c501c664fd074db73f0218a602e003433a002fed40433a0056e14e433a0056104e433a0056fd53433a002eed55433a007e4b57433a002ee25aff00020120f6720d0a"));
+
+ verifyNull(decoder, binary(
+ "7979005bfd0358899050927725004c0020bf984358df603b2ea3a339e54335013a5b56455253494f4e5d47543036445f32305f3630444d325f423235455f5631355f574d5b4255494c445d323031332f31322f32382031353a3234002a3b240d0a7979005bfd0358899050927725004c0020bf984358df603b2ea3a339e54335013a5b56455253494f4e5d47543036445f32305f3630444d325f423235455f5631355f574d5b4255494c445d323031332f31322f32382031353a3234002d4f9b0d0a7979005bfd0358899050927725004c0020bf984358df603b2ea3a339e54335013a5b56455253494f4e5d47543036445f32305f3630444d325f423235455f5631355f574d5b4255494c445d323031332f31322f32382031353a3234003084ff0d0a"));
+
+ verifyNull(decoder, binary(
+ "78788b818300000000534545464e2626004f04220045042626262b37393035343031353534362626262626260410041b0415041a04210415041926262b373930363433333031313526260410043d044f26262b373936303437383430363426260412043e0432043026262b373932383834373738383126262626262626262626262626262626232300020022155d0d0a"));
+
+ verifyPosition(decoder, binary(
+ "787822220e0914160f07c9021a362805090a7800d8b802d402c30e00a98a0105010213f4bb0d0a"));
+
+ verifyNull(decoder, binary(
+ "787811010864717003664467100f190a0002c6d20d0a"));
+
+ verifyNull(decoder, binary(
+ "787811010123456789012345100B3201000171930D0A"));
+
+ verifyNull(decoder, binary(
+ "78780d1f000000000000000200b196a20d0a"));
+
+ verifyPosition(decoder, binary(
+ "78781f12110819110216d402f250340828924055d4c801944600d300c09501429c830d0a"));
+
+ verifyPosition(decoder, binary(
+ "78782516110819110208d402f264dc08289a4058d4c70901944600d300c0954606040600014057e90d0a"));
+
+ verifyNull(decoder, binary(
+ "78780d010359339075005244340d0a"));
+
+ verifyNotNull(decoder, binary(
+ "787800691709261259400700cc0400d376714600d37a3d5000d37a3c5000d393505a00d3765d5a00d376735a00d32e6b640d0a"));
+
+ verifyNull(decoder, binary(
+ "787801080d0a"));
+
+ verifyNull(decoder, binary(
+ "78782E2A0F0C1D071139CA027AC8000C4658000014D83132353230313335333231373730373900000000000001002A6ECE0D0A"));
+
+ verifyNull(decoder, binary(
+ "7878058A000688290D0A"));
+
+ verifyAttribute(decoder, binary(
+ "78780c95130a0209321c90000112800d0a"),
+ Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+
+ verifyAttribute(decoder, binary(
+ "78780c95130a0209321c90000112800d0a"),
+ "alarmValue", 1);
+
+ verifyAttribute(decoder, binary(
+ "78780c95130a0209321c91000112800d0a"),
+ Position.KEY_ALARM, Position.ALARM_BRAKING);
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java
new file mode 100644
index 000000000..178c7b763
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class Gt06ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ Gt06ProtocolEncoder encoder = new Gt06ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("787812800c0000000052656c61792c312300009dee0d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java
new file mode 100644
index 000000000..cb792ba09
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Gt30ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Gt30ProtocolDecoder decoder = new Gt30ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$005D3037811014 9955102834.000,A,3802.8629,N,02349.7163,E,0.00,,060117,,*13|1.3|26225BD"));
+
+ verifyPosition(decoder, text(
+ "$$005E3037811014 9999\u0003121909.000,A,3802.9133,N,02349.9354,E,0.00,,060117,,*18|1.8|264518B"));
+
+ verifyPosition(decoder, text(
+ "$$00633037811014 9999\u0002121901.000,A,3802.9137,N,02349.9334,E,2.86,18.16,060117,,*3E|1.8|262D752"));
+
+ verifyPosition(decoder, text(
+ "$$005E3037811014 9999\u0001121849.000,A,3802.9094,N,02349.9384,E,0.00,,060117,,*1C|1.2|2683812"));
+
+ verifyPosition(decoder, text(
+ "$$005B3037811124 9955161049.000,A,3802.9474,N,02241.1897,E,0.00,,021115,,*15|2.9|5A639"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java
new file mode 100644
index 000000000..bdb2ff37b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java
@@ -0,0 +1,71 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class H02FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeShort() throws Exception {
+
+ H02FrameDecoder decoder = new H02FrameDecoder(0);
+
+ assertEquals(
+ binary("2a48512c3335353438383032303131333931312c56312c3031323934352c412c353233312e37393238332c4e2c30313332342e31303731382c452c302e30352c302c3137303231372c464646464646464623"),
+ decoder.decode(null, null, binary("2a48512c3335353438383032303131333931312c56312c3031323934352c412c353233312e37393238332c4e2c30313332342e31303731382c452c302e30352c302c3137303231372c4646464646464646230d0a")));
+
+ assertEquals(
+ binary("2441060116601245431311165035313006004318210e000000fffffbffff0024"),
+ decoder.decode(null, null, binary("2441060116601245431311165035313006004318210e000000fffffbffff0024")));
+
+ assertEquals(
+ binary("2441060116601245431311165035313006004318210e000000fffffbffff0024"),
+ decoder.decode(null, null, binary("2441060116601245431311165035313006004318210e000000fffffbffff00242a48512c343130363031313636302c56312c3132343535322c412c353033352e333132392c4e2c30303433312e383231312c452c3030302e32302c3030302c3133313131362c464646464642464623")));
+
+ assertEquals(
+ binary("2a48512c3335333538383036303031353536382c56312c3139333530352c412c3830392e303031302c532c333435342e383939372c572c302e30302c302e30302c3239313031332c65666666666266662c3030303264342c3030303030622c3030353338352c3030353261612c323523"),
+ decoder.decode(null, null, binary("2a48512c3335333538383036303031353536382c56312c3139333530352c412c3830392e303031302c532c333435342e383939372c572c302e30302c302e30302c3239313031332c65666666666266662c3030303264342c3030303030622c3030353338352c3030353261612c323523")));
+
+ assertEquals(
+ binary("24430025645511183817091319355128000465632432000100ffe7fbffff0000"),
+ decoder.decode(null, null, binary("24430025645511183817091319355128000465632432000100ffe7fbffff0000")));
+
+ }
+
+ @Test
+ public void testDecodeLong() throws Exception {
+
+ H02FrameDecoder decoder = new H02FrameDecoder(0);
+
+ assertEquals(
+ binary("24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f"),
+ decoder.decode(null, null, binary("24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f")));
+
+ }
+
+ @Test
+ public void testDecodeAlternative() throws Exception {
+
+ H02FrameDecoder decoder = new H02FrameDecoder(0);
+
+ assertEquals(
+ binary("2a48512c343230363131393133302c4e42522c3130323430332c3233382c312c302c372c313131312c323236342c36332c313131312c323236352c35382c313131312c323236362c35302c313131312c333133352c33372c313131312c3630352c33332c313131312c343932302c33302c313131312c3630372c32382c3131303131372c46464646444646462c3623"),
+ decoder.decode(null, null, binary("2a48512c343230363131393133302c4e42522c3130323430332c3233382c312c302c372c313131312c323236342c36332c313131312c323236352c35382c313131312c323236362c35302c313131312c333133352c33372c313131312c3630352c33332c313131312c343932302c33302c313131312c3630372c32382c3131303131372c46464646444646462c3623")));
+
+ assertEquals(
+ binary("2442061191301024031101175540227006012321670c000095fffffbffff001f00000001f800ee010000000032"),
+ decoder.decode(null, null, binary("2442061191301024031101175540227006012321670c000095fffffbffff001f00000001f800ee010000000032")));
+
+ assertEquals(
+ binary("5800009814991024031101175540227006012321670c000095fffffbffff0033"),
+ decoder.decode(null, null, binary("5800009814991024031101175540227006012321670c000095fffffbffff0033")));
+
+ assertEquals(
+ binary("2a48512c343230363131393133302c4e42522c3130323431362c3233382c312c302c372c313131312c323236342c35332c313131312c323236352c36302c313131312c323236362c34342c313131312c333133352c34332c313131312c3630352c33392c313131312c343932302c32392c313131312c3630372c32342c3131303131372c46464646464246462c3623"),
+ decoder.decode(null, null, binary("2a48512c343230363131393133302c4e42522c3130323431362c3233382c312c302c372c313131312c323236342c35332c313131312c323236352c36302c313131312c323236362c34342c313131312c333133352c34332c313131312c3630352c33392c313131312c343932302c32392c313131312c3630372c32342c3131303131372c46464646464246462c3623")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
new file mode 100644
index 000000000..fe4988b5a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -0,0 +1,279 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class H02ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ H02ProtocolDecoder decoder = new H02ProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "*HQ,9180271064,V5,091233,V,2348.8912,N,09021.3302,E,000.00,000,051219,FFFFBBFF,470,01,21019,2033,2921283#"));
+
+ verifyPosition(decoder, binary(
+ "2491802711800850240512192350143206090249758e000001ffffbbff00bdf0900000000001d60161cc4b9a35"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,135790246811220,HTBT#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,865205035331981,V1,132926,A,1935.3933,N,07920.4134,E, 3.34,342,280519,FFFFFFFF#"));
+
+ verifyPosition(decoder, binary(
+ "24702802061601234020031910125482600612695044000000ffffbbff000000000000000001760d04e2c9934d"));
+
+ verifyNotNull(decoder, buffer(
+ "*hq,356327081001239,VP1,V,470,002,92,3565,0Y92,19433,30Y92,1340,29#"));
+
+ verifyPosition(decoder, binary(
+ "2435248308419329301047591808172627335900074412294E024138FEFFFFFFFF01120064BA73005ECC"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,054048,A,2828.2297,N,07733.4332,E,000.5,047,4,080918,EEE7FBDF,4261193,0#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505221264507,V2,100220,0,5238.26259,N,00507.33983,E,0.25,0,280917,FFFFFFFF,cc,28, db,d75b#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,,V1,173212,A,2225.78879,S,02829.19021,E,0.00,0,290418,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505221264507,VI1,075146,0,5238.25900,N,00507.33429,E,0.54,0,250917,FFFFFFFF,cc,28, db,d75b#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,353505510948929,V1,,V,,N,,E,0.00,0,,FFFFF7FF,f0,a,11a0,c0c6#"));
+
+ verifyPosition(decoder, buffer(
+ "*hq,356327080425330,VP1,A,2702.7245,S,15251.9311,E,0.48,0.0000,080917#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4209951296,V19,214452,A,5201.0178,N,01830.5029,E,000.00,000,200417,,195.63.13.195,89480610500392633029,BFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*hq,356327080425330,VP1,A,2702.7215,S,15251.9309,E,0.62,0.0000,050917#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,356327080425330,XT,1,100#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,353111080001055,V3,044855,28403,01,001450,011473,158,-62,0292,0,X,030817,FFFFFBFF#"));
+
+ verifyPosition(decoder, binary(
+ "2442091341332059572807175137358006000183640e000000fffffbfdff001f080000000000ea1e0000000021"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,4109198974,#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1700086468,LINK,180902,15,0,84,0,0,240517,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020882405,V3,095426,74001,01,010278,045142,128,-92,02DE,0,X,090517,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020882405,V3,095426,74001,04,010278,045142,128,-92,010278,026311,125,,010278,026582,125,,010278,028322,119,,02DD,0,X,090517,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4109179024,V19,181519,V,3853.2587,S,06205.9175,W,000.00,000,090217,,5492932630888,8954315265044716555?,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,8161289587,V1,181933,A,5444.3994,N,02516.3844,E,000.05,000,090317,FFFFBBFF,246,03,00002,41565#"));
+
+ verifyPosition(decoder, binary(
+ "2421305109380127171003170520046500100286297e003085ffffdfffff03440069129344006400001151415a20"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,2130510938,V1,012632,A,0520.0663,N,10028.6324,E,0.286,023,100317,FFFFDFFF,69129336,0,100.0,18,5141,5A20#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,201812,A,2608.9437,N,08016.2521,W,000.80,000,150317,EFE7F9FF,310,260,0,0,6#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,201844,A,2608.9437,N,08016.2521,W,000.80,000,150317,FFFFF9FF,310,260,0,0,6#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4109179024,V19,103732,V,3853.2770,S,06205.8678,W,000.00,000,100217,,5492932630888,8954314165044716555?,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,4109179024,NBR,103732,722,310,0,6,8106,32010,23,8101,22007,25,8106,12010,23,8106,22105,22,8101,22012,16,8106,42010,5,100217,FFFFFBFF,5#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020930796,V3,002339,62160,06,024852,035421,148,0,024852,035425,143,,022251,036482,137,,024852,000335,133,,024852,031751,133,,024852,035423,133,,02A1,0,X,010104,EFE7FBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4106012736,V1,224434,A,1827.3855,N,06705.7577,W,000.00,000,100117,FFFFFBFF,310,260,49101,1753,5#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,4208150188,NBR,210249,260,6,0,7,1014,50675,37,1014,50633,27,1014,17933,18,1014,17231,15,1014,50632,12,1014,13211,11,1014,17031,10,281216,FFFFFBFF,2#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,7401004662,NBR,160453,234,10,0,2,21580,27766,-67,21580,27766,-67,291019,FFFFFBFF,6#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068812,NBR,141335,262,02,255,6,431,17003,26,431,11101,13,431,6353,13,431,22172,13,431,11093,13,431,60861,10,151216,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353588020068342,V1,084436,A,3257.01525,N,00655.03865,W,57.78,40,011216,FFFBFFFF,25c,a, 154,b04c#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,356803210091319,BS,,2d4,a,1b63,1969,26,1b63,10b2,31,0,0,25,,ffffffff,60#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1400046168,NBR,160169,460,0,1,4,9338,3692,150,9338,3691,145,9338,3690,140,9338,3692,139,180813,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068860,NBR,120156,262,03,255,6,802,54702,46,802,5032,37,802,54782,30,802,5052,28,802,54712,12,802,5042,12,081116,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068860,NBR,110326,262,03,255,6,802,23152,23,812,49449,14,802,35382,13,802,35402,11,812,56622,09,802,23132,04,081116,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1600068860,LINK,112137,20,8,67,0,0,081116,FFFFFBFF#"));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,355488020533263,V3,121536,65501,04,000152,014001,156,-64,000161,010642,138,,000152,014003,129,,000152,013973,126,,02E4,0,X,071116,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4209917484,V19,093043,V,5052.9749,N,00426.4322,E,000.00,000,130916,,0032475874141,8944538530000543700F,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505220873067,V1,,V,4605.75732,N,01430.73863,E,0.00,0,,FFFFFFEF,125,194, 64,d3#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210051415,V1,164549,A,0956.3869,N,08406.7068,W,000.00,000,221215,FFFFFBFF,712,01,0,0,6#"),
+ position("2015-12-22 16:45:49.000", true, 9.93978, -84.11178));
+
+ verifyAttributes(decoder, buffer(
+ "*HQ,1451316451,NBR,112315,724,10,2,2,215,2135,123,215,2131,121,011215,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,1451316485,V1,121557,A,-23-3.3408,S,-48-2.8926,W,0.1,158,241115,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,1451316485,V1,121557,A,-23-35.3408,S,-48-2.8926,W,0.1,158,241115,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,355488020119695,V1,050418,,2827.61232,N,07703.84822,E,0.00,0,031015,FFFEFBFF#"),
+ position("2015-10-03 05:04:18.000", false, 28.46021, 77.06414));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,1451316409,V1,030149,A,-23-29.0095,S,-46-51.5852,W,2.4,065,070315,FFFFFFFF#"),
+ position("2015-03-07 03:01:49.000", true, -23.48349, -46.85975));
+
+ verifyNull(decoder, buffer(
+ "*HQ,353588020068342,V1,000000,V,0.0000,0,0.0000,0,0.00,0.00,000000,ffffffff,000106,000002,000203,004c87,16#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,3800008786,V1,062507,V,3048.2437,N,03058.5617,E,000.00,000,250413,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4300256455,V1,111817,A,1935.5128,N,04656.3243,E,0.00,100,170913,FFE7FBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,123456789012345,V1,155850,A,5214.5346,N,2117.4683,E,0.00,270.90,131012,ffffffff,000000,000000,000000,000000#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353588010001689,V1,221116,A,1548.8220,S,4753.1679,W,0.00,0.00,300413,ffffffff,0002d4,000004,0001cd,000047#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,354188045498669,V1,195200,A,701.8915,S,3450.3399,W,0.00,205.70,050213,ffffffff,000243,000000,000000#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V1,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#\r\n"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S17,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S14,100,10,1,3,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S20,ERROR,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S20,DONE,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,F7FFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,R8,ERROR,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S23,165.165.33.250:8800,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S24,thit.gd,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFF#"));
+
+ verifyPosition(decoder, buffer(
+ "*TH,2020916012,V4,S1,OK,pass_word,130305,050316,A,2212.8745,N,11346.6574,E,14.28,028,220902,FFFFFBFD#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353588020068342,V1,062840,A,5241.1249,N,954.9490,E,0.00,0.00,231013,ffffffff,000106,000002,000203,004c87,24#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505220903211,V1,075228,A,5227.5039,N,01032.8443,E,0.00,0,231013,FFFBFFFF,106,14, 201,2173#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,353505220903211,V1,140817,A,5239.3538,N,01003.5292,E,21.03,312,221013,FFFBFFFF,106,14, 203,1cd#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,356823035368767,V1,083618,A,0955.6392,N,07809.0796,E,0.00,0,070414,FFFBFFFF,194,3b5, 71,c9a9#"));
+
+ verifyNull(decoder, buffer(
+ "*HQ,8401016597,BASE,152609,0,0,0,0,211014,FFFFFFFF#"));
+
+ verifyPosition(decoder, binary(
+ "2441060116601245431311165035313006004318210e000000fffffbffff0024"));
+
+ verifyPosition(decoder, binary(
+ "24410600082621532131081504419390060740418306000000fffffbfdff0015060000002c02dc0c000000001f"),
+ position("2015-08-31 21:53:21.000", true, 4.69898, -74.06971));
+
+ verifyPosition(decoder, binary(
+ "2427051711092133391406135002584900014337822e000000ffffffffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2427051711092134091406135002584900014337822e000000ffffffffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2410307310010503162209022212874500113466574C014028fffffbffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2441090013450831250401145047888000008554650e000000fffff9ffff001006000000000106020299109c01"));
+
+ verifyPosition(decoder, binary(
+ "24270517030820321418041423307879000463213792000056fffff9ffff0000"));
+
+ verifyPosition(decoder, binary(
+ "2441091144271222470112142233983006114026520E000000FFFFFBFFFF0014060000000001CC00262B0F170A"));
+
+ verifyPosition(decoder, binary(
+ "24971305007205201916101533335008000073206976000000effffbffff000252776566060000000000000000000049"));
+
+ }
+
+ @Test
+ public void testDecodeStatus() throws Exception {
+
+ H02ProtocolDecoder decoder = new H02ProtocolDecoder(null);
+
+ verifyAttribute(decoder, buffer(
+ "*HQ,2705171109,V1,213324,A,5002.5849,N,01433.7822,E,0.00,000,140613,FFFFFFFF#"),
+ Position.KEY_STATUS, 0xFFFFFFFFL);
+
+ verifyAttribute(decoder, binary(
+ "2441091144271222470112142233983006114026520E000000FFFFFBFFFF0014060000000001CC00262B0F170A"),
+ Position.KEY_STATUS, 0xFFFFFBFFL);
+
+ verifyAttribute(decoder, buffer(
+ "*HQ,4210051415,V1,164549,A,0956.3869,N,08406.7068,W,000.00,000,221215,FFFFFBFF,712,01,0,0,6#"),
+ Position.KEY_STATUS, 0xFFFFFBFFL);
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
new file mode 100644
index 000000000..155869b24
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java
@@ -0,0 +1,72 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneOffset;
+import java.util.Date;
+
+import static org.junit.Assert.assertEquals;
+
+public class H02ProtocolEncoderTest extends ProtocolTest {
+
+ private H02ProtocolEncoder encoder = new H02ProtocolEncoder(null);
+ private Date time = Date.from(
+ LocalDateTime.of(LocalDate.now(), LocalTime.of(1, 2, 3)).atZone(ZoneOffset.systemDefault()).toInstant());
+
+ @Test
+ public void testAlarmArmEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_ARM);
+
+ assertEquals("*HQ,123456789012345,SCF,010203,0,0#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testAlarmDisarmEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_DISARM);
+
+ assertEquals("*HQ,123456789012345,SCF,010203,1,1#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testEngineStopEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ assertEquals("*HQ,123456789012345,S20,010203,1,1#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testEngineResumeEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_RESUME);
+
+ assertEquals("*HQ,123456789012345,S20,010203,1,0#", encoder.encodeCommand(command, time));
+ }
+
+ @Test
+ public void testPositionPeriodicEncode() throws Exception {
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.set(Command.KEY_FREQUENCY, 10);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+
+ assertEquals("*HQ,123456789012345,S71,010203,22,10#", encoder.encodeCommand(command, time));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java
new file mode 100644
index 000000000..6d879daf2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HaicomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HaicomProtocolDecoder decoder = new HaicomProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$GPRS012497007097169,T100001,150618,230031,5402267400332464,0004,2014,000001,,,1,00#V040*"),
+ position("2015-06-18 23:00:31.000", true, 40.37790, -3.54107));
+
+ verifyPosition(decoder, text(
+ "$GPRS123456789012345,602S19A,100915,063515,7240649312041079,0019,3156,111000,10004,0000,11111,00LH#V037"));
+
+ verifyPosition(decoder, text(
+ "$GPRS123456789012345,T100001,141112,090751,7240649312041079,0002,1530,000001,,,1,00#V039*"));
+
+ verifyPosition(decoder, text(
+ "$GPRS012497007101250,T100001,141231,152235,7503733600305643,0000,2285,000001,,,1,00#V041*"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java
new file mode 100644
index 000000000..9acf2ce87
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HomtecsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HomtecsProtocolDecoder decoder = new HomtecsProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "MDS0001_R6d1821f7,170323,143601.00,04,,,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "MDS0001_R6d1821f7,170323,143621.00,06,5105.29914,N,11400.52675,W,0.223,198.41,1,2.12,1042.3"));
+
+ verifyPosition(decoder, text(
+ "strommabus939_R01272028,160217,191003.00,06,5540.12292,N,01237.49814,E,0.391,,1,1.27,1.2"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java
new file mode 100644
index 000000000..0f24d4b5c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class HuaShengFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuaShengFrameDecoder decoder = new HuaShengFrameDecoder();
+
+ assertEquals(
+ binary("c0010c00120060000000000004000600010100c0"),
+ decoder.decode(null, null, binary("c0010c00120060000000000004000600010100c0")));
+
+ assertEquals(
+ binary("c0010c003e0002000000000010020012a0014f42445f3347315f56312e302e330013a0043335353835353035303434303635380006a08701000006a0a1035fc0"),
+ decoder.decode(null, null, binary("c0010c003e0002000000000010020012a0014f42445f3347315f56312e302e330013a0043335353835353035303434303635380006a08701000006a0a1035fc0")));
+
+ assertEquals(
+ binary("c00000003faa0000000000003ea5a5005a3f00c000000031363037303530373132353700e6d186ffcc7a25002201160010000000010015000000000000000000c0"),
+ decoder.decode(null, null, binary("c00000003faa0000000000003ea5a5005a3f00dbdc00000031363037303530373132353700e6d186ffcc7a25002201160010000000010015000000000000000000c0")));
+
+ assertEquals(
+ binary("C000000041AA00000000000030C000000031353035323630373538323800ADDCC100226AEF0000000000120005000100151206EF0504E99975002903EB80556492CEC0"),
+ decoder.decode(null, null, binary("C000000041AA00000000000030DBDC00000031353035323630373538323800ADDCC100226AEF0000000000120005000100151206EF0504E99975002903EB80556492CEC0")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
new file mode 100644
index 000000000..b624f69ab
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HuaShengProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuaShengProtocolDecoder decoder = new HuaShengProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "c0010c003c0002000000000044020010a0014f42445f3347315f56322e320013a0043335353835353035313032303536360006a08700000006a0a105c9c0"));
+
+ verifyNull(decoder, binary(
+ "c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0"));
+
+ verifyNotNull(decoder, binary(
+ "c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0"));
+
+ verifyPosition(decoder, binary(
+ "c000000060aa000000000000fa8000000031393037303431363434323700e9900affd61c1b00000000003a000000010015ffffff0000000000000004c2ffffffffff0005000a0d080000ca6a000900155741555a5a5a344730454e313133373233c0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa0000000000000f8000000031363130323030373236333600e6d4f9ffcc78c700000022003600000001001500000000000000000000059bffffffffff0005000a040300000253c0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa000000000000098000000031363130303732323236343700e6d4efffcc789f000000000026000000010015000000000000000000000488ffffffffff0005000a10060000008dc0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa000000000000098000000031363130303732323236343700e6d4efffcc789f000000000026000000010015000000000000000000000488ffffffffff0005000a10060000008dc0"));
+
+ verifyPosition(decoder, binary(
+ "c00000004baa00000000000005c400000131363037303630323537303800e6c82effcc7cb0003900a30089000000010015000000000000000000f20559ff577ce3980005000a060500000087c0"));
+
+ verifyNull(decoder, binary(
+ "c0010c003e0002000000000010020012a0014f42445f3347315f56312e302e330013a0043335353835353035303434303635380006a08701000006a0a1035fc0"));
+
+ verifyNull(decoder, binary(
+ "c0010c00120060000000000004000600010100c0"));
+
+ verifyNull(decoder, binary(
+ "C00000007EAA020000000000010001001047315F48312E305F56312E3000030013383632393530303238353334333036000400144C342D56374C673979497A7A2D724A6D0005000501000600084341524400070008434152440008000500000900183839383630303530313931343436313130393134000A0009434D4E4554C0"));
+
+ verifyPosition(decoder, binary(
+ "C000000041AA00000000000030C000000031353035323630373538323800ADDCC100226AEF0000000000120005000100151206EF0504E99975002903EB80556492CEC0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java
new file mode 100644
index 000000000..2d3937903
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class HuabaoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuabaoFrameDecoder decoder = new HuabaoFrameDecoder();
+
+ assertEquals(
+ binary("7e307e087d557e"),
+ decoder.decode(null, null, binary("7e307d02087d01557e")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
new file mode 100644
index 000000000..c5e8d5b2a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -0,0 +1,81 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class HuabaoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HuabaoProtocolDecoder decoder = new HuabaoProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E"));
+
+ verifyAttribute(decoder, binary(
+ "7E0200005C087034547007000B0000000000040003015A7CEF06CF8A84000000000000200108064451570800000000000000000104000000005309010000000000000000300113310109EB1D000800233030302E3838000A0003001A00000000000000050001037700B27E"),
+ "fuel1", 88.7);
+
+ verifyAttribute(decoder, binary(
+ "7e02000054086031592715006e0000000000000002015a3c1a06c8733800000000000019103022183633362a4d30302c34352c31313338363030526f79314f70656e26303030303030303030303030263132333435363738393031323334353623ff7e"),
+ Position.KEY_BATTERY, 3.86);
+
+ verifyPosition(decoder, binary(
+ "7e0200004e08026300003006480000000000000007021477d90841920700000000005019110515194001040000167130011631010cd00400000400d3020027d4013fd6143839363130313832303030343833363532383330da0104897e"));
+
+ verifyPosition(decoder, binary(
+ "7e020000400303000002280042000000000000000301618ab406c31ec800000000000518092116145701040000047830011031010aeb16000c00b28986011780108622216500060089ffffffffc37e"));
+
+ verifyPosition(decoder, binary(
+ "7E0200002A013833501744001900000000000000C201597FA806CC01580080000000081508180721210104000002F502020000030200009F7E"));
+
+ verifyPositions(decoder, binary(
+ "7E0704014301356777777707F5000801002600000000000000030159741206CBFD5001720000000116052709062401040000001D03020000002600000000000000030159746606CBFD50016B0000000116052709065301040000001D0302000000260000000000000003015975E406CBFE58013F0000000116052709072701040000001D0302000000260000000000000003015976DE06CBFF10012E0000000116052709075601040000001D0302000000260000000000000003015976BC06CBFED0012D0000000116052709083001040000001D0302000000260000000000000003015976FE06CBFEC001080000000116052709090001040000001D0302000000260000000000000003015976FE06CBFE7800FC0000000116052709093301040000001D0302000000260000000000000003015977A606CBFF3001080000000116052709100201040000001D030200001F7E"));
+
+ verifyAttributes(decoder, binary(
+ "7E0200002F01357888888800B60000000000000003015972B406CBF6B802230000000116061317571301040000000003020000EB0700050001016708B37E"));
+
+ verifyPositions(decoder, binary(
+ "7E070400F30303000002450064000401003A000000000000000301618AC606C31F20000000000029180514202847010400000000EB16000C00B28986061708003732585700060089FFFFFFFE003A000000000000000301618AE806C31EB800000000009F180514202917010400000000EB16000C00B28986061708003732585700060089FFFFFFFE003A000000000000000301618AE806C31EB800000000009F180514202947010400000000EB16000C00B28986061708003732585700060089FFFFFFFE003A000001000000080301618AE806C31EB800000000009F180514203006010400000000EB16000C00B28986061708003732585700060089FFFFFFFED77E"));
+
+ verifyPosition(decoder, binary(
+ "7e02000054093037612710000700000000000000010223aca000dc9dd800000000000017121417122133362a4d30302c34352c31313336393042383030313233303026303030303030303030303030263132333435363738393031323334353623897e"));
+
+ verifyPosition(decoder, binary(
+ "7e0200002c00160128561400020000000000040001005de1f7065c6cef00000000000017011710044201040000a9002a02000030011b3101030c7e"));
+
+ verifyPosition(decoder, binary(
+ "7e0200002c00160128561400030000000000040007005de13c065c6cdb00160000000017011710054201040000a9002a02000030011b310104e47e"));
+
+ verifyNull(decoder, binary(
+ "7e0100002d0141305678720024002c012f373031313142534a2d41362d424400000000000000000000003035363738373201d4c14238383838386d7e"));
+
+ verifyNull(decoder, binary(
+ "7E0100002D013511221122000500000000373031303748422D52303347424400000000000000000000003233363631303402CBD5424136383630387E"));
+
+ verifyNull(decoder, binary(
+ "7e0100002d007089994489002800000000000000000048422d523033474244000000000000000000000031393036373531024142433030303030d17e"));
+
+ verifyNull(decoder, binary(
+ "7E0102000E013511221122000661757468656E7469636174696F6E3F7E"));
+
+ verifyPosition(decoder, binary(
+ "7E02000032013511221122000700000000000C000301578CC006CA3A5C00470000000014072317201501040000000030011631010BD07E"));
+
+ verifyNull(decoder, binary(
+ "7E010200100940278494700084323031313131303831313333323139369F7E"));
+
+ verifyNull(decoder, binary(
+ "7e010000190940278494700012000000000000000000000000000000000000094027849470000a7e"));
+
+ verifyPosition(decoder, binary(
+ "7e0200002e094027587492000a000000010000000a03083db7001329f3000000140000130412164952010400000012360a0002341502cb0c20085c107e"));
+
+ verifyPosition(decoder, binary(
+ "7e020000220014012499170007000000000000400e012af16f02cbd2ba000000000000150101194257010400000077a97e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
new file mode 100644
index 000000000..d0a0427c8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class HuabaoProtocolEncoderTest extends ProtocolTest {
+
+ @Ignore
+ @Test
+ public void testEncode() throws Exception {
+
+ HuabaoProtocolEncoder encoder = new HuabaoProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("7e81050001080201000027001ff0467e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java
new file mode 100644
index 000000000..f0697f423
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HunterProProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HunterProProtocolDecoder decoder = new HunterProProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">0002<$GPRMC,170559.000,A,0328.3045,N,07630.0735,W,0.73,266.16,200816,,,A77, s000078015180\",0MD"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java
new file mode 100644
index 000000000..d211d80ce
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class IdplProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ IdplProtocolDecoder decoder = new IdplProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "*ID1,863071011086474,210314,153218,A,1831.4577,N,07351.1433,E,0.79,240.64,9,20,A,1,4.20,0,1,01,1,0,0,A01,R,935D#"),
+ position("2014-03-21 15:32:18.000", true, 18.524295, 73.852388333333));
+
+ verifyPosition(decoder, text(
+ "*ID1,863071011086474,210314,162752,A,1831.4412,N,07351.0983,E,0.04,213.84,9,25,A,1,4.20,0,1,01,1,0,0,A01,L,EA01#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
new file mode 100644
index 000000000..00b7de094
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
@@ -0,0 +1,64 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class IntellitracProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ IntellitracProtocolDecoder decoder = new IntellitracProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$OK:TRACKING"));
+
+ verifyPosition(decoder, text(
+ "101000001,20100304075545,121.64547,25.06200,0,0,61,7,2,1,0,0.046,0.000,20100304075546,0"),
+ position("2010-03-04 07:55:45.000", true, 25.06200, 121.64547));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217132813,121.646060,25.061725,20,157,133,7,0,11,15,0.096,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217132813,121.646060,25.061725,20,157,-133,7,0,11,15,0.096,0.000"));
+
+ verifyPosition(decoder, text(
+ "1001070919,20130405084206,37.903730,48.011377,0,0,235,10,2,2,0,20.211,0.153"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217144230,121.646102,25.061398,0,0,139,0,0,0,0,0.093,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000004,20050513153524,121.646075,25.063675,0,166,50,6,1,0,0,0.118,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000004,20050513154001,121.646075,25.063675,0,166,55,7,1,0,0,0.096,0.000"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217132813,121.646060,25.061725,20,157,0,7,0,11,15"));
+
+ verifyPosition(decoder, text(
+ "12345,1010000002,20030217132813,121.646060,25.061725,20,157,0,7,0,11,15"));
+
+ verifyPosition(decoder, text(
+ "1010000002,20030217144230,121.646102,25.061398,0,0,0,7,2,0,0"));
+
+ verifyPosition(decoder, text(
+ "$RP:12345,1010000002,20030217144230,121.646102,25.061398,0,0,0,7,2,0,0"));
+
+ verifyPosition(decoder, text(
+ "1010000001,20030105092129,121.651598,25.052325,0,0,33,0,1,0,0"));
+
+ verifyPosition(decoder, text(
+ "1010000001,20030105092129,-121.651598,-25.052325,0,0,33,0,1,0,0"));
+
+ verifyPosition(decoder, text(
+ "1015210962,20131010144712,-77.070037,-12.097935,0,0,77,7,2,2,0,0,139446.8,2095,20131010144712,,0.103,0.000"));
+
+ verifyPosition(decoder, text(
+ "1003269480,20131126100258,10.32989,49.93836,0,304,217,6,2,0,0,0.000,0.000,20131126100258,0,0,0,-40,0,0,-273,0,0,0,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java
new file mode 100644
index 000000000..f5c98b1aa
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ItsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ItsFrameDecoder decoder = new ItsFrameDecoder();
+
+ verifyFrame(
+ binary("242c2c3836383732383033373731373434312c312e3444335f4149533134305f312e302c56455253494f4e312e302c32382e3633333731372c4e2c37372e3232323730322c45"),
+ decoder.decode(null, null, binary("242c2c3836383732383033373731373434312c312e3444335f4149533134305f312e302c56455253494f4e312e302c32382e3633333731372c4e2c37372e3232323730322c45242c43502c41544c2c312e3444335f4149533134305f312e302c49462c30382c4c2c3836383732383033373731373434312c2c312c31373034323031392c3133313830392c32382e3633333731352c4e2c37372e3232323730322c452c302e302c342e30302c302c3231312e302c312e312c362e362c496465612c302c312c31322e362c332e392c302c4f2c31322c3430342c31312c3430612c3564332c326464642c3430612c30392c3438622c3430612c30362c3564342c3430612c30352c6165392c3430612c30352c303131312c30302c3236303831342c662a")));
+
+ verifyFrame(
+ binary("244865616465722c69547269616e676c65312c4b41303147313233342c3836343439353033343433343631302c315f333654303242303136344d4149535f362c4149533134302c302e3030303030302c4e2c302e3030303030302c452a3545"),
+ decoder.decode(null, null, binary("244865616465722c69547269616e676c65312c4b41303147313233342c3836343439353033343433343631302c315f333654303242303136344d4149535f362c4149533134302c302e3030303030302c4e2c302e3030303030302c452a35450d0a")));
+
+ verifyFrame(
+ binary("242c43502c41544c2c312e3444335f4149533134305f312e302c50432c31322c4c2c3836383732383033373731373434312c2c312c31363034323031392c3037333432372c32382e3633333533352c4e2c37372e3232323733332c452c302e302c3333392e30302c302c302e302c312e302c302e372c216465612c302c312c31332e372c332e392c302c4f2c32312c3430342c20342c38382c616433352c616437622c38382c32322c636661612c38382c31362c363666392c38382c31342c336632632c64372c31332c303131312c30302c3236303731312c"),
+ decoder.decode(null, null, binary("242c43502c41544c2c312e3444335f4149533134305f312e302c50432c31322c4c2c3836383732383033373731373434312c2c312c31363034323031392c3037333432372c32382e3633333533352c4e2c37372e3232323733332c452c302e302c3333392e30302c302c302e302c312e302c302e372c216465612c302c312c31332e372c332e392c302c4f2c32312c3430342c20342c38382c616433352c616437622c38382c32322c636661612c38382c31362c363666392c38382c31342c336632632c64372c31332c303131312c30302c3236303731312c3a2a2c4f2c532c2b393138373433393530333938")));
+
+ verifyFrame(
+ binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322c"),
+ decoder.decode(null, null, binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322cbc2a")));
+
+ verifyFrame(
+ binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322c"),
+ decoder.decode(null, null, binary("242c43502c41544c2c312e3444335f4149533134305f312e302c45412c31302c482c3836383732383033373731373434312c2c312c32393033323031392c3232353931332c32382e3533343031362c4e2c37372e3236383933362c452c302e302c34382e30302c302c302e302c312e322c302e372c216465612c302c312c31322e362c332e392c312c4f2c32322c3430342c20342c3161612c346230622c643433642c3161612c31352c643433622c3161612c31352c346230632c3161612c31342c346136322c3161612c31322c303131312c30302c3232313333322c2a2a")));
+
+ verifyFrame(
+ binary("244e524d2c524f41445250412c312e394149532c4e522c30312c4c2c3836393836373033363031353636392c2c312c30383034323031392c3230303530362c32362e39303634353636372c4e2c37352e37383936323333332c452c302e302c3238302e37342c31342c3437312e322c302e38302c302e36302c566f6461666f6e65202d2052616a61737468616e2c302c312c31322e392c342e322c302c4f2c32302c3430342c36302c303746352c333939462c31302c303746352c324434372c31302c303746352c333941312c31302c303746352c324136452c30382c303746352c333536382c303030302c30302c3030333838342c3936443446373031"),
+ decoder.decode(null, null, binary("244e524d2c524f41445250412c312e394149532c4e522c30312c4c2c3836393836373033363031353636392c2c312c30383034323031392c3230303530362c32362e39303634353636372c4e2c37352e37383936323333332c452c302e302c3238302e37342c31342c3437312e322c302e38302c302e36302c566f6461666f6e65202d2052616a61737468616e2c302c312c31322e392c342e322c302c4f2c32302c3430342c36302c303746352c333939462c31302c303746352c324434372c31302c303746352c333941312c31302c303746352c324136452c30382c303746352c333536382c303030302c30302c3030333838342c39364434463730312a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
new file mode 100644
index 000000000..8733e9034
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
@@ -0,0 +1,108 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class ItsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ItsProtocolDecoder decoder = new ItsProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$LGN,,869867037009679,3.2AIH,9.99546000,N,76.35886167,E"));
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,3.5AIS,NR,01,L,869867036940288,,1,04012020,094901,23.18731933,N,77.45079633,E,0.0,0.00,13,545.6,1.10,0.60,Idea Cellular Ltd,0,1,13.2,4.1,0,C,31,404,78,62E1,5799,29,62E1,579B,23,62E1,52EB,22,62E1,52EA,21,62E1,2FF1,0100,00,0,0,001926,8252.226,-,-,-,-,5_5_5_5_0,171B56E1*"));
+
+ verifyPosition(decoder, text(
+ "$NMP,TRAXSMART,1.7.7,NR,1,L,862818043908237,0000,0,04012020,104208,28.6183987,N,77.3888474,E,001.0,000.00,00,000.0,0.0,0.0,Idea P,0,1,13.3,4.0,0,C,22,404,30,0089,2793,x,x,x,x,x,x,x,x,x,x,x,x,0002,00,000591,00.4,00.4,0,(0,0,0)*FC"));
+
+ verifyPosition(decoder, text(
+ "$NRM,,3.2AIH,NR,01,L,869867037003854,,1,02122019,074801,9.99553167,N,76.35911000,E,0.0,125.73,18,103.7,0.10,0.10,CellOne Kerala,0,1,13.1,4.2,0,C,19,404,72,08FE,0940,13,08FE,093E,11,08FE,7DA7,07,08FE,093F,07,08FE,7DA6,0100,00,0,0,003372,6897.214,,,,,5_5_3_5_0,B74BBD72*"));
+
+ verifyPosition(decoder, text(
+ "$,ID01,SAT,1.0.0,NR,1,L,868345034056903,DL3CAB1021,1,27052019,040234,28.359895,N,76.927879,E,0.0,285.6,12,254.9,1.4,0.7,IDEA,1,1,12.6,3.8,0,25,404,04,0138,0927,4ECD,0138,41,1C2B,0138,37,D77A,0138,34,D843,0138,33,0000,00,0.03,0.00,000091,A3,*"));
+
+ verifyPosition(decoder, text(
+ "$NMP,GPSBOX,1.6.8,NR,H,868997035844834,0000,1,220519,035419,28.6291409,N,77.3928299,E,015.2,157.40,07,197.86,2.1,1.0,airtel,1,1,13.3,4.1,0,O,31,404,10,0099,79b4,(-57,0099,334c,x,x,x,x,x,x,x,x,x),0012,00,000348,2,08.4,00.3,(0,0,0),CD*"));
+
+ verifyPosition(decoder, text(
+ "$RLP,N.A,2.0.2,NR,01,L,869867030181814,N.A,28022019,180155,1,28.688226,N,076.993570,E,0.0,80.26,17,201.0,0.89,0.60,VODAFONE I,0,1,25.0,4.20,0,C,14,404,11,415,F34A,51f7,415,13,840b,415,8,a3f7,0c2,5,ef77,415,5,0001,00,17888,47,*"));
+
+ verifyAttribute(decoder, text(
+ "$TEL123,Teltonika,03.18.16,NR,1,L,352093085223096,KA09X6945,1,24122018,055749,12.303873,N,76.690697,E,0.0,349.00,10,795.0,0.50,0.40,Airtel,0,1,14.6,4.1,0,,28,404,45,625A,116E,29,28DF,03A3,28,9A5C,0923,26,116F,625A,25,2A51,03A3,0010,00,000042,0.1,0.1,0,()*7B"),
+ Position.PREFIX_ADC + 2, 0.1);
+
+ verifyPosition(decoder, text(
+ "$Header,iTriangle,1_37T02B0164MAIS,BR,6,L,861693034634154,KA01I2000,1,09112017,160702,12.976593,N,77.549782,E,25.1,344,15,911.0,1.04,0.68,Airtel,1,1,11.8,3.8,1,C,24,404,45,61b4,9ad9,31,9adb,61b4,35,ffff,0000,33,ffff,0000,31,ffff,0000,0001,00,000014,0.0,0.1,4,()*1E"));
+
+ verifyAttribute(decoder, text(
+ "$EPB,EMR,869867036066035,NM,03042019,192008,V,000.00000000,N,000.00000000,E,0000000000.0,0000.0,00.000,G,,0,404,22,ECFB,36EF*226F7BD1"),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyPosition(decoder, text(
+ "$,CP,ATL,1.4D3_AIS140_1.0,EA,10,H,868728037717441,,1,31032019,140054,28.533699,N,77.269020,E,0.0,188.00,14,76.0,1.3,0.0,Idea,0,1,12.7,3.9,1,O,22,404,11,69,979c,fc1,69,18,fbf,69,15,e36e,69,14,ba2f,3ff,13,0111,00,249404,"));
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,1.8AIS,EA,01,L,869867036341099,,1,11032019,231048,19.25166667,N,73.04615167,E,0.0,230.21,17,12.8,0.80,0.80,airtel,0,1,13.5,4.2,0,O,22,404,90,0CC9,EBC8,19,0CC9,8992,16,0CC9,AB49,15,0CC9,AB44,14,0CC9,F03C,0000,00,002080,C9FBBF99"));
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,1.7AIS,NR,01,L,869867036345389,,1,25022019,051716,25.12891000,N,75.85587833,E,7.6,350.00,14,284.8,1.00,1.00,AIRTEL,1,1,28.0,4.2,0,C,13,404,70,4E3B,3C84,11,4E3B,39B8,08,4E3B,3965,07,4E3B,48B5,07,4E3B,3C85,0000,00,000551,71978C6B"));
+
+ verifyPosition(decoder, text(
+ "$,03,XYZ123,0.0.1,TA,16,L,869867035297185,MH12AB1234,1,20,02,2019,10,59,13,023.482630,N,086.399673,E,000.1,015.19,21,212.3,01.12,00.58,NOSERV,0,1,00.0,4.6,1,C,11,404,75,082a,db3a,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,01,000013,01dbd51f,"));
+
+ verifyPosition(decoder, text(
+ "$NRM,ROADRPA,1.7AIS,NR,01,L,869867036345389,,1,25022019,051716,25.12891000,N,75.85587833,E,7.6,350.00,14,284.8,1.00,1.00,AIRTEL,1,1,28.0,4.2,0,C,13,404,70,4E3B,3C84,11,4E3B,39B8,08,4E3B,3965,07,4E3B,48B5,07,4E3B,3C85,0000,00,000551,71978C6B"));
+
+ verifyPosition(decoder, text(
+ "$,1,CHVTS,CHVTS1.0,DT,16,L,861359039868243,861359039868243,1,05022019,071225,19.965062,N,73.736088,E,0,050,03,0632,6.67,6.75,Idea Cel,1,1,23.96,4.0,0,W,28,404,004,4e2b,49e,4e2bea86727ab3d6704e2bea7714e2be9d72,0000,00,001133,232"));
+
+ verifyPosition(decoder, text(
+ "$,04,XYZ123,0.0.1,TA,16,L,861359034100626,MH12AB1234,1,12,11,2018,08,53,08,018.489645,N,073.855972,E,000.0,220.04,12,593.0,01.13,00.75,AIRTEL,1,1,00.0,4.1,1,C,18,404,90,0c23,781a,5169,0c23,-093,0000,0000,0000,0000,0000,0000,0000,0000,0000,1000,01,000006,f906c65c,"));
+
+ verifyPosition(decoder, text(
+ "$,A,MFR,7.0,NR,01,L,869026034780985,PJ09BU1234,1,12112018,121953,12.756974,N,077.800025,E,000.0,318.03,15,794.0,001.3,000.7,TAMIL NAD,0,1,13.08,04.13,0,O,22,404,80,0919,71C1,0919,7168,19,0919,71c3,17,0919,71c2,11,0919,7167,09,0011,00,000173,D8,000000.00,,,"));
+
+ verifyPosition(decoder, text(
+ "$,04,XYZ123,0.0.1,TA,16,L,861359034100626,MH12AB1234,1,14,10,2018,04,50,52,018.489624,N,073.855980,E,000.0,039.86,13,584.1,01.11,00.75,AIRTEL,1,1,00.0,4.1,1,C,15,404,90,0c23,781a,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,1000,01,000005,13b75499,"));
+
+ verifyNull(decoder, text(
+ "$,01,XYZ123,0.0.1,861359034137271,MH12AB1234,"));
+
+ verifyNull(decoder, text(
+ "$,02,XYZ123,0.0.1,861359034137271,100,30,00.0,00005,00600,1000,01,00.1,00.0,"));
+
+ verifyPosition(decoder, text(
+ "$,EPB,EMR,861359034100626,SP,00,00,0000,00,00,00,V,000.000000,N,000.000000,E,000.0,000.0,000.00,N,MH12AB1234,0000000000000,d34679e1,"));
+
+ verifyPosition(decoder, text(
+ "$,03,XYZ123,0.0.1,TA,16,L,861359034137271,MH12AB1234,0,00,00,0000,00,00,00,000.000000,N,000.000000,E,000.0,000.00,00,000.0,00.00,00.00,IDEAIN,1,1,00.0,4.0,1,O,16,404,22,2797,11b7,11b9,2797,-087,11b8,2797,-093,11b4,2797,-106,0000,0000,0000,1000,01,000032,8173e058,"));
+
+ verifyPosition(decoder, text(
+ "$,04,XYZ123,0.0.1,BR,06,L,861359034137271,MH12AB1234,0,00,00,0000,00,00,00,000.000000,N,000.000000,E,000.0,000.00,00,000.0,00.00,00.00,IDEAIN,1,1,00.0,3.8,1,O,17,404,22,2797,11b7,11b9,2797,-093,11b8,2797,-098,0000,0000,0000,0000,0000,0000,1000,00,000006,abd26284,"));
+
+ verifyNull(decoder, text(
+ "$Header,nliven,EMR,861693034634154,NM,09112017155133,A,12.976495,N,77.549713,E,906.0,0.0,23,G,KA01I2000,+919844098440*4B"));
+
+ verifyNull(decoder, text(
+ "$EPB,iTriangle1,EMR,864495034445822,SP,03082018110730,A,22.829292,N,75.935806,E,543.0,0.0,0,G,KA01G1234,+9164061023*13"));
+
+ verifyPosition(decoder, text(
+ "$Header,iTriangle,1_37T02B0164MAIS_2,NR,1,L,864495034490141,KA01I2000,1,19042018,102926,22.846401,N,75.948952,E,0.0,311,5,578.0,3.80,3.67,AirTel,0,1,12.5,4.3,1,C,14,404,93,0456,16db,29,ebd8,0458,28,3843,18ab,25,072e,18ab,22,35da,0458,0000,00,031181,0.0,0.0,0,()*34"));
+
+ verifyPosition(decoder, text(
+ "$Header,nliven,1_37T02B0164MAIS,BR,6,L,861693034634154,KA01I2000,1,09112017,160702,12.976593,N,77.549782,E,25.1,344,15,911.0,1.04,0.68,Airtel,1,1,11.8,3.8,1,C,24,404,45,61b4,9ad9,31,9adb,61b4,35,ffff,0000,33,ffff,0000,31,ffff,0000,0001,00,000014,0.0,0.1,4,()*1E"));
+
+ verifyPosition(decoder, text(
+ "$Header,iTriangle,1_37T02B0164MAIS_2,NR,1,L,864495034490141,KA01I2000,1,31032018,122247,22.845999,N,75.949005,E,0.0,44,16,545.0,1.19,0.65,AirTel,1,1,12.0,4.3,0,C,13,404,93,0456,16db,27,16dd,0456,22,3843,18ab,19,ebd8,0458,14,072c,18ab,0101,00,003735,0.0,0.0,0,()*48"));
+
+ verifyNull(decoder, text(
+ "$Header,nliven,KA01I2000,861693034634154,1_37T02B0164MAIS,AIS140,12.976545,N,77.549759,E*50"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java
new file mode 100644
index 000000000..225edd643
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class ItsProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ ItsProtocolEncoder encoder = new ItsProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ assertEquals("@SET#RLP,OP1,", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
new file mode 100644
index 000000000..b0b416891
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
@@ -0,0 +1,60 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Ivt401ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Ivt401ProtocolDecoder decoder = new Ivt401ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(TLA,356917051007891,190118,090211,+16.986606,+82.242416,0,66,4,13,1,5,000,00,0.0,11.59,8.30,37.77,0.0,1,1.02,0,0,208,0,0,0,0,000000000,0,0,0,0,0,0,0,1,8654604,5,9,114)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,862107032006249,230218,180500,+18.479728,+73.896339,30,0,944,13,1,5,111,11,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,865933030026336,250118,063827,+18.598098,+73.806518,0,79,0,1,1,5,1200,0,0.0,11.50,4.00,36,0,0,1.00,0,0,12702,202,0)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,865933030026336,250118,063838,+18.598110,+73.806518,0,334,0,1,1,5,1200,0,0.0,12.20,4.10,36,0,0,1.00,0,0,12704,0,0,0,0,0,0,0,0,0,0,0,0,1,0,3,203,0)"));
+
+ verifyNull(decoder, text(
+ "(TLB,356917050440515,0,0,+0.0,+0.0,0,0,0,0,0,0,000,00,0.00,12.21,7.95,26.57,0.0,1,1.12,0,0,0,1,90)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050440515,250118,094723,+0.0,+0.0,0,0,0,0,3,5,000,00,0.00,12.25,7.95,27.11,0.0,0,1.13,0,0,0,4,86)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,356917050440515,250118,094733,+0.0,+0.0,0,0,0,0,3,5,000,00,0.00,12.25,7.95,27.20,0.0,0,1.12,0,0,0,0,0,0,0,000000000,0,1,0,0,0,0,0,1,0,0,5,70)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050440515,250118,094733,+0.0,+0.0,0,0,0,0,3,5,000,00,0.00,12.25,7.95,27.20,0.0,0,1.13,0,0,0,6,87)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050440515,250118,103541,+17.015546,+54.080841,0,0,31,12,1,5,000,00,0.0,0.00,8.04,28.59,M+0+0+0+0+0,0,1.12,0,0,2,48,30)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050291991,090315,133525,+12.990582,+77.589080,0,0,944,13,1,5,000,00,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050269732,061117,220046,+21.134126,+74.798561,51,28,204,14,1,5,100,00,0.0,13.92,7.82,23.74,0.0,1,1.33,-9,0,429,4848,67)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050269732,061117,220116,+21.137619,+74.800659,52,28,202,14,1,3,100,00,0.0,13.92,7.82,23.74,0.0,1,1.26,-23,0,445,4849,125)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,356917050217335,190115,011336,+12.932403,+79.898887,0,0,71.7,08,3,10,000,00,0.00,10.41,7.07,26.84,0.00,0,0.99,63,0,0,0,0,0,0,000000000,0,0,0,0,0,0,0,2,0,0,14,86)"));
+
+ verifyPosition(decoder, text(
+ "(TLB,356917050291991,090315,133525,+12.990582,+77.589080,0,0,944,13,1,5,000,00,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLL,356917050217335,190115,011336,+12.932403,+79.898887,0,0,71.7,08,3,10,000,00,0.00,10.41,7.07,26.84,0.00,0,0.99,63,0,0,0,0,0,0,000000000,0,0,0,0,0,0,0,2,0,0,14,86)"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java
new file mode 100644
index 000000000..8179a2bae
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class JpKorjarProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ JpKorjarProtocolDecoder decoder = new JpKorjarProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "KORJAR.PL,329587014519383,160910144240,52.247254N,021.013375E,0.00,1,F:4.18V,1 260 01 794B 3517,"));
+
+ verifyPosition(decoder, text(
+ "KORJAR.PL,329587014519383,160910144240,52.895515N,021.949151E,6.30,212,F:3.94V,0 260 01 794B 3519,"));
+
+ verifyPosition(decoder, text(
+ "KORJAR.PL,329587014519383,160910144240,52.895596N,021.949343E,12.46,087,L:2.18V,1 260 01 794B 3517,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
new file mode 100644
index 000000000..e695624a9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
@@ -0,0 +1,43 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Jt600FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Jt600FrameDecoder decoder = new Jt600FrameDecoder();
+
+ verifyFrame(
+ binary("2478905197081711003405101917164812492365028134847d0a1c000002640c0000000020c032759600731000000f0f0f0f0f0f0f0f0f0f000702850274"),
+ decoder.decode(null, null, binary("2478905197081711003405101917164812492365028134847d0a1c000002640c0000000020c032759600731000000f0f0f0f0f0f0f0f0f0f000702850274")));
+
+ verifyFrame(
+ binary("24315011626912001b21111718095900000000000000000e0000005c000000000000000000"),
+ decoder.decode(null, null, binary("24315011626912001b21111718095900000000000000000e0000005c00000000000000000024315011626912001b22111708130400000000000000000e0000005a00000000000000000024315011626912001b22111708140400000000000000000e0000005a000000723e18a61b01")));
+
+ verifyFrame(
+ binary("2475604055531611002311111600311326144436028210791d016c0000001f070000000020c03c4f6d07d80ccf"),
+ decoder.decode(null, null, binary("2475604055531611002311111600311326144436028210791d016c0000001f070000000020c03c4f6d07d80ccf")));
+
+ verifyFrame(
+ binary("2475605035891613002328091601152806086750106533350c00000000000a000000000000e1ff4f97007f1607"),
+ decoder.decode(null, null, binary("2475605035891613002328091601152806086750106533350c00000000000a000000000000e1ff4f97007f1607")));
+
+ verifyFrame(
+ binary("28333132303832303032392C5730312C30323535332E333535352C452C323433382E303939372C532C412C3137313031322C3035333333392C302C382C32302C362C33312C352C32302C323029"),
+ decoder.decode(null, null, binary("28333132303832303032392C5730312C30323535332E333535352C452C323433382E303939372C532C412C3137313031322C3035333333392C302C382C32302C362C33312C352C32302C323029")));
+
+ verifyFrame(
+ binary("24312082002911001B171012053405243809970255335555000406140003EE2B91044D1F02"),
+ decoder.decode(null, null, binary("24312082002911001B171012053405243809970255335555000406140003EE2B91044D1F02")));
+
+ verifyFrame(
+ binary("28373536303430353535332c404a5429"),
+ decoder.decode(null, null, binary("28373536303430353535332c404a5429")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
new file mode 100644
index 000000000..4a3c752cd
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
@@ -0,0 +1,123 @@
+package org.traccar.protocol;
+
+import org.traccar.ProtocolTest;
+
+import org.junit.Test;
+
+public class Jt600ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Jt600ProtocolDecoder decoder = new Jt600ProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "2478807035371711003419081920061851380856003256223b000000000000070000000020c0ff965d54de1800000f0f0f0f0f0f0f0f0f0f02d600ea0a21"));
+
+ verifyPositions(decoder, binary(
+ "2475201509261611002313101503464722331560113555309F00000000002D0500CB206800F064109326381A03"));
+
+ verifyPositions(decoder, binary(
+ "2475810297431713003401010000030100000000000000000e000000000001000000000020e0641aba1b6f1b00000f0f0f0f0f0f0f0f0f0f000001942803"));
+
+ verifyPositions(decoder, binary(
+ "2440811188882400A209060908045322564025113242329F0598000001003F0000002D0009060908050322564025113242329F0598000001003F0000002D0009060908051322564025113242329F0598000001003F0000002D0009060908052322564025113242329F0598000001003F0000002D0009060908053322564025113242329F0598000001003F0000002D0009060908054322564025113242329F0598000001003F0000002D001F"));
+
+ verifyPositions(decoder, binary(
+ "24408111888823019509060908045322564025113242329F0598000001003F0000002D0009060908050322564025113242329F0598000001003F0000002D0009060908051322564025113242329F0598000001003F0000002D0009060908052322564025113242329F0598000001003F0000002D0009060908053322564025113242329F0598000001003F0000002D0009060908054322564025113242329F0598000001003F0000002D0009060908055322564025113242329F0598000001003F0000002D0009060908060322564025113242329F0598000001003F0000002D0009060908061322564025113242329F0598000001003F0000002D0009060908062322564025113242329F0598000001003F0000002D0009060908063322564025113242329F0598000001003F0000002D0009060908064322564025113242329F0598000001003F0000002D0009060908065322564025113242329F0598000001003F0000002D0009060908070322564025113242329F0598000001003F0000002D0009060908071322564025113242329F0598000001003F0000002D001F"));
+
+ verifyPositions(decoder, binary(
+ "2475801263981711002713061813333723501622090221558f012f0000002a070000000020c055b88552191f000f0f0f07"));
+
+ verifyPositions(decoder, binary(
+ "24408111888821001B09060908045322564025113242329F0598000001003F0000002D00AB"));
+
+ verifyPositions(decoder, binary(
+ "2475609213701711002701010000020200000000000000000e00000000000f000000000020c164cd7b00d516000f0f0f02"));
+
+ verifyPositions(decoder, binary(
+ "24657060730131001b13111710361906538525079524797f000000000000000003f300036c"));
+
+ verifyPositions(decoder, binary(
+ "24624090196121001b19071703493631277203074235752f295800005308010000768b0822"));
+
+ verifyPositions(decoder, binary(
+ "24624090196123019519071703412131285623074214252f10560000130801000076850819071703420631282832074215165f172c0000030801000076850919071703422131282792074216635f222e0000130801000076850919071703423631282808074218261f212a0000130801000076860819071703435131283678074222928f08350000930801000076860919071703440631283003074223174f19500000930801000076870819071703445131279872074224584f07380000930801000076870819071703453631280643074227091f1b220000530801000076880919071703455131281043074228216f0a260000530801000076880819071703460631281229074228988f0c260000530801000076880819071703462131281551074229954f1f220000530801000076880919071703463631281289074230503f114e0000530801000076880819071703465131281186074230884f094f0000530801000076880819071703470631280308074231240f17500000530801000076880619071703472131280358074231636f0b1d0000530801000076890821"));
+
+ verifyPositions(decoder, binary(
+ "2475604055531611002311111600311326144436028210791d016c0000001f070000000020c03c4f6d07d80ccf"));
+
+ verifyPositions(decoder, binary(
+ "2475201509260111002313101503464722331560113555309F00000000002D0500CB206800F064109326381A03"));
+
+ verifyPositions(decoder, binary(
+ "2475605035891613002328091601152806086750106533350c00000000000a000000000000e1ff4f97007f1607"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U01,040812,185302,T,22.564025,N,113.242329,E,5.21,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U02,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U03,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyNull(decoder, buffer(
+ "(3301210003,U04)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U06,1,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,0000000000011,10133,5173,22,100,1,300,100,10)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U01,220916,135251,T,9.552607,N,13.658292,W,0.31,0,9,0%,00001001000000,11012,10,27,0,0,33)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U01,010100,000024,F,0.000000,N,0.000000,E,0.00,0,0,100%,00000001000000,263,1,18,0,0,33)"));
+
+ verifyNull(decoder, buffer(
+ "(3460311327,@JT)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U06,11,220916,135643,T,9.552553,N,13.658265,W,0.61,0,9,100%,00000001000000,11012,10,30,0,0,126,0,30)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U06,10,220916,140619,T,9.552495,N,13.658227,W,0.43,0,7,0%,00101001000000,11012,10,0,0,0,126,0,30)"));
+
+ verifyPositions(decoder, binary(
+ "24311021600111001B16021105591022329862114046227B0598095080012327951435161F"),
+ position("2011-02-16 05:59:10.000", true, 22.54977, -114.07705));
+
+ verifyPositions(decoder, binary(
+ "24312082002911001B171012052831243810120255336425001907190003FD2B91044D1FA0"));
+
+ verifyPositions(decoder, binary(
+ "24312082002911001B1710120533052438099702553358450004061E0003EE000000000C00"));
+
+ verifyPositions(decoder, binary(
+ "24608111888821001B09060908045322564025113242329F0598000001003F0000002D00AB"));
+
+ verifyPosition(decoder, buffer(
+ "(3110312099,W01,11404.6204,E,2232.9961,N,A,040511,063736,4,7,100,4,17,1,1,company)"),
+ position("2011-05-04 06:37:36.000", true, 22.54994, 114.07701));
+
+ verifyPosition(decoder, buffer(
+ "(3120820029,W01,02553.3555,E,2438.0997,S,A,171012,053339,0,8,20,6,31,5,20,20)"));
+
+ verifyPosition(decoder, buffer(
+ "(3330104377,U01,010100,010228,F,00.000000,N,000.000000,E,0,0,0,0%,00001000000000,741,14,22,0,206)"));
+
+ verifyNull(decoder, buffer(
+ "(6221107674,2,U09,129,2,A,280513113036,E,02711.0500,S,1721.0876,A,030613171243,E,02756.7618,S,2300.0325,3491,538200,14400,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U02,040812,185302,T,00.000000,N,000.000000,E,0,0,0,0%,00000000000011,741,51,22,0,1,05)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U06,4,250916,133207,T,7.011013,N,25.060708,W,27.61,102,10,0%,00101011000000,0,1,0,448,0,126,1,30)"));
+
+ verifyPosition(decoder, buffer(
+ "(3551001012,U01,010100,000032,F,0.000000,N,0.000000,E,0.00,0,0,10%,00000000010000,15748,7923,23,0,0,3E)"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
new file mode 100644
index 000000000..c0b399c63
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class Jt600ProtocolEncoderTest extends ProtocolTest {
+ Jt600ProtocolEncoder encoder = new Jt600ProtocolEncoder(null);
+ Command command = new Command();
+
+ @Test
+ public void testEngineStop() throws Exception {
+ command.setType(Command.TYPE_ENGINE_STOP);
+ assertEquals("(S07,0)", encoder.encodeCommand(command));
+ }
+
+ @Test
+ public void testEngineResume() throws Exception {
+ command.setType(Command.TYPE_ENGINE_RESUME);
+ assertEquals("(S07,1)", encoder.encodeCommand(command));
+ }
+
+ @Test
+ public void testSetTimezone() throws Exception {
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+4");
+ assertEquals("(S09,1,240)", encoder.encodeCommand(command));
+ }
+
+ @Test
+ public void testReboot() throws Exception {
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+ assertEquals("(S17)", encoder.encodeCommand(command));
+ }
+}
diff --git a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java
new file mode 100755
index 000000000..5596913c9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class KenjiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ KenjiProtocolDecoder decoder = new KenjiProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">C800000,M005004,O0000,I0002,D124057,A,S3137.2783,W05830.2978,T000.0,H254.3,Y240116,G06*17"),
+ position("2016-01-24 12:40:57.000", true, -31.62131, -58.50496));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
new file mode 100644
index 000000000..62b6070b6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class KhdProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ KhdProtocolDecoder decoder = new KhdProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "2929b1000605162935b80d"));
+
+ verifyPosition(decoder, binary(
+ "29298e006d1f29402d181117083846801193910365274500000000f80000227ffc3f00001e00500000000000060088000000220019ffc100000000000000000000000000000000007080002000000016ff893839323534303231303734313134323334333639000800233030302e30306e0d"));
+
+ verifyPosition(decoder, binary(
+ "2929a3002e1780c663170216203353003060811013839500000114f8000000ffff5000000a00000000000000060102003db70d"));
+
+ verifyPosition(decoder, binary(
+ "292980002805162935140108074727801129670365336900000103ffff000082fc0000001e78091b000000360d"));
+
+ verifyPosition(decoder, binary(
+ "29298100280A9F9538081228160131022394301140372500000330FF0000007FFC0F00001E000000000034290D"));
+
+ verifyPosition(decoder, binary(
+ "29298000280A81850A120310095750005281370061190800000232F848FFBBFFFF0000001E000000000000ED0D"));
+
+ verifyPosition(decoder, binary(
+ "29298E00280F80815A121218203116022318461140227000720262FB00077C7FBF5600001E3C3200000000850D"));
+
+ verifyPosition(decoder, binary(
+ "29298200230AA2CC391205030505220285947903109550008002078400000002000000000000750D"));
+
+ verifyPosition(decoder, binary(
+ "29298500081DD08C22120312174026026545710312541700000000F819C839FFFF1D00001E00500000003AF90D"));
+
+ verifyPosition(decoder, binary(
+ "292980002822836665140825142037045343770193879200000050ffff000082fc000004b0780b170000002a0d"));
+
+ verifyPosition(decoder, binary(
+ "292980002802425349120811032137022373011140211100000334FFFF000082FC0000001E780913000034DF0D"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java
new file mode 100644
index 000000000..390defe6f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class KhdProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ KhdProtocolEncoder encoder = new KhdProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("29293900065981972d5d0d"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java
new file mode 100644
index 000000000..5ffa3d8d1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class L100FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ L100FrameDecoder decoder = new L100FrameDecoder();
+
+ verifyFrame(
+ binary("41544c2c4c2c3836383334353033383137313936332c4e2c3230313231382c3039333031362c412c3032352e3036373134342c4e2c3035352e3134343833332c452c3030302e302c4750532c333933392c3432342c30332c30303430352c303038383334"),
+ decoder.decode(null, null, binary("41544c2c4c2c3836383334353033383137313936332c4e2c3230313231382c3039333031362c412c3032352e3036373134342c4e2c3035352e3134343833332c452c3030302e302c4750532c333933392c3432342c30332c30303430352c30303838333440")));
+
+ verifyFrame(
+ binary("4c2c41544c2c3836363739353033303437373935322c30312c303033352c"),
+ decoder.decode(null, null, binary("4c2c41544c2c3836363739353033303437373935322c30312c303033352c2a28")));
+
+ verifyFrame(
+ binary("41544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735242c2330313130303131313030313031302c4e2e432c4e2e432c4e2e432c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c"),
+ decoder.decode(null, null, binary("200141544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735242c2330313130303131313030313031302c4e2e432c4e2e432c4e2e432c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c027a")));
+
+ verifyFrame(
+ binary("41544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735244c4f432c436f6e6e61756768742043697263757320c2a0436f6e6e617567687420506c61636520c2a04e65772044656c686920c2a044656c6869c2a0496e6469612c2330313130303130313130313031302c322e332c33352e36372c38302c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c"),
+ decoder.decode(null, null, binary("200341544c3335363839353033373533333734352c244750524d432c3131313731392e3030302c412c323833382e303034352c4e2c30373731332e333730372c452c302e30302c2c3132303831302c2c2c412a3735244c4f432c436f6e6e61756768742043697263757320c2a0436f6e6e617567687420506c61636520c2a04e65772044656c686920c2a044656c6869c2a0496e6469612c2330313130303130313130313031302c322e332c33352e36372c38302c31323334352e36372c33312e342c342e322c32312c4d43432c4d4e432c4c41432c43656c6c494441544c047a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java
new file mode 100644
index 000000000..04f586f7a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class L100ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ L100ProtocolDecoder decoder = new L100ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "ATL,NP,868004029750174,$GPRMC,062943,A,2533.6719,N,09154.3203,E,0,179,311218,,,*39,#01111011000000,0,0,0,934.82,27.13,4.0,25,405,755,15af,974b,0,0,0,ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL,L,868345038171963,N,201218,093016,A,025.067144,N,055.144833,E,000.0,GPS,3939,424,03,00405,008834"));
+
+ verifyPosition(decoder, text(
+ "N,111116,090031,A,028.123456,N,077.123456,E,000.0,GPS,4180,404,11,00159,064753"));
+
+ verifyAttributes(decoder, text(
+ "L,ATLOBD,866795030475584,03,7429,143344,130918,CAN,0101:00076100,0103:0200,0104:3C,0105:84,010A:XX,010B:19,010C:0F98,010D:22,010E:68,010F:5A,0110:XXXX,0111:28,011C:20,011F:XXXX,0121:0000,0122:XXXX,012F:XX,0162:XX,0132:XXXX,0133:61,0143:00A8,0145:0F,0146:XX,0147:30,0148:XX,0149:31,014A:18,014B:XX,014C:92,0151:XX,0131:00BB,0144:8000,015E:XXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0902:XXXXXXXXXXXXXXX"));
+
+ verifyPosition(decoder, text(
+ "H,ATL,866795030478513,02,0981,054448,230318,A,28.633486;N,77.222595;E,0,154,1.14,4.2,18,404,4,88,ad7b,#1031,0,ATL,"));
+
+ verifyNull(decoder, text(
+ "L,ATL,866795030477952,01,0035,"));
+
+ verifyPosition(decoder, text(
+ "ATL861693039769518,$GPRMC,074930.000,A,2838.0112,N,07713.3602,E,0000,223.36,290518,,,A*7E,#01111011000100,0.012689,0,0,2.572415,0,4.015,22,404,4,88,3ad5,0,0.01,1.4_800F_VTS3D3_gen_peri_myn,,internet,00000000,ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL867857039216564,$GPRMC,131101,A,2838.010010,N,7713.354980,E,0,0,240418,,,*09,#00011011000000,0,0,0,10.70,24.31,3.8,0,0,0,0,0ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL867857039216564,$GPRMC,131033,A,2838.010010,N,7713.354980,E,0,51,240418,,,*3D,#00011011000000,0,0,0,10.70,24.31,3.8,20,404,4,88,cfaaATL"));
+
+ verifyPosition(decoder, text(
+ "ATL868004026997257,$GPRMC,095542,A,2838.0107,N,07713.3579,E,0,98,010617,,,*03,#01111011000000,0,0,0,0.01,45.94,4.0,25,404,4,88,3ad5ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL861693035285253,$GPRMC,022040,A,2954.0481,N,07353.1694,E,0,150,280417,,,*36,#01111011000000,0,0,0,82.92,37.92,4.0,23,404,70,163,b178ATL"));
+
+ verifyPosition(decoder, text(
+ "ATL356895037533745,$GPRMC,111719.000,A,2838.0045,N,07713.3707,E,0.00,,120810,,,A*75,#01100111001010,N.C,N.C,N.C,12345.67,31.4,4.2,21,100,000,000001,00000ATL"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
new file mode 100644
index 000000000..5b66ed865
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
@@ -0,0 +1,132 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class LaipacProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ LaipacProtocolDecoder decoder = new LaipacProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$AVRMC,80006405,212645,r,3013.9938,N,08133.3998,W,0.00,0.00,010317,a,4076,0,1,0,0,53170583,310260*78"));
+
+ verifyNull(decoder, text(
+ "$AVSYS,99999999,V1.50,SN0000103,32768*15"));
+
+ verifyNull(decoder, text(
+ "$ECHK,99999999,0*35"));
+
+ verifyNull(decoder, text(
+ "$AVSYS,MSG00002,14406,7046811160,64*1A"));
+
+ verifyAttributes(decoder, text(
+ "$EAVSYS,MSG00002,8931086013104404999,,Owner,0x52014406*76"));
+
+ verifyNull(decoder, text(
+ "$ECHK,MSG00002,0*5E"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*37"),
+ position("2003-07-18 16:43:39.000", true, 43.85090, -79.39241));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,a,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*17"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,v,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*00"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,r,4351.0542,N,07923.5445,W,0.29,78.66,180703,0,3.727,17,1,0,0*04"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,S,3.727,17,1,0,0*54"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,T,3.727,17,1,0,0*53"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,3,3.727,17,1,0,0*34"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,X,3.727,17,1,0,0*5F"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,99999999,164339,A,4351.0542,N,07923.5445,W,0.29,78.66,180703,4,3.727,17,1,0,0*33"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,003016,v,0000.0000,N,00000.0000,E,0.00,0.00,200614,0,3804,167,1,0,0,0D7AB913,020408*23"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,003049,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,3804,167,1,0,0,0D7AB913,020408*71"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,041942,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,4115,167,1,0,0*0E"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,043703,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,4115,167,1,0,0*07"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,043750,V,0000.0000,N,00000.0000,E,0.00,0.00,200614,H,4115,167,1,0,0*01"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,124022,V,0000.0000,N,00000.0000,E,0.00,0.00,240614,3,4076,167,1,0,0,0D7AB913,020408*0D"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,124058,A,5053.0447,N,00557.8549,E,0.45,65.06,240614,0,4037,167,1,0,0,0D7AB913,020408*26"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,124144,A,5053.0450,N,00557.8544,E,0.00,65.06,240614,3,4076,167,1,0,0,0D7AB913,020408*26"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,125142,R,5053.0442,N,00557.8694,E,1.21,40.90,240614,0,4037,167,1,0,0,0D7AB913,020408*33"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,125517,R,5053.0442,N,00557.8694,E,0.00,0.00,240614,H,4076,167,1,0,0,0D7AB913,020408*75"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,043104,p,5114.4664,N,00534.3308,E,0.00,0.00,280614,0,4115,495,1,0,0,0D48C3DC,020408*52"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,MSG00002,050601,P,5114.4751,N,00534.3175,E,0.00,0.00,280614,0,4115,495,1,0,0,0D48C3DC,020408*7D"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,96414215,170046,p,4310.7965,N,07652.0816,E,0.00,0.00,071016,0,4069,98,1,0,0*04"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,999999999999999,111602,r,5050.1262,N,00419.9660,E,0.00,0.00,120318,0,3843,95,1,0,0,3EE4A617,020610*47"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143456,R,5050.1285,N,00420.0620,E,0.00,309.27,190318,0,3455,119,1,0,0,3EE4A617,020610*54"));
+
+ verifyPosition(decoder, text(
+ "$AVRMC,999999999999999,084514,r,5050.1314,N,00419.9719,E,0.68,306.39,120318,0,3882,84,1,0,0,3EE4A617,020610*4D"));
+
+ // Alarm button
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,142945,R,5050.1254,N,00420.0490,E,0.00,0.00,190318,3,3455,119,1,0,0,3EE4A617,020610*53"));
+
+ // G-Sensor
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143407,R,5050.1254,N,00420.0490,E,0.00,0.00,190318,8,3455,119,1,0,0,3EE4A617,020610*52"));
+
+ // Powered off
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143648,A,5050.1141,N,00420.0525,E,1.24,174.38,190318,H,3455,119,1,0,0,3EE4A617,020610*3E"));
+
+ // No network
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143747,R,5050.1124,N,00420.0542,E,1.34,161.96,190318,a,3416,119,1,0,0*7D"));
+
+ // Zero LAC, CID, MCC, MNC
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143747,P,5050.1124,N,00420.0542,E,1.34,161.96,190318,A,3416,119,1,0,0,0,0*5F"));
+
+ // New unknown parameters
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143747,P,5050.1124,N,00420.0542,E,1.34,161.96,190318,A,3416,119,1,0,0,0,0,0,0*5F"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java
new file mode 100644
index 000000000..7fe405ea8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class LeafSpyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ LeafSpyProtocolDecoder decoder = new LeafSpyProtocolDecoder(null);
+
+ verifyNull(decoder, request(
+ "/?Lat=60.0&Long=30.0"));
+
+ verifyPosition(decoder, request(
+ "/?user=driver&pass=123456&DevBat=80&Gids=200&Lat=60.0&Long=30.0&Elv=5&Seq=50&Trip=1&Odo=10000&SOC=99.99&AHr=55.00&BatTemp=15.2&Amb=12.0&Wpr=12&PlugState=0&ChrgMode=0&ChrgPwr=0&VIN=ZE0-000000&PwrSw=1&Tunits=C&RPM=1000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java
new file mode 100644
index 000000000..65c9cc43b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class M2cProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ M2cProtocolDecoder decoder = new M2cProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "[#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,1,L,0,20,171221,062016,28.647552,77.192841,0,0,0.0,0,0,64,255,11983,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7524\r\n",
+ "#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,2,L,0,20,171221,062019,28.647552,77.192841,0,0,0.0,0,0,64,255,11983,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7528\r\n",
+ "#M2C,2020,P1.B1.H3.F9.R1,102,864547034433966,3,L,0,20,171221,062024,28.647552,77.192841,0,0,0.0,0,0,64,255,16292,0,0,0,0.0,0,0,0,404,4,1F6,4D77,31,0*7523\r\n"));
+
+ verifyPositions(decoder, text(
+ "[#M2C,2020,P1.B1.H1.F1.R1,101,862462038980016,2,L,1,100,170704,074933,28.647556,77.192940,900,194,0.0,0,0,0,255,11942,0,0,0,0,0,0,0,0,30068,5051,0,0,1*8159\r\n"));
+
+ verifyPositions(decoder, text(
+ "[#M2C,2020,P1.B1.H1.F1.R1,101,862462038980016,7,L,0,31,170704,075905,28.647615,77.192970,300,260,0.0,6,7,3,255,11967,0,12,0,0,0,0,0,0,19500,5051,0,27,1*8234\r\n",
+ "#M2C,2020,P1.B1.H1.F1.R1,101,862462038980016,8,L,0,33,170704,075905,28.647615,77.192970,300,260,0.0,6,7,0,255,11942,0,12,0,0,0,0,0,0,20300,5051,0,27,1*8217\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java
new file mode 100644
index 000000000..1c45c976b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class M2mProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ M2mProtocolDecoder decoder = new M2mProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "235A3C2A2624215C287D70212A21254C7C6421220B0B0B"));
+
+ verifyPosition(decoder, binary(
+ "A6E12C2AAADA4628326B2059576E30202A2FE85D20200B"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java
new file mode 100644
index 000000000..c7a7624c0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MaestroProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MaestroProtocolDecoder decoder = new MaestroProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "@353893040202807,705,UPV-02,1,13.2,17,0,0,16/09/11,11:42:49,0.352705,32.647918,1210.5,0.000000,35.33,11,0.8,0.000,0!\0"));
+
+ verifyPosition(decoder, text(
+ "@353893040202807,705,UPV-02,1,13.4,18,0,0,16/09/11,11:43:30,0.352808,32.647990,1211.0,0.000000,80.96,11,0.8,0.000,0!\0"));
+
+ verifyPosition(decoder, text(
+ "@353893040202807,601,UPV-02,0,13.4,10,0,0,16/11/04,17:21:14,0.352793,32.647927,0,0,0,0,99,0.000,0!\0"));
+
+ verifyPosition(decoder, text(
+ "@123451234512345,531,M2MGTW,1,12.5,30,0,0,11/10/10,09:09:09,22.222222,114.141414,45.6,0.0,160.0,8,1,20!"));
+
+ verifyPosition(decoder, text(
+ "@123451234512345,702,M2MGTW,1,14.7,30,0,1,11/10/10,09:09:09,22.222222,114.141414,45.6,25.12,160.0,8,1,25!"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java
new file mode 100644
index 000000000..1d6f80ae3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ManPowerProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ManPowerProtocolDecoder decoder = new ManPowerProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "simei:352581250259539,,,tracker,51,24,1.73,130426023608,A,3201.5462,N,03452.2975,E,0.01,28B9,1DED,425,01,1x0x0*0x1*60x+2,en-us,"),
+ position("2013-04-26 02:36:08.000", true, 32.02577, 34.87163));
+
+ verifyPosition(decoder, text(
+ "simei:352581250259539,,,weather,99,20,0.00,130426032310,V,3201.5517,N,03452.3064,E,1.24,28B9,25A1,425,01,1x0x0*0x1*60x+2,en-us,"));
+
+ verifyPosition(decoder, text(
+ "simei:352581250259539,,,SMS,54,19,90.41,130426172308,V,3201.5523,N,03452.2705,E,0.14,28B9,01A5,425,01,1x0x0*0x1*60x+2,en-us,"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
new file mode 100644
index 000000000..68606a98a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MegastekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MegastekFrameDecoder decoder = new MegastekFrameDecoder();
+
+ verifyFrame(
+ binary("30313337244d47563030322c3335343535303035303239323636392c4756543930302c522c3134313231352c3033313830342c412c2c532c2c452c30302c30332c30302c332e36372c302e3030302c302e30302c3131372e312c302e302c3531302c31302c2c2c2c303030302c303030302c32322c31322c302c202c202c2c312d312c39382c5057204f4e3b21"),
+ decoder.decode(null, null, binary("30313337244d47563030322c3335343535303035303239323636392c4756543930302c522c3134313231352c3033313830342c412c2c532c2c452c30302c30332c30302c332e36372c302e3030302c302e30302c3131372e312c302e302c3531302c31302c2c2c2c303030302c303030302c32322c31322c302c202c202c2c312d312c39382c5057204f4e3b21")));
+
+ verifyFrame(
+ binary("244d47563030322c3031333737373030373533363433342c2c522c3031303131342c3030303035372c562c303030302e303030302c4e2c30303030302e303030302c452c30302c30302c30302c39392e392c302e3030302c302e30302c302e302c38302e3236332c3531302c38392c323334322c303330422c2c303030302c303030302c3230302c39362c302c202c202c2c2c2c54696d65723b21"),
+ decoder.decode(null, null, binary("244d47563030322c3031333737373030373533363433342c2c522c3031303131342c3030303035372c562c303030302e303030302c4e2c30303030302e303030302c452c30302c30302c30302c39392e392c302e3030302c302e30302c302e302c38302e3236332c3531302c38392c323334322c303330422c2c303030302c303030302c3230302c39362c302c202c202c2c2c2c54696d65723b210d0a")));
+
+ verifyFrame(
+ binary("53545832363034373520202020202020202020024f244750524d432c3133313131302e30302c562c2c2c2c2c2c2c3036303931332c2c2c4e2a37362c3232322c30312c383135412c443435352c31312c39372c303030302c303030312c302c54696d65723b3735"),
+ decoder.decode(null, null, binary("53545832363034373520202020202020202020024f244750524d432c3133313131302e30302c562c2c2c2c2c2c2c3036303931332c2c2c4e2a37362c3232322c30312c383135412c443435352c31312c39372c303030302c303030312c302c54696d65723b37350d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
new file mode 100644
index 000000000..1bf3dbd25
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
@@ -0,0 +1,100 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MegastekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MegastekProtocolDecoder decoder = new MegastekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "0132$MGV002,869152024261561,,S,310818,133945,V,3814.35442,N,02144.50662,E,00,00,00,99.9,,,44.2,,202,10,,,13,0,0,0,0,90,,,,11,100,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "0151$MGV002,869152024261561,ID,S,070918,155544,V,3814.35419,N,02144.52113,E,00,00,00,99.9,0.062,,12.3,,202,01,0898,D1BE,8,34,1055,0,0,600,,,,11,010,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "0174$MGV002,014682001957744,014682001957744,R,260318,042537,A,3853.77301,N,07728.66673,W,00,09,00,1.06,0.147,329.51,123.3,,310,26,B46C,5E69375,5,0000,0000,0,,,,,,10,019,Timer,,;!"));
+
+ verifyNull(decoder, text(
+ "0112$MGV002,,GVT900-3,S,010114,000003,,,,,,00,00,00,,0.000,0.00,,0.0,,,,,,0000,0000,14,10,0, , ,,1-0,0,Low Ext Vol;!"));
+
+ verifyPosition(decoder, text(
+ "0170$MGV002,354550056642321,GVT900-3,S,011017,090208,A,1635.8484,N,10446.6095,E,00,09,00,0.91,16.980,257.73,177.6,0.0,457,01,0741,00C0,21,0000,0000,20,10,0, , ,,1-1,54,Dist;!"));
+
+ verifyNull(decoder, text(
+ "0140$MGV002,354550056642321,GVT900-3,S,300917,071731,V,,,,,00,00,00,99.9,0.000,0.00,,0.0,457,01,0741,00CD,,0000,0000,20,10,0, , ,,1-1,94,PW ON;!"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,869152024446923,,S,290816,200627,V,5056.21059,N,00439.25034,E,00,00,00,99.9,,,-25.1,,206,01,0BBB,4418,28,,,,,,,,,01,093,Timer;"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,869152024446923,869152024446923,S,240816,151631,A,5053.83335,N,00424.05702,E,00,10,00,0.88,2.645,76.09,22.7,,206,01,07D1,6600,28,,,,,,,,,01,100,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "STX,013950007137061,$GPRMC,191959.000,A,5203.09602,N,00830.77057,E,5.73,255.27,240716,,,A*62,L,Belt Up,imei:013950007137061,0/5,,Battery=52%,,1,262,03,0084,B20E;FD"));
+
+ verifyPosition(decoder, text(
+ "STX,865067021328417,$GPRMC,064721.000,A,4241.2793,N,02321.9762,E,6.74,346.90,300316,,,1*CA,F,Nil-Alarms,imei:865067021328417,9,559.8,Battery=82%,0,284,03,047E,2B5F;99"));
+
+ verifyNull(decoder, text(
+ "0147$MGV002,354550050292669,GVT900,S,141215,031804,A,,S,,E,00,04,00,5.17,0.000,193.05,117.1,0.0,510,10,041B,0A5E,,0000,0000,22,12,0, , ,,1-1,98,Timer;!"));
+
+ verifyNull(decoder, text(
+ "0137$MGV002,354550050292669,GVT900,R,141215,031804,A,,S,,E,00,03,00,3.67,0.000,0.00,117.1,0.0,510,10,,,,0000,0000,22,12,0, , ,,1-1,98,PW ON;!"));
+
+ verifyPosition(decoder, text(
+ "0125$MGV002,860719020193193,DeviceName,R,240214,104742,A,2238.20471,N,11401.97967,E,00,03,00,1.20,0.462,356.23,137.9,1.5,460,07,262C,0F54,25,0000,0000,0,0,0,28.5,28.3,,,100,Timer;"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,860719020193193,DeviceName,R,240214,104742,A,2238.20471,N,11401.97967,E,00,03,00,1.20,0.462,356.23,137.9,1.5,460,07,262C,0F54,25,0000,0000,0,0,0,28.5,28.3,,,100,Timer;!"),
+ position("2014-02-24 10:47:42.000", true, 22.63675, 114.03299));
+
+ verifyPosition(decoder, text(
+ "STX2010101801 j$GPRMC,101053.000,A,2232.7607,N,11404.7669,E,0.00,,231110,,,A*7F,460,00,2795,0E6A,14,94,1000,0000,91,Timer;1D"));
+
+ verifyPosition(decoder, text(
+ "STX,861001005215757,$GPRMC,180118.000,A,4241.330116,N,2321.931251,E,0.00,182.19,130915,,E,A,F,Nil-Alarms,imei:861001005215757,8,577.0,Battery=38%,0,284,03,03E8,3139;7A"));
+
+ verifyPosition(decoder, text(
+ "STX,865067020439090,$GPRMC,171013.000,A,5919.1411,N,01804.1681,E,0.000,294.41,140815,,,A"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,013777007536434,,R,010114,000057,V,0000.0000,N,00000.0000,E,00,00,00,99.9,0.000,0.00,0.0,80.263,510,89,2342,030B,,0000,0000,200,96,0, , ,,,,Timer;!"));
+
+ verifyPosition(decoder, text(
+ "STX,GerAL22,$GPRMC,174752.000,A,3637.060059,S,6416.2354,W,0.00,0.00,030812,,,A*55,F,,imei:861785000249353,05,180.6,Battery=100%,,1,722,310,0FA6,39D0;8F"));
+
+ verifyPosition(decoder, text(
+ "STX,GerAL22,$GPRMC,000051.000,A,3637.079590,S,6416.2148,W,1.72,332.98,010109,,,A*52,L,,imei:861785000249353,03,275.3,Battery=68%,,1,722,07,0515,1413;41"));
+
+ verifyPosition(decoder, text(
+ "STX,,$GPRMC,001339.000,A,4710.85395,N,02733.58209,E,1.65,238.00,010109,,,A*67,L,Help,imei:013227009737796,0/8,137.1,Battery=100%,,0,226,01,2B9B,BBBF;8D"));
+
+ verifyPosition(decoder, text(
+ "STX,102110830074542,$GPRMC,114229.000,A,2238.2024,N,11401.9619,E,0.00,0.00,310811,,,A*64,F,LowBattery,imei:012207005553885,03,113.1,Battery=24%,,1,460,01,2531,647E;57"));
+
+ verifyPosition(decoder, text(
+ "STX863070014949464 $GPRMC,215942.290,A,4200.1831,N,02128.5904,E,003.1,079.8,090813,,,A*6E,294,02,0064,0F3D,18,17,0000,000000,0000,0.00,0.02,0.00,Store;D8"));
+
+ verifyPosition(decoder, text(
+ "STX123456 $GPRMC,063709.000,A,2238.1998,N,11401.9670,E,0.00,,250313,,,A*7F,460,01,2531,647E,11,87,1000,001001,0000,0.00,0.02,0.00,Timer;4A"));
+
+ verifyPosition(decoder, text(
+ "STX260475 $GPRMC,104032.001,A,4022.1119,N,01811.4081,E,000.0,000.0,060913,,,A*67,222,01,815A,D455,11,99,0000,0001,0,Timer;"));
+
+ verifyPosition(decoder, text(
+ "LOGSTX,123456789012345,$GPRMC,225419.000,A,3841.82201,N,09494.73357,W,12.46,135.33,270914,,,A*47,F,,imei:123456789012345,0/6,,Battery=100%,,0,,,5856,78A3;24"));
+
+ verifyPosition(decoder, text(
+ "LOGSTX,123456789012345,$GPRMC,230551.000,A,3841.81956,N,09494.45403,W,0.00,0.00,270914,,,A*7C,L,,imei:123456789012345,0/7,269.7,Battery=100%,,0,,,5856,78A3;83"));
+
+ verifyPosition(decoder, text(
+ "LOGSTX,123456789012345,$GPRMC,230739.000,A,3841.81895,N,09494.12409,W,0.00,0.00,270914,,,A*70,L,,imei:123456789012345,0/7,269.7,Battery=100%,,0,,,5856,78A3;78"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
new file mode 100644
index 000000000..2d09c626b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class MeiligaoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeiligaoFrameDecoder decoder = new MeiligaoFrameDecoder();
+
+ assertNull(
+ decoder.decode(null, null, binary("00")));
+
+ assertEquals(
+ binary("2424007b8621700151517899553233323835372e3030302c562c333632372e313835342c4e2c30313034352e323130392c452c302e30302c372c3239303131332c2c2a31347c302e307c347c303030307c303030382c303030357c303235443030303230303541374432327c30367c303030314530353527f40d0a"),
+ decoder.decode(null, null, binary("2424007B8621700151517899553233323835372E3030302C562C333632372E313835342C4E2C30313034352E323130392C452C302E30302C372C3239303131332C2C2A31347C302E307C347C303030307C303030382C303030357C303235443030303230303541374432327C30367C303030314530353527F40D0A")));
+
+ assertEquals(
+ binary("2424007b8621700151517899553233323835372e3030302c562c333632372e313835342c4e2c30313034352e323130392c452c302e30302c372c3239303131332c2c2a31347c302e307c347c303030307c303030382c303030357c303235443030303230303541374432327c30367c303030314530353527f40d0a"),
+ decoder.decode(null, null, binary("002424007B8621700151517899553233323835372E3030302C562C333632372E313835342C4E2C30313034352E323130392C452C302E30302C372C3239303131332C2C2A31347C302E307C347C303030307C303030382C303030357C303235443030303230303541374432327C30367C303030314530353527F40D0A")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
new file mode 100644
index 000000000..94f4c8202
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
@@ -0,0 +1,143 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MeiligaoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeiligaoProtocolDecoder decoder = new MeiligaoProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "2424011e143190975469ff99993130343634382e3030302c562c303735332e353338332c4e2c30393832322e313737382c452c302e30302c302c3230303132302c2c2a31417c302e307c307c363430307c303030302c303030302c303130312c303238467c30323038303030353137444630304633363838467c30387c30303030314242367c30307c2520205e59454e53414241494348414924534f4e474b52414e244d522e5e5e3f3b363030373634333130303530303337333835333d3135303531393637303631343d3f2b2020202020202020202020202032342020202020202020202020203120202020202020202020202030303034313131202030303130302020202020202020202020202020202020202020203f7b850d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424008d143190975469ff99993130343634312e3030302c562c303735332e353338332c4e2c30393832322e313737382c452c302e30302c302c3230303132302c2c2a31337c302e307c307c323430307c303030302c303030302c303130302c303238397c30323038303030353137444630304633363838467c30387c30303030314242367c3030be980d0a"));
+
+ verifyNull(decoder, binary(
+ "24240012254748594772ff080002ffff0d0a"));
+
+ verifyNull(decoder, binary(
+ "242403fe254748594772ff99880242681100ffd8ffe000104a46494600010101000000000000ffdb004300080606070605080707070909080a0c140d0c0b0b0c1912130f141d1a1f1e1d1a1c1c20242e2720222c231c1c2837292c30313434341f27393d38323c2e333432ffdb0043010909090c0b0c180d0d1832211c213232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffc000110801e0028003012100021101031101ffda000c03010002110311003f00f0cc679a5a977d84b517d69474a98f615930e3a52edabea50b8a5c54c9e84c9f4178a5c7b50ad617bbd45a5c00287b16d681f852f5eb44aefde32e5bbb0ec0a38eb4d36d9a48753f150f4d5916f74318a70ad5dd8dc75168ed53ccadee8e239452f434afd10921719a7639aad56854ac3c0a5a98cfc8971d6c85a7f6a65ad07e29f8a1b10fc52ff153f405a6e48053f145f4b31abc5928e9cd4cabd3152f4d01ab6a4ca2a50a33509d9d809d56a50b56a7a87c24cbd6a655a2510ea4aa2a555a953b30255a92a80945498a435b8f1520a3d043b14e02974d40752d37a20490b4b40076a2a760129298094949e8030d368527b8c6530d4ddc84861a6353b8119a69e94b402334ca6e5a0861eb4c22b3d876184530d56e85623c506b27cd719e494bdabadab6a2d43e945227588a7d29739e69d92dc7d4703476cd569612d472b6452d4fbb61d828c734d6eac263fb52d539caf7634c55f6a2a1b8885a78e949db94528bd90539714dbea69bea2e7d29d9c51b19db4b053c75abffff0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400716578902405843299553136323533332e3937382c412c343632332e313137392c4e2c30373932342e323437312c572c303030302c3030302c3139313231372c2c2a31437c31312e357c3139347c303030307c313139322c303030307c3835383030307c30303331343809540d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400716578902405843299553136323533332e3937382c412c343632332e313137392c4e2c30373932342e323437312c572c303030302c3030302c3139313231372c2c2a31437c31312e357c3139347c303030307c313139322c303030307c3835383030307c30303331343809540d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424010a142170525979ff9999753137353830322e3030302c412c313330362e303639342c4e2c31303035342e323439302c452c302e30302c3331332c3234313031372c2c2a30457c302e397c377c323530307c303030302c303030302c303130312c303241447c30323038303030353043313330313638353333427c30427c30303032313034357c30417c2520205e59454e53414241494348414924534f4e474b52414e244d522e5e5e3f3b363030373634333130303530303337333835333d3135303531393637303631343d3f2b202020202020202020202020203234202020202020202020202020312020202020202020202020203030303431313120203030313030545c0d0a"));
+
+ verifyPositions(decoder, binary(
+ "2424006661172036237118668801003039333630342e3030302c562c303330332e333231352c4e2c31303134372e313530302c452c302e30302c2c3235313031377c302e307c302e307c303030307c303030302c303030307c30303030303230343259ca0d0a"));
+
+ verifyPositions(decoder, binary(
+ "242401d961172036237118668805003039353830332e3030302c412c303330332e333431392c4e2c31303134372e343130342c452c372e30342c3230362e36312c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031313532325c003039353833332e3030302c412c303330332e323630302c4e2c31303134372e333734342c452c31302e33382c3236332e31342c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031313734355c003039353930332e3030302c412c303330332e313833382c4e2c31303134372e333735362c452c382e34392c3232332e37372c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031313839375c003039353933332e3030302c412c303330332e313033312c4e2c31303134372e333435332c452c382e37312c3139312e35302c3235313031377c302e307c302e307c303230307c303030302c303030307c3030303031323130325c003130303030302e3030302c412c303330332e313032332c4e2c31303134372e333338372c452c302e30302c3231332e36392c3235313031377c302e307c302e307c303030307c303030312c303030307c3030303031323131380d110d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007f1092ffffffffff9999523232303534392e3030302c412c333533372e313231372c4e2c30313130302e303633332c452c362e34382c3139332c3238303631372c2c2a30357c302e387c32347c323030307c303030432c303030417c303235443030303230303833354437427c31357c3037303636424142f7310d0a"));
+
+ verifyAttributes(decoder, binary(
+ "242400561001ffffffffff99553030303030302e3030302c562c303030302e303030302c532c30303030302e303030302c572c302e30302c302e30302c3232303839392c2c2c412a37457c7c307c3030303059ae0d0a0000"));
+
+ verifyPosition(decoder, binary(
+ "242400706573402852404799553130313932372e3030302c412c313732362e38323739332c4e2c30373832382e31393637312c452c312e382c362e342c3137313131362c2c2a32427c312e36387c3534342e327c313030307c303030302c303030307c3030303032383638373a1a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007d0000000000000099553231303333302e3030302c562c343533342e333832342c532c30373230332e303630302c572c302e30302c302c3231313031362c2c2a31327c302e307c3332397c323030307c303030452c303030437c303244413030303145413634393541417c31307c30303030303030306e540d0a"));
+
+ verifyAttributes(decoder, binary(
+ "4040005066104020094432990131302E312C302C3135362C302E30302C31392E36312C2D33342C33342E32362C32312E38332C372E39312C313033332C322E36392C362E35352C302C302C309DBF0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "242400736610402421174399553130353033342e3937382c412c333933352e333638392c4e2c30303233382e313638342c452c303034382c3034322c3038313231362c2c2a31437c31312e357c3139347c313030317c303341362c303030307c30303130343030307c3030303030303cd00d0a2424004e66104024211743990131342e312c323638372c39302c32312e35372c342e37312c38352c372e31302c382e31362c342e32372c3130342c302e33342c392e33342c302c312c30b7160d0a2424003266104024211743990232352c322e34302c302e37392c32322c34332c3131392c333735362c37352c3132e4c90d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400746251103044ffff99553033353033392e3939392c412c323832332e373632312c4e2c31303635322e303730342c572c3030302e302c3030302e302c3136303631362c2c2c412a37357c302e397c323038332e327c303030307c303030302c303030307c31303034333736333265780d0a"));
+
+ verifyPosition(decoder, binary(
+ "24240072190820157fffff99553039343335342e3030302c412c313930372e303631392c4e2c30373235312e333235312c452c3031302e312c3138382e352c3234303231362c2c2c412a36427c302e387c36352e327c303830307c303030302c303030307c303336343838373532c73f0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400680790209818ffff99553038333235382e3030302c412c303131352e393338302c532c30333634382e313430392c452c302e30302c3331352e35352c3132303131367c302e37347c313930322e337c303430307c303030302c303030307c302e30f41b0d0a"));
+
+ verifyNull(decoder, binary(
+ "24240011671440258855405000b24d0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400706796502079108999553131333131382e3030302c412c313033372e393637382c4e2c30363132312e353637392c572c302e35342c322e34322c3330303931352c2c2c412a37307c302e37377c392e397c303030307c303030302c303161327c3030313138373132374cae0d0a"),
+ position("2015-09-30 11:31:18.000", true, 10.63280, -61.35947));
+
+ verifyPosition(decoder, binary(
+ "24240074630700194707719966009E1F7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007C3132303334302C3238303630362C323430302E303030302C4E2C31323130302E303030302C458F7E0D0A"),
+ position("2006-06-28 12:03:40.000", true, 24.00000, 121.00000));
+
+ verifyPosition(decoder, binary(
+ "24240076220720151fffff99660012b3ab00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c3135303634382c3233303731352c313931352e37323835362c4e2c30373235322e35333034342c456dd00d0a"));
+
+ verifyNull(decoder, binary(
+ "24240000123456FFFFFFFF50008B9B0D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400722015032700004299553134313131352e3030302c412c353031312e343335342c4e2c30303834332e373039322c452c3030302e302c3034342e362c3134303431352c2c2c412a36437c322e317c39392e347c303030307c303030302c303030307c3030303032343730350e480d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424006e241120141fffff99553039333931302e30302c412c313931342e37373736352c4e2c30373235302e36383037322c452c302e3035312c2c3230313231342c2c2c442a37357c302e38327c322e387c303030307c303030302c303030307c3031303833373433311d170d0a"));
+
+ verifyPosition(decoder, binary(
+ "24240000123456FFFFFFFF99553033353634342E3030302C412C323233322E363038332C4E2C31313430342E383133372C452C302E30302C2C3031303830392C2C2A31437C31312E357C3139347C303030307C303030302C3030303069620D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400003358019703581F99553133343335312E3030302C412C303932352E353032352C4E2C30363931342E383130372C572C302E30302C32332C3330313031322C2C2A32437C302E387C3138367C323030307C303132362C303046467C303244453030303244384344423431357C30367C303046443642373995820D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400001691000484124F99553134303630332E3030302C412C303933342E323535342C4E2C30363931332E303936362C572C302E30302C2C3330313031322C2C2A30327C302E387C3230377C30303030FA420D0A"));
+
+ verifyPosition(decoder, binary(
+ "2424000045124220306FFF9999143135353432322E3030302C562C323233302E373632332C4E2C31313430332E343231382C452C302E30302C302C3036303231312C2C2A31417C302E307C32367C303030307C303030302C303030307C303030303030303030303030303030307C36337C3030303030303030BAC10D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400008621700142458F9999503139323935382E3030302C412C333632372E313639392C4E2C30313034332E353632372C452C302E30302C3233392C3039313231322C2C2A30467C312E307C377C303030307C303141392C303139377C303235443030303230303541383639467C31327C3030303333424233E2480D0A"));
+
+ verifyPosition(decoder, binary(
+ "24240000123456789FFFFF99553032303630302E3933302C412C323330392E323035312C4E2C31313331382E383434392C452C302E30302C302E30302C3039303731302C2C2C412A36417C322E367C39362E377C303030307C303030302C334646467C303030303030303030C4520D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400005977203744058499553032303131372E3030302C412C343131372E393231322C4E2C30383133302E323039362C572C302E30302C3330332E38352C3236303231337C312E367C30307C303030307C303030302C3030303071CD0D0A"));
+
+ verifyPosition(decoder, binary(
+ "242400003511111111111199553133343734332e3030332c412c303634382e393836362c532c31303730372e353739352c452c3030302e302c3030302e302c323630333133f3150d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400000091800369764199553038353133302e3030302c412c333035332e313634352c4e2c30373535352e373437362c452c302e30302c32372c3136303431332c2c2a33467c302e387c3234357c323030307c303346372c303030302c303030302c303031422c303030302c303030302c303030302c303030307c303139343030303230314343363237437c31417c3031313630383439e6a70d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424000026016100901fff99553136353835332c412c343130392e36303231322c4e2c3833382e35393131392c572c332e3838332c31322e30302c3034303731332c2c2a34467c322e357c3131307c000600007c0000010f7c303242302c3042333697740d0a"));
+
+ verifyNull(decoder, binary(
+ "2424000067622010053562aa0000010001ae4f00000007800000003039353135362e3030302c412c323632332e383936362c4e2c30353030352e303638302c452c32342e352c3234312e302c323231303133599c0d0a"));
+
+ verifyPosition(decoder, binary(
+ "242400001007ffffffffff99553136323330392e3035342c562c303933312e393136332c4e2c30363931312e383233332c572c2c2c3235313131332c2c2c4e2a36437c7c3135387c303030309cc60d0a"));
+
+ verifyAttributes(decoder, binary(
+ "242400003563070435652099553035323034322e3030302c412c343435382e333536352c4e2c30343130342e343831332c452c302e30302c302e30302c3139303131342c2c2a39437c302e3730303030307c2d3835393131373337367c303130307c307c7c7c4f2a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424005035784251ffffff99553030303033362e3938312c562c303933312e333437312c4e2c30363931312e383431322c572c2c2c3238303131342c2c2c4e2a36357c7c3136387c323030305e420d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007811223344ffffff99553031303735372e3030302c412c323935392e313337342c4e2c30393534302e333238342c572c3030302e302c3038382e372c3234303631342c2c2c412a37397c312e347c33352e317c303030307c303030312c303030307c3030303030333732337c3030303030e39f0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007f3568960306760199553131303932342e3030302c412c343533342e323538352c4e2c30313933382e363531342c452c302e30302c2c3237303731342c2c2c412a37317c312e377c3130307c383030307c303737422c303030302c303030302c303030302c303030302c303030302c303030302c303030305dfc0d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424007f2015603256ffff99553230303230392e30302c412c313734342e36393434382c4e2c30383331392e30353537302c452c31302e3236322c3234382e35352c3236303631352c2c2c442a35437c302e39397c33312e397c303830317c303030302c303030637c3030303933303131367c3030376165313035d5550d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java
new file mode 100644
index 000000000..d7aa25be8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java
@@ -0,0 +1,41 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class MeiligaoProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MeiligaoProtocolEncoder encoder = new MeiligaoProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ verifyCommand(encoder, command, binary("404000111234567890123441016cf70d0a"));
+
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 100);
+
+ verifyCommand(encoder, command, binary("40400013123456789012344102000a2f4f0d0a"));
+
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+8");
+
+ verifyCommand(encoder, command, binary("4040001412345678901234413234383030ad0d0a"));
+
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+
+ verifyCommand(encoder, command, binary("40400011123456789012344902d53d0d0a"));
+
+ command.setType(Command.TYPE_ALARM_GEOFENCE);
+ command.set(Command.KEY_RADIUS, 1000);
+
+ verifyCommand(encoder, command, binary("4040001312345678901234410603e87bb00d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java
new file mode 100644
index 000000000..53749816e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class MeitrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeitrackFrameDecoder decoder = new MeitrackFrameDecoder();
+
+ assertEquals(
+ binary("24244e3132372c3836333037313031333830333036362c4141412c33352c2d312e3330323638302c33362e3835323133352c3135303430393231313032362c412c392c302c302e312c302c352c313635332c343039362c33323634382c3633397c30327c313030347c3930432c303030302c307c307c307c3346467c3330302c2a37430d0a"),
+ decoder.decode(null, null, binary("24244e3132372c3836333037313031333830333036362c4141412c33352c2d312e3330323638302c33362e3835323133352c3135303430393231313032362c412c392c302c302e312c302c352c313635332c343039362c33323634382c3633397c30327c313030347c3930432c303030302c307c307c307c3346467c3330302c2a37430d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
new file mode 100644
index 000000000..3e05d5243
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -0,0 +1,110 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MeitrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "242441313038362c3836343530373033313231393937342c4430302c3138303232343037323631345f4331453130395f4e31553144312e6a70672c31342c302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00cca69ac8d06e3348569884db4845021b498a60371494008692980119a8ca7a5342101a5cd5221a0ab312ed1ee68b943e80dce2a467ffd0c806a48e592270f13b230e841a0096eeea7bb09e6c85b667033552800069c2980e14f15422418a916ad099228a95455089505584140993a2d5fb598a7cae72bd8fa536ae892e8e69e2b9d971168a459fffd1ece8a0028a006b534f4a68ce5b9130a89ab444919a61a6c634d34d21894952310d25002514084a4a00ffd2d2349564086929082929805250025140094940c4a4a04251400949408292819fffd3cca31591a098a5c62801a45464531098a69a6210d371400629a6980628eb400c64cd3791c1aa16c491479393563343105424fcc4d007ffd4c3463c0a94500381a5e3b8cd000c99e57f2a8f3835402834e0d4d08914d4aa6ac4c954d4aad4c4c955aa647a6496236ab51b552132e412e383d3f955b0722b09ab32a0c5a2a0d0ffd5ece8a0028a0061a6d5193dc8daa36ab422334c34c634"));
+
+ verifyPositions(decoder, binary(
+ "24246b3131342c3836353738393032343134303439352c4343452c0000000001005000130006011f05010607071415001b00060800000949010a0c000b9b0119a1011afe010602e934ce0203fc9aeb0004309f13220cafc503000d97741e001c01000000010e0ce8000300092f2e060000b7ff2a33330d0a"));
+
+ verifyPosition(decoder, buffer(
+ "$$^182,864507031245110,AAA,109,13.844553,100.644360,171227173141,A,11,19,0,359,0.8,8,15075,934591,520|4|0643|07D20555,8400,0000|0000|0000|018D|04CB,,,108,0000,,6,0,,,,,10|171227173100*7C"));
+
+ verifyPosition(decoder, buffer(
+ "$$S214,864507031219974,AAA,109,13.844643,100.644395,171207021520,A,10,28,0,31,0.8,6,390,421327,520|0|0016|000F2DB0,8400,0000|0000|0000|018D|04C6,,,108,0000,,6,0,,,,,11|171207091500|171207091500|78|3500|000000|000003*12"));
+
+ verifyPositions(decoder, binary(
+ "24245f3237382c3836353738393032313434373233322c4343452c5b00000003005000130006012305010608070d15001b0006080000091e010a09000b2e0019a1011af90106025c033300039be60c06044f6678210c6f1806000d48db06001c41000000010e0cf60113002005912b830001ff5000130006012305010608070d15001b0006080000091e010a09000b2e0019a0011af90106025c033300039be60c0604506678210c6f1806000d49db06001c41000000010e0cf60113002005912b830001ff5000130006012305010608070d15001b0006080000091e010a09000b2e0019a1011af90106025c033300039be60c0604516678210c6f1806000d4adb06001c41000000010e0cf60113002005912b830001ff2a37460d0a"));
+
+ verifyPosition(decoder, buffer(
+ "$$V177,863835026871173,AAA,35,34.516428,10.470160,170915154043,A,9,12,68,74,0.9,9,1988259,525882,605|2|008C|0007B5A6,0200,0003|0000|0000|01A6|0571,00000001,,3,0000,06FB2E,360,511*74"));
+
+ verifyPosition(decoder, buffer(
+ "$$V177,863835026871173,AAA,35,34.516428,10.470160,170915154043,A,9,12,68,74,0.9,9,1988259,525882,605|2|008C|0007B5A6,0200,0003|0000|0000|01A6|0571,00000001,,3,0000,010A92,360,511*74"));
+
+ verifyPosition(decoder, buffer(
+ "$$B136,011691002364761,AAA,29,47.055220,28.893193,170914144240,V,0,7,0,0,0,132,129754946,129793197,259|2|02F8|413F,0000,000D|000C||028C|,*9E"));
+
+ verifyNotNull(decoder, buffer(
+ "$$F153,863835026880190,AAA,29,25.313160,55.422473,170628150902,V,0,0,0,0,0.0,0,6553,6697,0|0|0000|00000000,0000,0002|0000|0000|018B|0000,,,3,0000,,110,386*22"));
+
+ verifyPosition(decoder, buffer(
+ "$$T143,869013024733944,AAA,1,18.459575,-69.947161,170220142912,A,5,15,10,300,1.6,115,3989,187884,370|2|5337|2B2C,0100,0000|0000|0000|0964|0B04,,*C2"));
+
+ verifyPosition(decoder, buffer(
+ "$$K157,866771027160687,AAA,3,37.040231,10.042391,160412151656,A,10,11,0,48,0.8,21,1035518,774980,605|2|0010|307B,0400,0000|0000|0000|0A47|03E3,,,1,0000,001206*2C"));
+
+ verifyNull(decoder, buffer(
+ "$$D28,353358017784062,D03,OK*F3"));
+
+ verifyPosition(decoder, buffer(
+ "$$A158,79007001520234,AAA,35,40.996370,-8.575065,150730184834,A,8,24,0,1,1.3,173,32573389,31405012,268|3|2BC0|250B,2000,|||0A2D|0000,00000001,,50,,,,,,,,,,,,,*4A"),
+ position("2015-07-30 18:48:34.000", true, 40.99637, -8.57507));
+
+ verifyPosition(decoder, buffer(
+ "$$G145,862106024274815,AAA,35,-1.287125,36.906061,150530054639,A,10,13,12,67,0.8,1621,38359791,42330881,639|2|FB2|2F3,0000,3|0|0|A58|432,,,1,0009,*26"));
+
+ verifyPosition(decoder, buffer(
+ "$$I152,013949004569813,AAA,37,54.739468,25.273648,150208173414,A,5,24,0,73,1.5,165,74,3381,246|1|0065|118A,0000,0003|0003|0000|08D4|0002,006380DF,,1,0008*7C"));
+
+ verifyPosition(decoder, buffer(
+ "$$E141,863071013799553,AAA,35,-1.264521,36.801128,150307132846,A,11,20,0.2,0,5,1767,84045888,36496633,639|02|100E|844,1234,0018|||025D|00CB,*17"));
+
+ verifyPosition(decoder, buffer(
+ "$$m140,013777008931857,AAA,1,54.739580,25.273263,141120144603,V,0,25,0,6,50.0,159,19825,13940,246|1|0065|118A,0100,0000|0000|0000|092A|0001,,*1C"));
+
+ verifyPosition(decoder, buffer(
+ "$$X138,862170010187175,AAA,35,-29.960365,-51.655455,130507201625,A,8,9,0,107,0.9,7,169322,126582,724|6|0547|132B,0000,0009|000A||0278|0000,*BE"));
+
+ verifyPosition(decoder, buffer(
+ "$$X138,862170010187175,AAA,35,-29.960365,-51.655455,130507201625,A,8,9,0,107,0.9,-7,169322,126582,724|6|0547|132B,0000,0009|000A||0278|0000,*BE"));
+
+ verifyPosition(decoder, buffer(
+ "$$]138,012896000475498,AAA,35,-6.138255,106.910545,121205074600,A,5,18,0,0,0,49,3800,24826,510|10|0081|4F4F,0000,0011|0012|0010|0963|0000,,*94"));
+
+ verifyPosition(decoder, buffer(
+ "$$d138,012896000475498,AAA,35,-6.138255,106.910545,121205074819,A,7,18,0,0,0,49,3800,24965,510|10|0081|4F4F,0000,000D|0010|0012|0963|0000,,*BF"));
+
+ verifyPosition(decoder, buffer(
+ "$$j138,012896000475498,AAA,35,-6.138306,106.910655,121205103708,A,3,11,0,0,1,36,4182,35025,510|10|0081|4F4F,0000,000A|000C|000A|0915|0000,,*BF"));
+
+ verifyPosition(decoder, buffer(
+ "$$m139,012896005334567,AAA,35,-33.866423,151.190060,121208020649,A,7,27,0,32,4,13,6150,49517,505|2|0B67|5A6C,0000,0000|0000|0000|0977|0000,,*F1"));
+
+ verifyPosition(decoder, buffer(
+ "$$A141,012896005334567,AAA,35,-33.866543,151.190148,121209081758,A,6,27,0,16,1,48,65551,152784,505|2|0B5F|D9D3,0000,0000|0000|0000|0A39|0000,,*5B"));
+
+ verifyPosition(decoder, buffer(
+ "$$_128,861074020109479,AAA,34,22.512618,114.057065,090215000318,V,0,31,0,0,0,0,0,733,302|720|3EE4|BBB5,0000,0006|0006||028C|0000,*E3"));
+
+ verifyPosition(decoder, buffer(
+ "$$K146,013227004985762,AAA,35,28.618005,-81.246783,131101213828,A,9,22,0,209,1.1,23,80974,1187923,310|260|2A13|634E,0000,0000|0000|0000|09DA|0B34,,*51"));
+
+ verifyPosition(decoder, buffer(
+ "$$E150,013777001165479,AAA,35,10.296601,123.872115,140501161505,A,4,22,1,170,1.4,77,39097,393563,515|3|A0CC|ED96,0000,0008|0003|0000|09D5|0000,,,1,0009*1E"));
+
+ verifyPosition(decoder, buffer(
+ "$$B140,013777001293701,AAA,35,-7.266760,112.743550,140521095314,A,3,22,0,275,2.7,45,1984,8059,510|1|3504|EBFE,0000,0000|0000|0000|0914|0002,,*F9\r\n"));
+
+ verifyPosition(decoder, buffer(
+ "$$J163,123123123123123,AFF,0004,35,58.588926,16.180473,140928192856,A,10,27,0,161,1.2,19,1648894,435695,240|24|88B9|E435,0000,|||0A22|0000,00000001,,50,,,,,,,,,,,,,*70\r\n"));
+
+ verifyPositions(decoder, binary(
+ "24245838362c3336393830303031343039303032312c4343432c020134000100000023381f91ffe354b806c5e3121b0009130000000000000000d33801007cbf0200fe0101000435feeb02000500a3010000000000002a62650d0a"),
+ position("2014-05-24 04:59:49.000", false, -7.26650, 112.74365));
+
+ verifyPositions(decoder, binary(
+ "2424473937302c3336393830303031333436303637342c4343432c020134005b000000010ce304035db9e000ec6f591a000013000000000c001801edb70200c96d0100e60001004838576501000300a101c20400000000010ce304035db9e000ee6f591a000013000000000c001801edb70200ca6d0100e60001004838576501000300a101c20400000000010ce304035db9e000ef6f591a000013000000000c001801edb70200cc6d0100e60001004838576501000300a101c20400000000020ce304035db9e000f76f591a000016000000000c001801edb70200d36d0100e60001004838576502000300a101bf04000000000a0ce304035db9e000f76f591a000016000000000c001801edb70200d46d0100e60001004838576500000300a101bf0400000000020ce304035db9e000fb6f591a000016000000000c001801edb70200d86d0100e60001004838576502000300a101760400000000180ce304035db9e000fc6f591a0000120000000000008c00edb70200d96d0100e60001004838576502000300a10176040000000019b1e2040323b9e0000b70591a0105150600bb0012002901edb70200e76d0100e60001004838576502000300a2017005000000002023e304031fb9e0001070591a010615070027010d001601fcb70200ec6d0100e60001004838576502000300a201800500000000201fe3040302b9e0001170591a010615090019010d001501feb70200ed6d0100e60001004838576502000300a2018005000000002018e30403dcb8e0001270591a0106150b0011010d00150100b80200ee6d0100e60001004838576502000300a2018005000000002036e3040345b8e0001570591a0107150b002d010b0013010ab80200f16d0100e60001004838576502000300a2018005000000002053e3040326b8e0001670591a0107150d0041010b0013010eb80200f26d0100e60001004838576502000300a2018005000000002070e3040310b8e0001770591a0107150e004f010b00130111b80200f36d0100e60001004838576502000300a2018005000000002095e3040306b8e0001870591a0107150d005a010b00140115b80200f46d0100e60001004838576502000300a20180050000000020b3e3040305b8e0001970591a0107150b0060010b00140118b80200f56d0100e60001004838576502000300a20183050000000020cfe3040308b8e0001a70591a0107150b0066010b0014011bb80200f66d0100e60001004838576502000300a20183050000000020eee304030cb8e0001b70591a0106170b0004000d0014011eb80200f76d0100e60001004838576502000300a2018305000000002a62350d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
new file mode 100644
index 000000000..2b5054cee
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class MeitrackProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MeitrackProtocolEncoder encoder = new MeitrackProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ assertEquals("@@Q25,123456789012345,A10*68\r\n", encoder.encodeCommand(command));
+
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REQUEST_PHOTO);
+
+ assertEquals("@@D46,123456789012345,D03,1,camera_picture.jpg*1F\r\n", encoder.encodeCommand(command));
+
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SEND_SMS);
+ command.set(Command.KEY_PHONE, "15360853789");
+ command.set(Command.KEY_MESSAGE, "Meitrack");
+
+ assertEquals("@@f48,123456789012345,C02,0,15360853789,Meitrack*B0\r\n", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
new file mode 100644
index 000000000..794b2b57e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MictrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MictrackProtocolDecoder decoder = new MictrackProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "MT;6;866425031361423;R0;10+190109091803+22.63827+114.02922+2.14+69+2+3744+113"),
+ position("2019-01-09 09:18:03.000", true, 22.63827, 114.02922));
+
+ verifyAttributes(decoder, text(
+ "MT;6;866425031377981;R1;190108024848+6a:db:54:5a:79:6d,-91,00:9a:cd:a2:e6:21,-94+3+3831+0"));
+
+ verifyAttributes(decoder, text(
+ "MT;1;866425031379169;R2;181129081017+0,21681,20616,460+4+3976+0"));
+
+ verifyAttributes(decoder, text(
+ "MT;1;866425031379169;R3;181129081017+0,167910723,14924,460,176+4+3976+0"));
+
+ verifyAttributes(decoder, text(
+ "MT;6;866425031377981;R12;190108024848+6a:db:54:5a:79:6d,-91,00:9a:cd:a2:e6:21,-94+0,21681,20616,460+3+3831+0"));
+
+ verifyAttributes(decoder, text(
+ "MT;6;866425031377981;R13;190108024848+6a:db:54:5a:79:6d,-91,00:9a:cd:a2:e6:21,-94+0,167910723,14924,460,176+3+3831+0"));
+
+ verifyAttributes(decoder, text(
+ "MT;5;866425031379169;RH;5+190116112648+0+0+0+0+11+3954+1"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java
new file mode 100644
index 000000000..be0209975
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MilesmateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MilesmateProtocolDecoder decoder = new MilesmateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "ApiString={A:861359037373030,B:09.8,C:00.0,D:083506,E:2838.5529N,F:07717.8049E,G:000.00,H:170918,I:G,J:00004100,K:0000000A,L:1234,M:126.86}"));
+
+ verifyPosition(decoder, text(
+ "ApiString={A:861359037496211,B:12.7,C:06.0,D:060218,E:2837.1003N,F:07723.3162E,G:016.80,H:310818,I:G,J:10010100,K:0000000A,L:1234,M:358.33}"),
+ position("2018-08-31 06:02:18.000", true, 28.61834, 77.38860));
+
+ verifyPosition(decoder, text(
+ "ApiString={A:862631032208018,B:12.1,C:24.4,D:055852,E:2838.5310N,F:07717.8126E,G:000.0,H:200117,I:G,J:10100100,K:1000000A,L:1234,M:324.45}"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
new file mode 100644
index 000000000..afa930e5b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
@@ -0,0 +1,73 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MiniFinderProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MiniFinderProtocolDecoder decoder = new MiniFinderProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "!1,867273023933661,V07S.5701.1621,100"));
+
+ verifyAttributes(decoder, text(
+ "!3,ok"));
+
+ verifyNull(decoder, text(
+ "!1,123456789012345"));
+
+ verifyNull(decoder, text(
+ "!5,17,V"));
+
+ verifyNull(decoder, text(
+ "!1,860719027585011"));
+
+ verifyPosition(decoder, text(
+ "!D,02/05/17,19:56:17,47.083542,15.482373,0,0,100001,479.3,100,4,9,0"));
+
+ verifyPosition(decoder, text(
+ "!D,15/04/17,13:58:53,51.483067,-0.452548,60,180,140001,28.7,47,4,13,0"));
+
+ verifyPosition(decoder, text(
+ "!D,07/04/17,05:42:26,-37.588970,145.121231,0,0,0c0001,185.2,92,7,14,1.2"));
+
+ verifyPosition(decoder, text(
+ "!D,28/11/16,00:04:09,42.926067,-85.747589,124,236,140001,179.8,60,11,16,0"));
+
+ verifyPosition(decoder, text(
+ "!C,30/1/16,1:1:6,31.259157,30.020910,0,0,100001,25.32,100,0.03,0.01,0"));
+
+ verifyPosition(decoder, text(
+ "!A,26/10/12,00:28:41,7.770385,-72.215706,0.0,25101,0"));
+
+ verifyPosition(decoder, text(
+ "!A,01/12/10,13:25:35,22.641724,114.023666,000.1,281.6,0"));
+
+ verifyPosition(decoder, text(
+ "!D,08/07/15,04:01:32,40.428257,-3.704808,0,0,170001,701.7,22,5,14,0"));
+
+ verifyPosition(decoder, text(
+ "!D,08/07/15,04:55:13,40.428257,-3.704932,0,0,180001,680.0,8,8,13,0"));
+
+ verifyPosition(decoder, text(
+ "!D,08/07/15,02:01:32,40.428230,-3.704950,4,170,170001,682.7,43,6,13,0"));
+
+ verifyNull(decoder, text(
+ "!1,860719020212696"));
+
+ verifyPosition(decoder, text(
+ "!D,22/2/14,13:40:58,56.899601,14.811541,0,0,1,176.0,98,5,16,0"),
+ position("2014-02-22 13:40:58.000", true, 56.89960, 14.81154));
+
+ verifyPosition(decoder, text(
+ "!D,22/2/14,13:47:51,56.899517,14.811665,0,0,b0001,179.3,97,5,16,0"));
+
+ verifyPosition(decoder, text(
+ "!D,3/7/13,6:35:30,22.645952,114.040436,0.0,225.8,1f0001,12.11,98,0,0,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java
new file mode 100644
index 000000000..5de1346a2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class MiniFinderProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MiniFinderProtocolEncoder encoder = new MiniFinderProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+1");
+
+ assertEquals("123456L+01", encoder.encodeCommand(command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, 2);
+ command.set(Command.KEY_PHONE, "1111111111");
+
+ assertEquals("123456C1,1111111111", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
new file mode 100644
index 000000000..5898b74a8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Minifinder2ProtocolDecoder decoder = new Minifinder2ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "ab10150076f1320003100133353534363530373130323933303602105a"));
+
+ verifyNotNull(decoder, binary(
+ "ab1024009b3f9742011001383635323039303336333430303235113154cfc95d0a00000080d0c95d0a000000"));
+
+ verifyPosition(decoder, binary(
+ "ab103f007e2533000110013335353436353037313032393330360930e09d245d210100000924b49e245d01025b201620e6c03b1ef367420400000000aa026d00c90e0000100110"));
+
+ verifyAttributes(decoder, binary(
+ "ab1845005d39370301100133353836383830303030303338303209245b92b55c84004b610502001000002221ca00050b4a005cc30f4a0056c80f4a003ba90e4a0055c8074a005dc3034a0057c8"));
+
+ verifyAttributes(decoder, binary(
+ "ab185c001db78b03011001333538363838303030303033383032092448bd8a5c82003b130502010000003922ca923bad10f794bd30b5c2cb0595b2944a0c49a4f9b6a4b1e9991e79ba0026bb78c08fb4581faae7ee3fb0e091f5778e96b074a78ed46528"));
+
+ verifyNotNull(decoder, binary(
+ "ab18ba0339dd89030110013335383638383030303030333830320924b9298a5c940083392221ca0005154a005dc3144a003ba90d4a00876c0d4a00406c0c4a00856c0a4a0056c80924a92a8a5c94003b452221ca0005154a005dc3154a003ba9124a0056c80e4a00876c0d4a00856c064a0057c80924d52b8a5c94007b4e2221ca0005124a005dc31a4a003ba90d4a0056c80d4a005cc30c4a00856c0c4a00406c1931a4298a5c8c050000d02a8a5c7e040000fc2b8a5c422200000924012d8a5c94009b592221ca0005134a005dc3174a003ba9164a0056c8114a00856c104a00406c0f4a0057c809242d2e8a5c9400bb5f2221ca00051a4a005dc3164a003ba9124a0056c8104a00406c0d4a00856c0c4a005cc30924592f8a5c9400bb642221ca00051a4a005dc3154a003ba9114a00406c104a0056c80c4a00856c0c4a005cc3092485308a5c9400bb642221ca00051a4a005dc3154a003ba9114a0056c8104a00406c0c4a005cc30b4a0057c80924b1318a5c9400bb642221ca00051a4a005dc3154a003ba9124a0056c80c4a00856c0b4a005cc30a4a0057c80924dd328a5c9400bb642221ca00051a4a005dc3154a003ba9114a0056c8104a00406c0c4a00856c0b4a005cc30931542e8a5c34440000092409348a5c9400bb642221ca00051a4a005dc3134a003ba9124a0056c80c4a005cc30c4a00856c0a4a0057c8092436358a5c9400bb642221ca00051b4a005dc3164a003ba9124a0056c8114a00406c0c4a00856c0c4a005cc3092462368a5c9400bb642221ca00051b4a005dc3154a003ba9134a0056c80f4a00406c0e4a005cc30c4a00856c09248e378a5c9400bb642221ca00051b4a005dc3174a003ba9134a0056c8104a00406c0e4a005cc30c4a00856c0924ba388a5c9400bb642221ca00051b4a005dc3164a003ba9134a0056c8114a00406c0d4a00856c0c4a0057c80924e6398a5c9400bb642221ca00051b4a005dc3134a0056c8104a00406c0e4a00856c0c4a005cc30a4a0057c80924123b8a5c9400b3642221ca0005194a005dc3184a003ba9134a0056c8104a00406c0d4a00856c0d4a005cc309243e3c8a5c9400bb642221ca00051a4a005dc3164a003ba9114a0056c8104a00406c0e4a00856c0c4a005cc309246a3d8a5c940063642221ca00051a4a005dc3174a003ba9114a00406c0f4a0056c80d4a00856c0c4a0057c80924963e8a5c840083642221ca0005144a005dc31d4a0056c81a4a00d73a174a003ba9164a00856c134a005cc30924c43f8a5c840083632221ca0005164a005cc31b4a0056c8174a003ba9164a006903134a005dc3124a006803"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java
new file mode 100644
index 000000000..60351d497
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MotorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MotorProtocolDecoder decoder = new MotorProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "341200007E7E00007E7E020301803955352401161766210162090501010108191625132655351234567F12345F"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java
new file mode 100644
index 000000000..c4f15d907
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MtxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MtxProtocolDecoder decoder = new MtxProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "#MTX,353815011138124,20101226,195550,41.6296399,002.3611174,000,035,000000.00,X,X,1111,000,0,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
new file mode 100644
index 000000000..834a35011
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MxtProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MxtProtocolDecoder decoder = new MxtProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "01a631144c7e0008643ad2f456fb2d49747cfe4cbe0ffd002008800000001021000fd43d3f1403000000ff300000f42760001031102445a81fda04"));
+
+ verifyPosition(decoder, binary(
+ "01a631361e7a00082471418b052a2c46b587ffc01ae3fd000008800000000000003345422203000000f000f00000000000ea1e04"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d00086440628d226e2bc26a97feac8a3afd10210010308000000000000018003d2b10240000005e2f0000f427f21031feff0000593804"));
+
+ verifyPosition(decoder, binary(
+ "01a631bd777d0008646e319e17292ce86798fed4cd3afd102110211030800000102403001f15003e2b102400000034300000f4271021007b175535a7be04"));
+
+ verifyPosition(decoder, binary(
+ "01a631e3f97e00087cf40a98151c2cc46898fee0ce3afd1021001030c0000006102116072e003829bb00000036102100001024000000062b0000f42730004b06a6384b4304"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d00086468457a466a2bc26a97feac8a3afd10212010308000000000001fe1053d291024000000922f0000f4271021007b17553599bb04"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d0008648645ec486a2bc26a97feac8a3afd1021001030c0000000001419eb05372b1024000000982a0000f4271021007b17000010308c04"));
+
+ verifyPosition(decoder, binary(
+ "01a631e3f97e00087cfa0af3151c2c126798febace3afd1021801030c0000006102122082f003e29bb00000037102100001024000000ab2f0000f42730004b060000488c04"));
+
+ verifyPosition(decoder, binary(
+ "01a631e3f97e00087cfe0a4b161c2c126798febace3afd1021801030800000071021240731003e2abb00000038102100001024000000c12f0000f42730004b06a638633104"));
+
+ verifyPosition(decoder, binary(
+ "01a63118787d0008648645ec486a2bc26a97feac8a3afd1021001030c0000000001419eb05372b1024000000982a0000f4271021007b17000010308c04"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java
new file mode 100644
index 000000000..2db4afbf2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NavigilProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NavigilProtocolDecoder decoder = new NavigilProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01004300040020000000f60203080200e7cd0f510c0000003b00000000000000"));
+
+ verifyPosition(decoder, binary(
+ "0100b3000f0024000000f4a803080200ca0c1151ef8885f0b82e6d130400c00403000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java
new file mode 100644
index 000000000..0ebfeacd2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.traccar.ProtocolTest;
+
+import org.junit.Test;
+
+public class NavisFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeNtcb() throws Exception {
+
+ NavisFrameDecoder frameDecoder = new NavisFrameDecoder();
+
+ verifyFrame(binary(
+ "404e5443010000000000000059009adb2a3e54250000000000ff1500040b0a1008291838001200760ee600000000000000000000000f1500040b0a10ac20703fb1aec23f00000000320149668f430000000000000000000000000000000000000000000000f3808080"),
+ frameDecoder.decode(null, null, binary("404e5443010000000000000059009adb2a3e54250000000000ff1500040b0a1008291838001200760ee600000000000000000000000f1500040b0a10ac20703fb1aec23f00000000320149668f430000000000000000000000000000000000000000000000f3808080")));
+
+ }
+
+ @Test
+ public void testDecodeFlex10() throws Exception {
+
+ NavisFrameDecoder frameDecoder = new NavisFrameDecoder();
+
+ frameDecoder.setFlexDataSize(73);
+
+ verifyFrame(binary(
+ "7e54040000000400000030129957405c000b00632f9857405ccace03021e129101a103000000000000c4005ba3fe3b00000000120046100000000000001aff7f000080bfffff80000080bfffffffff9f"),
+ frameDecoder.decode(null, null, binary("7e54040000000400000030129957405c000b00632f9857405ccace03021e129101a103000000000000c4005ba3fe3b00000000120046100000000000001aff7f000080bfffff80000080bfffffffff9f")));
+
+ verifyFrame(binary(
+ "7e4101080000000917c057405c002b001833c057405cbbce030225129101a00300007c6102408900400c1b3cfce3b23a12004710e000000000001bff7f000080bfffff80000080bfffffffffb2"),
+ frameDecoder.decode(null, null, binary("7e4101080000000917c057405c002b001833c057405cbbce030225129101a00300007c6102408900400c1b3cfce3b23a12004710e000000000001bff7f000080bfffff80000080bfffffffffb2")));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java
new file mode 100644
index 000000000..33a6bab24
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java
@@ -0,0 +1,79 @@
+package org.traccar.protocol;
+
+import org.traccar.ProtocolTest;
+
+import org.junit.Test;
+
+public class NavisProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeNtcb() throws Exception {
+
+ NavisProtocolDecoder decoder = new NavisProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "404E5443010000007B000000130044342A3E533A383631373835303035323035303739"));
+
+ verifyNull(decoder, binary(
+ "404E5443010000007B000000130047372A3E533A383631373835303035313236303639"));
+
+ verifyPosition(decoder, binary(
+ "404e5443010000000000000059009adb2a3e54250000000000ff1500040b0a1008291838001200760ee600000000000000000000000f1500040b0a10ac20703fb1aec23f00000000320149668f430000000000000000000000000000000000000000000000f3808080"),
+ position("2016-11-11 21:00:04.000", true, 53.74336, 87.14437));
+
+ verifyPositions(decoder, binary(
+ "404e544300000000040000005a00c6812a3e410125e3a60700011705071503011030210c0000fa200910e6000000000000000000000001082106150010ae97643f88a39f3f0000000090001fcc6c450000000000000000000000000000000000000000000000f6808080"));
+
+ verifyPositions(decoder, binary(
+ "404e544301000000000000005a002e6c2a3e410125d7540100001512233a0b0a0f08026300000a000b000b00020000000000000000000c12233b0b0a0f03fd6d3f0fde603f00000000ba0051e0c845000000000000000000000000000000000000000000000080808080"));
+
+ verifyPositions(decoder, binary(
+ "404E5443010000007B0000005A0050692A3E410125DB0E00000015110707110A0C0880630000AA39A2381600020000000000000000000C110708110A0CB389793F1AEF263F00000000120034F516440000000000000000000000FAFF000000FAFF000000FAFF80808080"));
+
+ verifyPosition(decoder, binary(
+ "404e544301000000cdfbf5027200852e2a3e5406aa170000c11116162410001310a9110e80996b281003000a0008000000000000000000d207d207ffffff00fbff00fbff00fbff00fbff00fbff00fbff00fbff2d808080ffffffffffff2b161624100013509b0302b0f89201830500000000000037002fb8cf43eed5843a35003500"),
+ position("2019-01-16 22:22:36.000", true, 56.31800, 44.01523));
+
+ verifyPositions(decoder, binary(
+ "404e54430100000045635902730081972a3e4101060b7e0e000b171328050d00133029110e00bc6141100200000000000000000000000000d207d307ffffff00fbff00fbff00fbff00fbff00fbff00fbff00fbff02808080ffffffffffff4f1328050d001371cd0302c5109101a60300000000000000003d1b37470000000096009600"));
+ }
+
+ @Test
+ public void testDecodeFlex10() throws Exception {
+
+ NavisProtocolDecoder decoder = new NavisProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "404e544301000000c9b5f602130046c52a3e533a383639363936303439373232383235"));
+
+ verifyNull(decoder, binary(
+ "404e544301000000aaecf6021300c8712a3e464c4558b00a0a45ffff300a08080f8388"));
+
+ verifyPosition(decoder, binary(
+ "7e54040000000400000030129957405c000b00632f9857405ccace03021e129101a103000000000000c4005ba3fe3b00000000120046100000000000001aff7f000080bfffff80000080bfffffffff9f"),
+ position("2019-01-17 10:23:20.000", true, 56.33996, 43.80762));
+
+ verifyPositions(decoder, binary(
+ "7e4101080000000917c057405c002b001833c057405cbbce030225129101a00300007c6102408900400c1b3cfce3b23a12004710e000000000001bff7f000080bfffff80000080bfffffffffb2"));
+ }
+
+ @Test
+ public void testDecodeFlex20() throws Exception {
+
+ NavisProtocolDecoder decoder = new NavisProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "404e544301000000a9eef602130043fb2a3e533a383639363936303439373337333835"));
+
+ verifyNull(decoder, binary(
+ "404e544301000000a9eef6021a003f8e2a3e464c4558b014147afffff008080800000e00000000000000"));
+
+ verifyPosition(decoder, binary(
+ "7e5428000000280000002111d16b435c00a900154bd16b435ce19e030259f6920133050000b7623e429300c9e7f03f2ba45a3e1f001f007b6c5910850f0100001629080a000000000000060947"),
+ position("2019-01-19 18:26:25.000", true, 56.31952, 44.01423));
+
+ verifyPositions(decoder, binary(
+ "7e4101270000000b17b16b435c00a9000d4bb26b435caaa2030229f29201620500000000000093004493d53fee892d3e1f001f00ac6c591081f00000001700080a0000000000000609f2"));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java
new file mode 100644
index 000000000..450f77144
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NavisetFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NavisetFrameDecoder decoder = new NavisetFrameDecoder();
+
+ verifyFrame(
+ binary("1310e4073836383230343030353935383436362a060716"),
+ decoder.decode(null, null, binary("1310e4073836383230343030353935383436362a060716")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java
new file mode 100644
index 000000000..09ea10f1f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NavisetProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NavisetProtocolDecoder decoder = new NavisetProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "1310e4073836383230343030353935383436362a060716"));
+
+ verifyPositions(decoder, binary(
+ "6b511a203f95162b7822515d78a92503042df6030000ff040000c4003f1922471000001af3000030e4503490e8ff00000000000000000000000000000000ff27000000000000808080808080808000000000ffdd0000000000000000000000000000000000000000000000210000000000ff00000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096160b7d22515d3ca92503e42cf6030000ff040000c7001ef521471000001af3000070e44034e0e8ff00000000000000000000000000000000ff27000000000000808080808080808000000000ffdd0000000000000000000000000000000000000000000000210000000000ff00000000000000000000000000000000000000000000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000664a"));
+
+ verifyPositions(decoder, binary(
+ "b7501a203fab0d0bffcf4b5df0a82503a02cf6030000ff0c4200ba0007462a3a10000098280000f0f610fc4042ff00000000000000000000000000000000ff26000000000000808080808080808000000000ff000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008bff"));
+
+ verifyPositions(decoder, binary(
+ "b7501a203fda0d097fdc4b5d70aa2503c42cf6030000ff0fb40dea0006b12a3a100000b228000080f210044041ff00000000000000000000000000000000ff25000000000000808080808080808000000000ff1201000000000000000000000000000000000000000000001a0000000000ff00000000000000000000000000000000000000000000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b603"));
+
+ verifyPositions(decoder, binary(
+ "14501a2000a50c0955a64b5db8a92503fc2cf603000084ab"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java
new file mode 100644
index 000000000..a8db30476
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NeosProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NeosProtocolDecoder decoder = new NeosProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">12345678,1,1,070201,144111,W05829.2613,S3435.2313,,00,034,25,00,126-000,0,3,11111111*2d!\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java
new file mode 100644
index 000000000..7c02402b1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java
@@ -0,0 +1,45 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NoranProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NoranProtocolDecoder decoder = new NoranProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0d0a2a4b57000d000080010d0a"));
+
+ verifyPosition(decoder, binary(
+ "34000800010b0000000000003f43bb8da6c2ebe229424e523039423233343439000031362d30392d31352030373a30303a303700"));
+
+ verifyPosition(decoder, binary(
+ "28003200c380000000469458408c4ad340ad381e3f4e52303947313336303900000001ff00002041"));
+
+ verifyPosition(decoder, binary(
+ "28003200c38000d900fcc97a416b1a7a42b43eef3d4e523039473034383737000000000092fcda4a"));
+
+ verifyPosition(decoder, binary(
+ "3400080001090000000000001D43A29BE842E62520424E523039423036363932000031322D30332D30352031313A34373A343300"));
+
+ verifyPosition(decoder, binary(
+ "34000800010c000000000080a3438e20944149bd07c24e523039423139323832000031352d30342d32362030383a34333a353300"));
+
+ verifyNull(decoder, binary(
+ "0f0000004e52303946303431353500"));
+
+ verifyPosition(decoder, binary(
+ "22000800010c008a007e9daa42317bdd41a7f3e2384e523039463034313535000000"));
+
+ verifyPosition(decoder, binary(
+ "34000800010c0000000000001c4291251143388d17c24e523039423131303930000031342d31322d32352030303a33333a303700"));
+
+ verifyPosition(decoder, binary(
+ "34000800010c00000000000000006520944141bd07c24e523039423139323832000031352d30342d32352030303a30333a323200"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java
new file mode 100644
index 000000000..4991f1371
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class NoranProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ NoranProtocolEncoder encoder = new NoranProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary(
+ "0d0a2a4b5700440002000000000000002a4b572c3030302c3030372c3030303030302c302300000000000000000000000000000000000000000000000000000000000d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java
new file mode 100644
index 000000000..8a00207eb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class NvsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NvsFrameDecoder decoder = new NvsFrameDecoder();
+
+ assertEquals(
+ binary("0012333537303430303630303137383234312e38"),
+ decoder.decode(null, null, binary("0012333537303430303630303137383234312e38")));
+
+ assertEquals(
+ binary("cccccccc0073000144b9ddf2aca002015694823d1f165d80902139a44f00aa001e1400000103000a080115001a001d001e0141004001f00065001301061600001700001800004231da430000440000085000000000480000000049000000004a0000000047ffffffff6900000004c700000000e10000000100954a"),
+ decoder.decode(null, null, binary("cccccccc0073000144b9ddf2aca002015694823d1f165d80902139a44f00aa001e1400000103000a080115001a001d001e0141004001f00065001301061600001700001800004231da430000440000085000000000480000000049000000004a0000000047ffffffff6900000004c700000000e10000000100954a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java
new file mode 100644
index 000000000..9a516e733
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NvsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NvsProtocolDecoder decoder = new NvsProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "0012333537303430303630303137383234312e38"));
+
+ verifyNull(decoder, binary(
+ "0012313233343536373839303132333435312E31"));
+
+ verifyPositions(decoder, binary(
+ "cccccccc0073000144b9ddf2aca002015694823d1f165d80902139a44f00aa001e1400000103000a080115001a001d001e0141004001f00065001301061600001700001800004231da430000440000085000000000480000000049000000004a0000000047ffffffff6900000004c700000000e10000000100954a"));
+
+ verifyPositions(decoder, binary(
+ "CCCCCCCC00FE00007048860DDF79020446a6f1ce010f14f650209cca80006f00d6040004010300030101150316030001460000015d0046a6f1dc0d0f14ffe0209cc580006e00c7050001010300030101150316010001460000015e0046a6f1ea0e0f150f00209cd20000950108040000010300030101150016030001460000015d0046a6f1ff0b0f150a50209cccc000930068040000010300030101150016030001460000015b006123"));
+
+ verifyPositions(decoder, binary(
+ "cccccccc0217000144b9ddf2aca002055683f72b01165d80632139a3c800ab00ce0a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000b7c700000000e100000001005683f74901165d80632139a3c800ab012a0a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000b8c700000000e100000001005683f76801165d80632139a3c800ab00590a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000b9c700000000e100000001005683f78601165d80632139a3c800ab00c80a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000bac700000000e100000001005683f7a401165d80632139a3c800ab01310a00000403000a080115bf1a001d001e0141004001f00065011301061600001700001800004231a9430000440000085000000000480000000049000000004a0000000047ffffffff69000000bbc700000000e100000001001d72"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java
new file mode 100644
index 000000000..4cafd7612
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NyitechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NyitechProtocolDecoder decoder = new NyitechProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "4040690030313436383230303238373201201c0c12031a308080801c0c12031a3007d67e7e08aceb841002000000ae08000000000000000000000000001e002900f0ffdd002700f2ffe0002700f2ffe1002400f0ffdf002400f3ffe3008a00ffff01010000a9c70d0a"));
+
+ verifyPosition(decoder, binary(
+ "4040390030313436383230303238373203200100010c000000001c0c1203192a1b0c12171d3104fed87d089288801000000000000011ec0d0a"));
+
+ verifyPosition(decoder, binary(
+ "4040480030313436383230303238373201101c0c12031a2907fa7e7e08b8eb841002000000bc080101040904040300010100000a818283848586878862611c0c12031a293f9c0d0a"));
+
+ verifyPosition(decoder, binary(
+ "40404b003247512d313630313030313901101e0b100604190c02c83707f887ac0f000000002d130101030304020000010100000d426162636465666768696a6ba51e0b1006041965c30d0a"));
+
+ verifyPosition(decoder, binary(
+ "4040490030313436383230303238373202201c0c120319348080001b0c12171d3104fed87d0892888010000000000000000000000000000000000000008b00ffff010100008a480d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java
new file mode 100644
index 000000000..4c33d1766
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ObdDongleProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ObdDongleProtocolDecoder decoder = new ObdDongleProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "55550003383634383637303232353131303135010009010011023402010201ABAAAA"));
+
+ verifyPosition(decoder, binary(
+ "5555000338363438363730323235313130313503000100010355AABBCC184F1ABC614E21C1FA08712A84ABAAAA"),
+ position("2015-07-18 20:49:16.000", true, 22.12346, -123.45678));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java
new file mode 100644
index 000000000..c79978f88
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OigoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OigoProtocolDecoder decoder = new OigoProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "7e002e000000146310002523830400001bfb000369150f310c0591594d062ac0c0141508011303cd63101604fd00000000"));
+
+ verifyPosition(decoder, binary(
+ "0103537820628365110310410790660962521813380026EE4EFF8593AA0065003E00794C020600100500000000"));
+
+ verifyPosition(decoder, binary(
+ "0E03537820628344660204043255862749531B100E0026EE3AFF8593A3FFFE00BF00044C20090710C300000000"));
+
+ verifyPosition(decoder, binary(
+ "00035378206638500203340201271426226b190203001ac000ff72eedd00370097238b4c34116a130b000094d9"));
+
+ verifyPosition(decoder, binary(
+ "1d035378206638500203340201271426226b19020c001ab144ff72f74d005f0097298a4c1d066d130b000094de"));
+
+ verifyPosition(decoder, binary(
+ "00035378206638500203340201271426226b191016001c04e5ff760081013d002900814c1a0f5e130b00009576"));
+
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff02d0690e000220690e0002200696dbd204bdfde31a070000b307101135de106e05f500000000010908010402200104ffff8001"));
+
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff02d1690e00051f690e00051f0696dbd204bdfde31a070000b307100f35c0106305f500000000010908010402200104ffff8001"));
+
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff0d82691300001669130000160696dbd804bdfdbb1a0800000007101035a2106905f500000000010908010402200104ffff8001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
new file mode 100644
index 000000000..e2f72c161
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OkoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OkoProtocolDecoder decoder = new OkoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "{861694033681089,045403.00,A,4924.14181,N,03207.43787,E,0.080,,151117,07,0.00,01,24.8,1,02,5n4}"));
+
+ verifyPosition(decoder, text(
+ "{045411.00,A,4924.14243,N,03207.43754,E,0.172,,151117,07,0.00,F9,28.1,2,C2,5n4}"));
+
+ verifyPosition(decoder, text(
+ "{861001001016415,115031.000,A,4804.101180,N,02255.227002,E,4.121,111.0,211215,6,0.00,F7,13.6,1,00"));
+
+ verifyPosition(decoder, text(
+ "{132810.000,A,4926.4243,N,03203.6831,E,25.0,183,131011,07,5.69,05,14.1,1,82,3N5"));
+
+ verifyPosition(decoder, text(
+ "{115034.000,A,4804.098944,N,02255.233436,E,7.858,120.9,211215,7,0.00,F7,13.7,1,00"));
+
+ verifyPosition(decoder, text(
+ "{115038.000,A,4804.091227,N,02255.250213,E,17.621,128.1,211215,8,0.00,00,13.7,2,00"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java
new file mode 100644
index 000000000..ae21de107
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OmnicommFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OmnicommFrameDecoder decoder = new OmnicommFrameDecoder();
+
+ verifyFrame(
+ binary("c08600004566"),
+ decoder.decode(null, null, binary("c08600004566")));
+
+ verifyFrame(
+ binary("c080080061a61915340100001dec"),
+ decoder.decode(null, null, binary("c080080061a61915340100001dec")));
+
+ verifyFrame(
+ binary("c0860d0510ab1200b56c9b14002500080c1308b5d9eda401200750ce01142b0896d384940410c8fdbce60218002000280a30002c0c000a0211031308c6d9eda40114660008011308d4d9eda4012001280040f201482c50ce01600068007000800102880195e4ef01900100142b08d4da84940410acf1bce602180c208401280930f81e38d4d9eda4012c330800344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c660008121308d5d9eda4011483010a56543050303e3243303a313031313120543150323e3143313a313131313120566572333039204e6f762031342032303139525354313030303139353020554152545f4750535f393630303a203334353733303234380d0a8401700008011308e6d9eda4012001280040f201482c50ce016000680070008001028801a7e4ef01900164142b08ecd784940410c6f2bce6021800208401280a30f01e38e6d9eda4012c33080010be980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308f8d9eda4012007280040f201482c50ce016000680070008001028801b9e4ef01900164142b08ecd784940410c6f2bce6021800208401280b30f01e38f8d9eda4012c33080010c8de0118f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f00080113088bdaeda4012007280040f201482c50ce016000680070008001028801cbe4ef01900164142b08ecd784940410c6f2bce6021800208401280c30f01e3889daeda4012c33080010966a18f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f00080113089ddaeda4012007280040f201482c50ba066000680070038001028801dde4ef01900164142b08ecd784940410c6f2bce6021800208401280b30f01e389cdaeda4012c33080010b01818f02220e638344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f0008011308afdaeda4012007280040f201482c50d2016000680070008001028801efe4ef01900164142b08ecd784940410c6f2bce6021800208401280c30f01e38aedaeda4012c33080010a27518f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308c1daeda4012007280040f201482c50ce01600068007000800102880181e5ef01900164142b08ecd784940410c6f2bce6021800208401280d30f01e38c0daeda4012c33080010d6e80118f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308d3daeda4012007280040f201482c50d001600068007000800102880193e5ef01900164142b08c2e784940410c0edbce602180020cb02280c30fa1e38d2daeda4012c33080010e6980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308e5daeda4012007287140f201482c50da016000680070008001028801a5e5ef01900164142b08c2e784940410c0edbce602180020cb02280b30fa1e38e3daeda4012c33080010f6980218ee2220e638344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308f7daeda4012007280040f201482c50d8016000680070008001028801b7e5ef01900164142b08c2e784940410c0edbce602180020cb02280c30fa1e38f6daeda4012c33080010fc980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4cc801"),
+ decoder.decode(null, null, binary("c0860d0510ab1200b56c9b14002500080c1308b5d9eda401200750ce01142b0896d384940410c8fdbce60218002000280a30002c0c000a0211031308c6d9eda40114660008011308d4d9eda4012001280040f201482c50ce01600068007000800102880195e4ef01900100142b08d4da84940410acf1bce602180c208401280930f81e38d4d9eda4012c330800344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c660008121308d5d9eda4011483010a56543050303e3243303a313031313120543150323e3143313a313131313120566572333039204e6f762031342032303139525354313030303139353020554152545f4750535f393630303a203334353733303234380d0a8401700008011308e6d9eda4012001280040f201482c50ce016000680070008001028801a7e4ef01900164142b08ecd784940410c6f2bce6021800208401280a30f01e38e6d9eda4012c33080010be980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308f8d9eda4012007280040f201482c50ce016000680070008001028801b9e4ef01900164142b08ecd784940410c6f2bce6021800208401280b30f01e38f8d9eda4012c33080010c8de0118f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f00080113088bdaeda4012007280040f201482c50ce016000680070008001028801cbe4ef01900164142b08ecd784940410c6f2bce6021800208401280c30f01e3889daeda4012c33080010966a18f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f00080113089ddaeda4012007280040f201482c50ba066000680070038001028801dde4ef01900164142b08ecd784940410c6f2bce6021800208401280b30f01e389cdaeda4012c33080010b01818f02220e638344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f0008011308afdaeda4012007280040f201482c50d2016000680070008001028801efe4ef01900164142b08ecd784940410c6f2bce6021800208401280c30f01e38aedaeda4012c33080010a27518f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308c1daeda4012007280040f201482c50ce01600068007000800102880181e5ef01900164142b08ecd784940410c6f2bce6021800208401280d30f01e38dbdcdaeda4012c33080010d6e80118f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308d3daeda4012007280040f201482c50d001600068007000800102880193e5ef01900164142b08c2e784940410dbdcedbce602180020cb02280c30fa1e38d2daeda4012c33080010e6980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308e5daeda4012007287140f201482c50da016000680070008001028801a5e5ef01900164142b08c2e784940410dbdcedbce602180020cb02280b30fa1e38e3daeda4012c33080010f6980218ee2220e638344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308f7daeda4012007280040f201482c50d8016000680070008001028801b7e5ef01900164142b08c2e784940410dbdcedbce602180020cb02280c30fa1e38f6daeda4012c33080010fc980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4cc801")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java
new file mode 100644
index 000000000..89cb7fadf
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OmnicommProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OmnicommProtocolDecoder decoder = new OmnicommProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "c080080061a61915340100001dec"));
+
+ verifyPositions(decoder, binary(
+ "c0863e05a5da0300a9168e14000c000a0211031308a9adb8a401140a0008111308d1aeb8a401140a0008111308feaeb8a401140a00081113088bafb8a40114430008011308bfb0b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c43080010001806200028003006446c0008121308c0b0b8a4011483010a5c4c6f616450726f746f4275663a20455252202d20636f7272757074656420535450425b305d0d0a566572333038204d617220323020323031395253544142434432303030205433343438353632333250303e3143303a3130303131208401430008011308f3b1b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308a7b3b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308dbb4b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c43080010001806200028003006444300080113088fb6b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308c3b7b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308f7b8b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308abbab8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308dfbbb8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c430800100018062000280030064443000801130893bdb8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308c7beb8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308fbbfb8a40120022800380040d801482950d2016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308afc1b8a40120022800380040d801482950d2016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308e3c2b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c430800100018062000280030064443000801130897c4b8a40120022800380040d801482950d2016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308cbc5b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644430008011308ffc6b8a40120022800380040d801482950d4016000680070008001028801bbdffe06142b0800100018002000280030002c4308001000180620002800300644"));
+
+ verifyPositions(decoder, binary(
+ "C0866300CD1400002273231400580008011308A2E68DA10110002006280030003800400048005000600068007000142B08EC979EB60410EEB7CC8C02180020002804300038A2E68DA1012C33080010001800200028003000344308381000180220382800300244DF2A"));
+
+ verifyPositions(decoder, binary(
+ "c0860d0510ab1200b56c9b14002500080c1308b5d9eda401200750ce01142b0896d384940410c8fdbce60218002000280a30002c0c000a0211031308c6d9eda40114660008011308d4d9eda4012001280040f201482c50ce01600068007000800102880195e4ef01900100142b08d4da84940410acf1bce602180c208401280930f81e38d4d9eda4012c330800344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c660008121308d5d9eda4011483010a56543050303e3243303a313031313120543150323e3143313a313131313120566572333039204e6f762031342032303139525354313030303139353020554152545f4750535f393630303a203334353733303234380d0a8401700008011308e6d9eda4012001280040f201482c50ce016000680070008001028801a7e4ef01900164142b08ecd784940410c6f2bce6021800208401280a30f01e38e6d9eda4012c33080010be980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308f8d9eda4012007280040f201482c50ce016000680070008001028801b9e4ef01900164142b08ecd784940410c6f2bce6021800208401280b30f01e38f8d9eda4012c33080010c8de0118f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f00080113088bdaeda4012007280040f201482c50ce016000680070008001028801cbe4ef01900164142b08ecd784940410c6f2bce6021800208401280c30f01e3889daeda4012c33080010966a18f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f00080113089ddaeda4012007280040f201482c50ba066000680070038001028801dde4ef01900164142b08ecd784940410c6f2bce6021800208401280b30f01e389cdaeda4012c33080010b01818f02220e638344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c6f0008011308afdaeda4012007280040f201482c50d2016000680070008001028801efe4ef01900164142b08ecd784940410c6f2bce6021800208401280c30f01e38aedaeda4012c33080010a27518f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308c1daeda4012007280040f201482c50ce01600068007000800102880181e5ef01900164142b08ecd784940410c6f2bce6021800208401280d30f01e38c0daeda4012c33080010d6e80118f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308d3daeda4012007280040f201482c50d001600068007000800102880193e5ef01900164142b08c2e784940410c0edbce602180020cb02280c30fa1e38d2daeda4012c33080010e6980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308e5daeda4012007287140f201482c50da016000680070008001028801a5e5ef01900164142b08c2e784940410c0edbce602180020cb02280b30fa1e38e3daeda4012c33080010f6980218ee2220e638344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4c700008011308f7daeda4012007280040f201482c50d8016000680070008001028801b7e5ef01900164142b08c2e784940410c0edbce602180020cb02280c30fa1e38f6daeda4012c33080010fc980218f02220e838344b0b08ff0110ff0118ff0120ff0128ff0130ff0138ff0140ff010c4cc801"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java
new file mode 100644
index 000000000..a04cf4e72
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OpenGtsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OpenGtsProtocolDecoder decoder = new OpenGtsProtocolDecoder(null);
+
+ verifyPosition(decoder, request(
+ "/?id=999000000000003&gprmc=$GPRMC,082202.0,A,5006.747329,N,01416.512315,E,0.0,,131018,1.2,E,A*2E"));
+
+ verifyPosition(decoder, request(
+ "/?id=gprmc_999000000000003&gprmc=$GPRMC,143013.0,A,5006.728217,N,01416.437869,E,0.0,329.6,281017,1.2,E,A*0E"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456789012345&dev=dev_name&acct=account&batt=0&code=0xF020&alt=160.5&gprmc=$GPRMC,191555,A,5025.46624,N,3030.39937,E,0.000000,0.000000,200218,,*2F"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java
new file mode 100644
index 000000000..bb5f1f135
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OrionProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OrionProtocolDecoder decoder = new OrionProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "5057000137bf6236235a0331b5c6e402a3b5ecff5102980003000e0c1d172936080e0c1d172936b03b01000882050000008e080000000000008c0300940500000084030085030003067600900113150000000000000000000000000000000000000004a4c8"));
+
+ verifyPositions(decoder, binary(
+ "5057004107367C242B440901ADE97D0163143B07B003000000000D041917382D000B0101000511000000000682050000008E080000000000008C0300840300850300090A0000000048010000008AFC"));
+
+ verifyPositions(decoder, binary(
+ "5057004107367C242C440901ADE97D0163143B07B003000000000D041917382D000B0101000513000000000682050000008E080000000000008C0300840300850300090A000000003BFEFFFF01FAE5"));
+
+ verifyPositions(decoder, binary(
+ "5057004107367C242D440901ADE97D0163143B07B003000000000D041917382D000B0101000514000000000682050000008E080000000000008C0300840300850300090A00000000FDFDFFFF023721"));
+
+ verifyPositions(decoder, binary(
+ "505700412ac86236354009114d20e402210f1f00d204000000000e06110d3414000b0101001228000000000682050000008e080000000000008c030084030085030003067b006801000930"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java
new file mode 100644
index 000000000..3c38bd831
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java
@@ -0,0 +1,48 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OsmAndProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OsmAndProtocolDecoder decoder = new OsmAndProtocolDecoder(null);
+
+ verifyNotNull(decoder, request(
+ "/?id=123456&timestamp=1377177267&cell=257,02,16,2224&cell=257,02,16,2223,-90&wifi=00-14-22-01-23-45,-80&wifi=00-1C-B3-09-85-15,-70"));
+
+ verifyNull(decoder, request(
+ "/?timestamp=1377177267&lat=60.0&lon=30.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=902064&lat=42.06288&lon=-88.23412&timestamp=2016-01-27T18%3A55%3A47Z&hdop=6.0&altitude=224.0&speed=0.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=902064&lat=42.06288&lon=-88.23412&timestamp=1442068686579&hdop=6.0&altitude=224.0&speed=0.0"));
+
+ verifyPosition(decoder, request(
+ "/?lat=49.60688&lon=6.15788&timestamp=2014-06-04+09%3A10%3A11&altitude=384.7&speed=0.0&id=353861053849681"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456&timestamp=1377177267&lat=60.0&lon=30.0&speed=0.0&bearing=0.0&altitude=0&hdop=0.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456&timestamp=1377177267&lat=60.0&lon=30.0"));
+
+ verifyPosition(decoder, request(
+ "/?lat=60.0&lon=30.0&speed=0.0&heading=0.0&vacc=0&hacc=0&altitude=0&deviceid=123456"));
+
+ verifyPosition(decoder, request(
+ "/?id=861001000719969&lat=41.666667&lon=-0.883333&altitude=350.059479&speed=0.000000&batt=87"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456&timestamp=1377177267&location=60.0,30.0"));
+
+ verifyPosition(decoder, request(
+ "/?id=123456789012345&timestamp=1504763810&lat=40.7232948571&lon=-74.0061408571&bearing=7.19889788244&speed=40&ignition=true&rpm=933&fuel=24"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java
new file mode 100644
index 000000000..12f0106f9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OutsafeProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OutsafeProtocolDecoder decoder = new OutsafeProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"device\":\"862061044762093\",\"owner\":\"\",\"data\":{\"cmd\":\"GEO\",\"ms1\":82,\"ms2\":80,\"ms3\":5266,\"ms4\":-68,\"observation\":\"$NMEA 323455\",\"content\":null},\"time\":null,\"origin\":\"TCP\",\"latitude\":19.334734,\"longitude\":-99.307236,\"altitude\":2000,\"heading\":0,\"rssi\":123}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"device\":\"1e09d88a-fe8e-4dee-90b9-6297088ff3de\",\"owner\":\"\",\"data\":{\"cmd\":\"GEO\",\"ms1\":82,\"ms2\":80,\"ms3\":5266,\"ms4\":-68,\"observation\":\"$NMEA 323455\",\"content\":null},\"time\":null,\"origin\":\"TCP\",\"latitude\":19.334734,\"longitude\":-99.307236,\"altitude\":2000,\"heading\":0,\"rssi\":123}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java
new file mode 100644
index 000000000..248920e21
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OwnTracksProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OwnTracksProtocolDecoder decoder = new OwnTracksProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"_type\":\"location\",\"acc\":15,\"alt\":440,\"batt\":46,\"conn\":\"w\",\"lat\":46.0681247,\"lon\":11.1512805,\"t\":\"u\",\"tid\":\"5t\",\"tst\":1551874878,\"vac\":2,\"vel\":0}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"lon\":2.29513,\"lat\":48.85833,\"tst\":1497349316,\"_type\":\"location\",\"tid\":\"JJ\"}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"cog\":271,\"lon\":2.29513,\"acc\":5,\"vel\":61,\"vac\":21,\"lat\":48.85833,\"tst\":1497349316,\"alt\":167,\"_type\":\"location\",\"tid\":\"JJ\",\"t\":\"u\",\"batt\":67}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"lat\":48.85,\"lon\":2.295,\"_type\":\"location\",\"tid\":\"JJ\",\"tst\":1497476456}")));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..ade2804da
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java
@@ -0,0 +1,32 @@
+package org.traccar.protocol;
+
+import io.netty.buffer.Unpooled;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class PacificTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testReadBitExt() {
+
+ assertEquals(0x35, PacificTrackProtocolDecoder.readBitExt(
+ Unpooled.wrappedBuffer(new byte[] { (byte) 0b10110101 })));
+
+ assertEquals(0x135, PacificTrackProtocolDecoder.readBitExt(
+ Unpooled.wrappedBuffer(new byte[] { (byte) 0b00000010, (byte) 0b10110101 })));
+ }
+
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PacificTrackProtocolDecoder decoder = new PacificTrackProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "fb82e80280883527530900009110818202c0909308990b122519076138fc03b3480205a3e80003a0834dd19fb08112c08f0143000e020000000100000014000101929f806328c0000f4240810a858ce011314334424a57464758444c3533313737330190868102100828cf"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java
new file mode 100644
index 000000000..4b9739242
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PathAwayProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PathAwayProtocolDecoder decoder = new PathAwayProtocolDecoder(null);
+
+ verifyPosition(decoder, request(
+ "?UserName=name&Password=pass&LOC=$PWS,1,\"Roger\",,,100107,122846,45.317270,-79.642219,45.00,42,1,\"Comment\",0*58"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java
new file mode 100644
index 000000000..03d0dd7b9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PiligrimProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PiligrimProtocolDecoder decoder = new PiligrimProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST,
+ "/bingps?imei=868204005544720&csq=18&vout=00&vin=4050&dataid=00000000",
+ binary("fff2200d4110061a32354f3422310062000a0005173b0000a101000300005e00fff2200d4110100932354f2b22310042000b000e173b00009f01000700006000")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java
new file mode 100644
index 000000000..0fe3a3a01
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class PluginProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PluginProtocolDecoder decoder = new PluginProtocolDecoder(null);
+
+ verifyAttribute(decoder, text(
+ "$$STATUS,60925,20190829123115,28.254151,-25.860605,0.0,0,0,-1,2,0.000,13699,0.00,0,0,28.4,23.4,0,0,0,0,0,0,0,0,0"),
+ Position.PREFIX_TEMP + 1, 28.4);
+
+ verifyPosition(decoder, text(
+ "$$STATUS,60550,20191014084650,28.254258,-25.860355,0.0,236,0,-1,2,7472.967,13697,0.00,0,0,0.0,0.0,0,0,0,0,0,0,0,0,0"));
+
+ verifyPosition(decoder, text(
+ "$$STATUS,fleet40,20190704122622,26.259431,-29.027889,0,9,0,-1,2,19719,805315969,0,0,0"));
+
+ verifyPosition(decoder, text(
+ "$$ALARM801739,20190612121950,28.254067,-25.860494,0,0,0,-1,2,2,12595331,0,0,0,+,22,0,0,0,0,0,,0,0"));
+
+ verifyPosition(decoder, text(
+ "$$STATUS801739,20190528143943,28.254086,-25.860665,0,0,0,-1,2,78,11395,0,0,0"));
+
+ verifyPosition(decoder, text(
+ "50000,20150623184513,113.828759,22.709578,70,190,0,-1,2,155135681,805327235,1.32,-32.1,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java
new file mode 100644
index 000000000..61a057dd7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PretraceProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PretraceProtocolDecoder decoder = new PretraceProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(867967021915915U1110A1701201500102238.1700N11401.9324E000264000000000009001790000000,&P11A4,F1050^47"));
+
+ verifyPosition(decoder, text(
+ "(864244029498838U1110A1509250653072238.1641N11401.9213E000196000000000406002990000000,&P195%,T1050,F14A5,R104C51E47B^30"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
new file mode 100644
index 000000000..aa08ecea2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class PretraceProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodePositionPeriodic() throws Exception {
+
+ PretraceProtocolEncoder encoder = new PretraceProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 300);
+
+ assertEquals("(123456789012345D221300,300,,^69)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ PretraceProtocolEncoder encoder = new PretraceProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "D21012");
+
+ assertEquals("(123456789012345D21012^44)", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java
new file mode 100644
index 000000000..dbc1665fb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PricolProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PricolProtocolDecoder decoder = new PricolProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "3c5052493030303350020000011402110b222b0455152e4e001de819ca450000000000000003820249000000000000000000000000000000000000000040003e"));
+
+ verifyNotNull(decoder, binary(
+ "3c544553303030324b02000000000000000000000000000000000000000000000000000000037c01f4000000000000000000000000000000000000000000003e"));
+
+ verifyPosition(decoder, binary(
+ "3c4944303030303150FFFFFFFF1C050C121D38045D09FA4e001DE815F4452FFFFFFFFFFF03FF03FF03FF03FF03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF113e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java
new file mode 100644
index 000000000..5f6f564b1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ProgressProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ProgressProtocolDecoder decoder = new ProgressProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "020037000100000003003131310f003335343836383035313339303036320f00323530303136333832383531353535010000000100000000000000e6bb97b6"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java
new file mode 100644
index 000000000..790c2c1d9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PstFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PstFrameDecoder decoder = new PstFrameDecoder();
+
+ verifyFrame(
+ binary("2fafac5a050f0000e0022fafac5a01891e882bbfdd06dd577c9865620a0efe524c419f940b6710f5ba0c86e5868ffc97c77eaaf166a31dba63f9894e98a91b9486c94e79ce537359737a5e9385431a590eb20b5115a2b7939e4e66ae"),
+ decoder.decode(null, null, binary("282fafac5a050f0000e0022fafac5a01891e882bbfdd06dd577c9865620a0efe524c419f940b6710f5ba0c86e5868ffc97c77eaaf166a31dba63f9894e98a91b9486c94e79ce537359737a5e9385431a590eb20b5115a2b7939e4e66ae29")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java
new file mode 100644
index 000000000..a5db8872d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PstProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PstProtocolDecoder decoder = new PstProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "2faf9ab606000004c7055052ec88c0070b04000015050c09b500a25271c733e0720d01fe0f045052ec8410145052ba07858413918af325e7020802fe010001051103ffff0015023bbdc87d"));
+
+ verifyPosition(decoder, binary(
+ "2faf9ab606000004c7055052ec88c0070b04000015050c09b500a25271c733e0720d01fe0f045052ec8410145052ba07858413918af325e7020802fe010001051103ffff0015023bbdc87d"));
+
+ verifyNull(decoder, binary(
+ "2faf9b4c0600000012054f36ec194000bfa9"));
+
+ verifyNull(decoder, binary(
+ "2faf9b5605e40000e0022faf9b560196cb2f003f0c72ab56129ae0847ac98801cd1ed8"));
+
+ verifyNull(decoder, binary(
+ "2fafac5a050f0000e0022fafac5a01891e882bbfdd06dd577c9865620a0efe524c419f940b6710f5ba0c86e5868ffc97c77eaaf166a31dba63f9894e98a91b9486c94e79ce537359737a5e9385431a590eb20b5115a2b7939e4e66ae"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java
new file mode 100644
index 000000000..59574aeee
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt215ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt215ProtocolDecoder decoder = new Pt215ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "58580d010359339075435451010d0a"));
+
+ verifyNull(decoder, binary(
+ "585801080d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java
new file mode 100644
index 000000000..e7d87d583
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt3000ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt3000ProtocolDecoder decoder = new Pt3000ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "%356939010012099,$GPRMC,124945.752,A,4436.6245,N,01054.4634,E,0.11,358.52,060408,,,A,+393334347445,N028d"),
+ position("2008-04-06 12:49:45.000", true, 44.61041, 10.90772));
+
+ verifyPosition(decoder, text(
+ "%356939010014433,$GPRMC,172821.000,A,4019.5147,N,00919.1160,E,0.00,,010613,,,A,+393998525043,N098d"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java
new file mode 100644
index 000000000..487a8500c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt502FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt502FrameDecoder decoder = new Pt502FrameDecoder();
+
+ verifyFrame(
+ binary("24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280"),
+ decoder.decode(null, null, binary("bffbf6d10324504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a28012800d0a")));
+
+ verifyFrame(
+ binary("244655533836353332383032363234333836342c3531302d56312e31322c4131312d56332e30"),
+ decoder.decode(null, null, binary("bffb192d00244655533836353332383032363234333836342c3531302d56312e31322c4131312d56332e300d0d")));
+
+ verifyFrame(
+ binary("24504f532c313336303030303237372c3138323234312e3030302c412c303834362e303839362c4e2c30373535322e313733382c572c31332e35382c32362e38382c3239313031372c2c2c412f30303030302c30303030302f3134322c302c302c302f36323739323930302f2f6636352f2f23"),
+ decoder.decode(null, null, binary("bffb57d50124504f532c313336303030303237372c3138323234312e3030302c412c303834362e303839362c4e2c30373535322e313733382c572c31332e35382c32362e38382c3239313031372c2c2c412f30303030302c30303030302f3134322c302c302c302f36323739323930302f2f6636352f2f230a24504f532c313336303030303237372c3138323235312e3030302c412c303834362e313234382c4e2c30373535322e313534352c572c31352e35322c33362e39332c3239313031372c2c2c412f30303030302c30303030302f3134312c302c302c302f36323739333030302f2f6636382f2f230a24504f532c313336303030303237372c3138323332342e3030302c412c303834362e323633362c4e2c30373535322e303630352c572c31382e39342c32392e39302c3239313031372c2c2c412f30303030302c30303030302f3133652c302c302c302f36323739333330302f2f6639372f2f230a24504f532c313336303030303237372c3138323332362e3030302c412c303834362e323733302c4e2c30373535322e303535342c572c31392e31322c33302e34322c3239313031372c2c2c412f30303030302c30303030302f3134302c302c302c302f36323739333330302f2f6639382f2f230a")));
+
+ verifyFrame(
+ binary("24504f532c3836353332383032363234333836342c3134343733352e3030302c412c313333322e373038332c4e2c3230342e363833312c452c302e302c3233302e30302c3032303531372c2c2c412f30303030302c31302f312c302f3233342f2f4646392f"),
+ decoder.decode(null, null, binary("24504f532c3836353332383032363234333836342c3134343733352e3030302c412c313333322e373038332c4e2c3230342e363833312c452c302e302c3233302e30302c3032303531372c2c2c412f30303030302c31302f312c302f3233342f2f4646392f0d0a")));
+
+ verifyFrame(
+ binary("24504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f"),
+ decoder.decode(null, null, binary("24504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f0d0a")));
+
+ verifyFrame(
+ binary("24504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f"),
+ decoder.decode(null, null, binary("bffb1b6a0024504f532c3335333435313030303136342c3038323430352e3030302c412c313235342e383530312c4e2c31303035312e363735322c452c302e30302c3233372e39392c3136303531332c2c2c412f303030302c302f302f35353030302f2f6137312f0d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
new file mode 100644
index 000000000..cb1c1eb0e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
@@ -0,0 +1,86 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Pt502ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt502ProtocolDecoder decoder = new Pt502ProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO3821-1,1156802639,022125.000,A,0707.0014,N,07307.3725,W,0.0,0.1,110418,,,A/00000,00000/0,0,0,0/500//fd4//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,1360000277,182241.000,A,0846.0896,N,07552.1738,W,13.58,26.88,291017,,,A/00000,00000/142,0,0,0/62792900//f65//#"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO0-1,1360000260,123012.000,A,0913.9644,N,07548.8345,W,0.0,309.8,111017,,,A/00000,10000/0,0,0,0/64551600//f98//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,865328026243864,151105.000,A,1332.7096,N,204.6787,E,0.0,10.00,050517,,,A/00000,10/1,0/234//FD9/"));
+
+ verifyNull(decoder, buffer(
+ "$FUS865328026243864,510-V1.12,A11-V3.0"));
+
+ verifyPosition(decoder, buffer(
+ "$HDA,20007,134657.000,A,0626.1607,N,00330.2245,E,33.38,81.79,041016,,,A/00010,00000/270,0,0,0/19948900//fa4//"));
+
+ verifyPosition(decoder, buffer(
+ "$HDB,20007,134708.000,A,0626.1759,N,00330.3192,E,26.55,80.37,041016,,,A/00010,00000/23b,0,0,0/19949100//fa4//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,20007,134704.000,A,0626.1698,N,00330.2870,E,31.23,79.58,041016,,,A/00010,00000/26c,0,0,0/19949100//fa4//#"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO6608,115099,133140.000,A,1307.1238,N,05936.4194,W,0.00,21.50,290816,,,A/00010,00000/0,0,0,0/185100//f59/"));
+
+ verifyPosition(decoder, buffer(
+ "$DFR,40456789,083125.000,A,2232.0971,N,11400.9504,E,0.0,5.00,090714,,,A/00000,00/0,0/200076//FE7/"));
+
+ verifyPosition(decoder, buffer(
+ "$FDA,40456789,083125.000,A,2232.0971,N,11400.9504,E,0.0,5.00,090714,,,A/00000,00/0,0/200076//FE7/"));
+
+ verifyAttribute(decoder, buffer(
+ "$CPA,40456789,083125.000,A,2232.0971,N,11400.9504,E,7.62,265.24,291117,,,A/00000,00000/0/1200//#"), Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+
+ verifyPosition(decoder, buffer(
+ "$POS,216769295715,163237.000,A,3258.1738,S,02755.4350,E,0.00,215.88,100915,,,A/0000,0//232300//5b3/"),
+ position("2015-09-10 16:32:37.000", true, -32.96956, 27.92392));
+
+ verifyPosition(decoder, buffer(
+ "$POS,11023456,033731.000,A,0335.2617,N,09841.1587,E,0.00,88.12,210615,,,A/0000,0/1f8/388900//f33//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6094,205523.000,A,1013.6223,N,06728.4248,W,0.0,99.3,011112,,,A/00000,00000/0/23895000//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6120,233326.000,V,0935.1201,N,06914.6933,W,0.00,,151112,,,A/00000,00000/0/0/"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6002,233257.000,A,0931.0430,N,06912.8707,W,0.05,146.98,141112,,,A/00010,00000/0/5360872"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,6095,233344.000,V,0933.0451,N,06912.3360,W,,,151112,,,N/00000,00000/0/1677600/"));
+
+ verifyPosition(decoder, buffer(
+ "$PHO0,6091,233606.000,A,0902.9855,N,06944.3654,W,0.0,43.8,141112,,,A/00010,00000/0/224000//"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,353451000164,082405.000,A,1254.8501,N,10051.6752,E,0.00,237.99,160513,,,A/0000,0/0/55000//a71/"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,012896008586486,154215.000,A,0118.0143,S,03646.9144,E,0.00,83.29,180714,,,A/0000,0/0/29200//644/"));
+
+ verifyPosition(decoder, buffer(
+ "$POS,1151000,205326.000,A,0901.3037,N,07928.2751,W,48.79,30.55,170814,,,A/00010,10000/0,0,0,0/15986500//fb8/"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
new file mode 100644
index 000000000..9e4a8a9a0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java
@@ -0,0 +1,69 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Pt502ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodeCustom() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "#PTI300");
+
+ assertEquals("#PTI300\r\n", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeOutputControl() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 2);
+ command.set(Command.KEY_DATA, "1");
+
+ assertEquals("#OPC2,1\r\n", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeTimezone() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+ command.set(Command.KEY_TIMEZONE, "GMT+8");
+
+ assertEquals("#TMZ8\r\n", encoder.encodeCommand(command));
+
+ }
+
+
+ @Test
+ public void testEncodeAlarmSpeed() throws Exception {
+
+ Pt502ProtocolEncoder encoder = new Pt502ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_SPEED);
+ command.set(Command.KEY_DATA, 120);
+
+ assertEquals("#SPD120\r\n", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java
new file mode 100644
index 000000000..1ba8d25c7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Pt60ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Pt60ProtocolDecoder decoder = new Pt60ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "@B#@|01|006|864891030184954|9425010450971470|20181213093127|2|1|"));
+
+ verifyNull(decoder, text(
+ "@B#@|01|006|864891030184954|9425010450971470|20181213093235|40412,10461,1,425,4|2|1|"));
+
+ verifyNotNull(decoder, text(
+ "@B#@|01|001|864891030184852|9425010450971470|1|84|20181205161005|40412,10461,1,425,10|2|"));
+
+ verifyPosition(decoder, text(
+ "@B#@|01|001|864891030184852|9425010450971470|1|45|20181127122717|32.701093|35.570938|1|"));
+
+ verifyNull(decoder, text(
+ "@B#@|01|003|864891030184954|9425010450971470|S6_EN_A_V1.3.7|0|66|20181122113251|40412,10461,1,425,18|49382,10461,1,425,9|40411,10461,1,425,7|2|"));
+
+ verifyNull(decoder, text(
+ "@B#@|01|033|864891030184954|9425010450971470|0|4|20181120151744|"));
+
+ verifyPosition(decoder, text(
+ "@B#@|01|001|111112222233333|8888888888888888|1|55|20160715150323|37.615124|125.48276|111.059279|49.346383|1|"));
+
+ verifyPosition(decoder, text(
+ "@B#@|01|001|111112222233333|8888888888888888|1|55|20160715150323|37.615124|125.48276|1|"));
+
+ verifyAttributes(decoder, text(
+ "@G#@,V01,14,357653051059785,9404223001501310,20180419165604,101,26,"));
+
+ verifyAttributes(decoder, text(
+ "@G#@,V01,13,357653051059785,9404223001501310,20180419112656,1180,"));
+
+ verifyPosition(decoder, text(
+ "@G#@,V01,6,111112222233333,8888888888888888,20150312010203,23.2014050;104.235212,"));
+
+ verifyNull(decoder, text(
+ "@G#@,V01,1,353882080015633,9460025014649193,"));
+
+ verifyNull(decoder, text(
+ "@G#@,V01,43,105681639001701,9460000412618231,20180410092923,5,460;0;9763;3852;-63|460;0;9763;3851;-26|460;0;9763;4080;-22|460;0;9763;3593;-18|460;0;9763;3591;-10,5,14:b8:37:26:64:88;004300680069006e0061004e00650074002d006e0047006e0058;-76|08:9b:4b:93:b5:b1;005400480049004e004b0052004100430045;-77|ec:3d:fd:c9:38:4a;004b00460052006f0075007400650072;-78|b0:d5:9d:c6:f8:82;003300360030514d8d390057006900460069002d00380032;-81|02:fa:84:3b:fa:6a;00470075006500730074005f0032002e003400470048007a;-82,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java
new file mode 100644
index 000000000..318cbdb51
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RaceDynamicsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RaceDynamicsProtocolDecoder decoder = new RaceDynamicsProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$GPRMC,12,260819,100708,862549040661129,"));
+
+ verifyPositions(decoder, text(
+ "$GPRMC,15,04,H,#,100632,A,1255.5106,N,07738.2954,E,001,260819,0887,06,1,00011,%,0000000000000000,000,000,0,0,1,0713,0,416,0,255,000,0,000,3258,000,000,00,0000,000,00000,0,F3VF01,%,#,#,100633,A,1255.5107,N,07738.2955,E,001,260819,0887,06,1,00012,%,0000000000000000,000,000,0,0,1,0713,0,416,0,255,000,0,000,3453,000,000,00,0000,000,00000,0,F3VF01,%,#,#,100634,A,1255.5106,N,07738.2964,E,001,260819,0887,06,1,00013,%,0000000000000000,000,000,0,0,1,0713,0,392,0,255,000,0,000,3651,000,000,00,0000,000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java
new file mode 100644
index 000000000..7dfdd7c21
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RadarProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RadarProtocolDecoder decoder = new RadarProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "361800011459015cb497554c01c101ff003500050038000207ff831c04c01f1c00555cb464895cb46487ff7f04eafeffdbd80000079402ead0000000110000000000120d2aff150000000000000002000a00050002436c61726f0000000000008b00000003764500037653005207ff831c04c01f1c00555cb4648a5cb46489ff7f04eafeffdbd80000079402ead0000000010000000000120e00ff150000000000000002000800060002436c61726f0000000000008d00000003764600037654000207ff831c04c01f1c00555cb464d85cb464d7ff7f04eafeffdbd80000079402ead0000000110000000000120e2aff150000000000000002000700070003436c61726f0000000000008d000000037694000376a2005207ff831c04c01f1c00555cb464d95cb464d9ff7f04eafeffdbd80000079402eac0000000010000000000120e00ff150000000000000002000700070003436c61726f0000000000008d000000037695000376a3000207ff831c04c01f1c00555cb465065cb46504ff7f04eafeffdbd80000079402ead0000000110000000000120e2aff150000000000000000000500060005436c61726f0000000000008d0000000376c2000376d07ed7"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java
new file mode 100644
index 000000000..165027351
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RaveonProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RaveonProtocolDecoder decoder = new RaveonProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$PRAVE,0001,0001,3308.9051,-11713.1164,195348,1,10,168,31,13.3,3,-83,0,0,,1003.4*66"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java
new file mode 100644
index 000000000..668879787
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RecodaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RecodaProtocolDecoder decoder = new RecodaProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "01100020480000000300000030393535360000000000000001000000303030303000000000000000000000000000000000000000006100004531313037353500ffffffffffff0000"));
+
+ verifyNull(decoder, binary(
+ "01200020100000000300000002000000"));
+
+ verifyNull(decoder, binary(
+ "0110000008000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java
new file mode 100644
index 000000000..779e21823
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RetranslatorProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RetranslatorProtocolDecoder decoder = new RetranslatorProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "74000000333533393736303133343435343835004B0BFB70000000030BBB000000270102706F73696E666F00A027AFDF5D9848403AC7253383DD4B400000000000805A40003601460B0BBB0000001200047077725F657874002B8716D9CE973B400BBB00000011010361766C5F696E707574730000000001"));
+
+ verifyPosition(decoder, binary(
+ "1f010000333533353439303930303934373330005b129b5f000000010bbb000000270102706f73696e666f004e3be14e5ec356c0e0e92f6201282c400000000000000000000000130d0bbb0000000a00036d636300000002c00bbb0000000a00036d6e6300000000010bbb0000000a00036c616300000001490bbb0000000e000363656c6c5f696400000056590bbb0000000a000361636300000000000bbb000000100003646174615f6d6f6465000000000e0bbb0000001200036770735f7265616c5f757000000000000bbb0000000d000373657269616c000000089b0bbb0000001000016d73675f747970650030783232000bbb000000110004637573746f6d0000000000000033400bbb0000001200046d696c65616765000000000000000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java
new file mode 100644
index 000000000..73d07efd6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RitiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RitiProtocolDecoder decoder = new RitiProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "3b28a2a2056315316d4000008100000000000000005f710000244750524d432c3138303535332e3030302c412c353532342e383437312c4e2c30313133342e313837382c452c302e30302c2c3032313231332c2c2c412a37340d0a00000000000000000000000000000000040404"));
+
+ verifyPosition(decoder, binary(
+ "3b2864a3056300006d40000003000000000000000000000000244750524d432c3231313734332e3030302c412c313335372e333637352c4e2c31303033362e363939322c452c302e30302c2c3031303931342c2c2c412a37380d0a00000000000000000000000000000000040404"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java
new file mode 100644
index 000000000..2e3853f86
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RoboTrackFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RoboTrackFrameDecoder decoder = new RoboTrackFrameDecoder();
+
+ verifyFrame(
+ binary("00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e"),
+ decoder.decode(null, null, binary("00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java
new file mode 100644
index 000000000..0c969ab68
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RoboTrackProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RoboTrackProtocolDecoder decoder = new RoboTrackProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "00524f424f545241434b00000000000000383638323034303032323533343136313233343536373839303132312e313261000000312e353761000000312e3030000000003e"));
+
+ verifyPosition(decoder, binary(
+ "03e020bb5a034409034862120210a9e105000000000000b9"));
+
+ verifyPosition(decoder, binary(
+ "03f120bb5a30460903426312021798e105000000000000cd"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java
new file mode 100644
index 000000000..fb6dca3e6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class RstProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RstProtocolDecoder decoder = new RstProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "RST;L;RST-MINIv2;V7.02;008068078;61;1;27-01-2020 21:36:33;27-01-2020 21:36:33;-16.696159;-49.284275;0;67;786;1;15;0;00;B0;00;19;06;12.42;4.16;79;20;FE;0000;01;E0;00800020;0;467;FIM;"));
+
+ verifyAttribute(decoder, text(
+ "RST;A;RST-MINIv2;V7.00;008033985;1;7;30-08-2019 11:31:38;30-08-2019 11:31:15;-23.645868;-46.637741;0;226;828;0;10;0;00;20;00;1A;02;0.02;3.40;0;0;FE;0000;04;80;11;0;FIM;"),
+ Position.KEY_BATTERY, 3.40);
+
+ verifyPosition(decoder, text(
+ "RST;A;RST-MINIv2;V7.00;008033985;1;7;30-08-2019 11:31:38;30-08-2019 11:31:15;-23.645868;-46.637741;0;226;828;0;10;0;00;20;00;1A;02;0.02;3.40;0;0;FE;0000;04;80;11;0;FIM;"));
+
+ verifyPosition(decoder, text(
+ "RST;A;RST-MINIv2;V7.00;008033985;6;47;30-08-2019 19:01:13;30-08-2019 19:01:14;-23.645851;-46.637817;0;294;811;1;11;0;00;30;00;1A;02;3.82;4.16;0;0;FE;0000;02;40;71;000001F60A55;FIM;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
new file mode 100644
index 000000000..4c7b09bcc
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class RuptelaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ RuptelaProtocolDecoder decoder = new RuptelaProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "03fc0003142b0c152acd2502003544444131464144000a0000ffd8ffe000104a46494600010100000100010000ffdb00c50006040506050406060506070706080a100a0a09090a140e0f0c1017141818171416161a1d251f1a1b231c1616202c20232627292a29191f2d302d283025282928010707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828020707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828ffc000110800f0014003012200021101031102ffc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffdd00040000ffda000c03010002110311003f00e27534fde484fa66950079add40e153754d73892794f552e00a6da0c4b282794f947d2aa92b2b1d7887795fb9b5a4200eee7e957e6bdfb2b0f2cb6eff669cba4dee99656d25f5acb02dd209622e38753d08fc315177cf151562e337196e8e46f5352cb5d6603cd52ebeb8c1adcb4be82e07eedc67d0f06b8f071daa64618c8383508573ae991251891430f7a836cd07fa97de9fdc7fe86b0adf51b8830377989e8d5a96daa413603131bfa350061fc49d41adbc39e5edc34ee14f7c639af1b91f2719aef7e2c5d4bf6cb780b7ee7607500f19e735e7a06e3c9e2b4a4b4b88b51b2aaf14bbc1351226796a79e3815b81283c500d460d381a405988d7a6785ad8c3a241bc9f9f2e47d7ffad5e73a5db35dddc30a757603e83bd7aba011c6a8bc2a8000a89e8807b1551815a5636fba38ce3af359248cd7516298862ff7456713fc58"));
+
+ verifyNull(decoder, binary(
+ "002e000315bc70d3e2ff0f4f42443130302e30312e30382e30300000c2b30ea77e430000601b000001f40000003c00144aa0"));
+
+ verifyAttributes(decoder, binary(
+ "0011000315A07F440B1D07534554494f20636f6e66696775726174696f6e2064617461206f6b341C"));
+
+ verifyAttributes(decoder, binary(
+ "0044000313612d76c5cb0744494e313d312c44494e323d302c44494e333d302c44494e343d302c444f5554313d312c444f5554323d312c41494e313d31372c41494e323d3236ac80"));
+
+ verifyPositions(decoder, binary(
+ "000B00000B1A29F64B1A0902FF4E9CAF2C07D608F11A1480BA015030303130FF4E9CAF2C07D608F11A1480BA0250303031318C91"));
+
+ verifyPositions(decoder, binary(
+ "01a4000315bc70f9b69244000458068f4a0030000d11398a1c0c19fd056524040b000c0a00090c0005010031f40032fd0033f200ce47002400002500001c010199000195010196010086000900aa0000001e0ff000d3ffff0043ffff01930000019200000194000002220000022300000200300000000200af000e872401008e000000000000000058068f4a0031000d11398a1c0c19fd056524040b000c0a00090400870000880000a90000820010008b0002021e0000021f0000021d0000021c0000022400000225000000890000008505f00220000002210000008300000084000002260000022700000228000003008a00000000008d00000000008c000000000058068f4a0032000d11398a1c0c19fd056524040b000c0a000905019f01005800001b1f00ad0000cfb10b02290000022a0000022b0000022c0000022d00000012000000130000001d367400c52f8000740055023e0502060097000000000096000058520041007746cb00d0000003f1005c0007c21b0072001864880058068f4a0033000d11398a1c0c19fd056524040b000c0a000900000001008e0000000000000000e815"));
+
+ verifyPositions(decoder, binary(
+ "033d000315bc70f9b69244000858068f3b0030010d11354e1c0c17a5055d54560c00000900050c0005010031f30032fb0033f300ce00002400002500001c010199000195010196010086000900aa0000001e0ff300d3ffff0043ffff01930000019200000194000002220000022300000200300000000000af000e872401008e000000000000000058068f3b0031010d11354e1c0c17a5055d54560c00000900050400870000880000a90000820010008b0000021e0000021f0000021d0000021c0000022400000225000000890000008500000220000002210000008300000084000002260000022700000228000003008a00000000008d00000000008c000000000058068f3b0032010d11354e1c0c17a5055d54560c000009000505019f01005800001b1f00ad0000cfac0b02290000022a0000022b0000022c0000022d00000012000000130000001d31b100c5000000740000023e0502060097000000000096000058520041007746be00d0000003f1005c0007c2150072001864880058068f3b0033010d11354e1c0c17a5055d54560c000009000500000001008e000000000000000058068f3b0130000d11354e1c0c17a5055d54560d00000900070c0005010031f30032fb0033f300ce00002400002500001c010199000195010196010086000900aa0000001e0ff300d3ffff0043ffff01930000019200000194000002220000022300000200300000000000af000e872401008e000000000000000058068f3b0131000d11354e1c0c17a5055d54560d00000900070400870000880000a90000820010008b0000021e0000021f0000021d0000021c0000022400000225000000890000008500000220000002210000008300000084000002260000022700000228000003008a00000000008d00000000008c000000000058068f3b0132000d11354e1c0c17a5055d54560d000009000705019f01005800001b1f00ad0000cfac0b02290000022a0000022b0000022c0000022d00000012000000130000001d31ae00c5000000740000023e0502060097000000000096000058520041007746be00d0000003f1005c0007c2150072001864880058068f3b0133000d11354e1c0c17a5055d54560d000009000700000001008e0000000000000000084d"));
+
+ verifyPositions(decoder, binary(
+ "0050000310f5615f419c0100015613d8ed0000fff5b37a035af37801e700000900000d07071b0c020003001c01202cad000500064302a81d33e61e100116317cd3ffff174ad60241000077fa960000f232003c2e"));
+
+ verifyPositions(decoder, binary(
+ "00560003116e7438a7a50100015565cbb9000020fd21300f113f4600005f000600090d090805011b13cf00020003001c012029ad00041d31dd1e0ebd160000c50000047200000000d0000000004100016a2a960000a5a300c9ee"));
+
+ verifyPositions(decoder, binary(
+ "00a10003116e7438a7a5010002553dddbe000020fddaff0f12289b007200000600000c070805011b18cf00020003001c01201dad01041d32d81e0d7d160000c50000047200000000d000000000410000b1ae960000a5a300553dddd4000020fdd96f0f122bfe005c16f80700050b090805011b18cf00020003001c01201ead01041d338a1e0d8d160000c50000047200000000d000000000410000b1bd960000a5a3001681"));
+
+ verifyPositions(decoder, binary(
+ "007900000b1a2a5585c30100024e9c036900000f101733208ff45e07b31b570a001009090605011b1a020003001c01ad01021d338e16000002960000601a41014bc16d004e9c038400000f104fdf20900d20075103b00a001308090605011b1a020003001c01ad01021d33b116000002960000601a41014bc1ea0028f9"));
+
+ verifyPositions(decoder, binary(
+ "009200000c07a6bacd4701000552db5cc20000187b8b251ace478e087c044c0a000009070000000052db5cfe0000187b8ab01ace47190879044c0900000b070000000052db5d3a0000187b8b251ace474b089d044c09000009070000000052db5d760000187b8b9a1ace475c08cd044c08000009070000000052db5db20000187b8b141ace46e708b3044c08000009070000000041cb"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
new file mode 100644
index 000000000..613a89b96
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class RuptelaProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ RuptelaProtocolEncoder encoder = new RuptelaProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+
+ command.set(Command.KEY_DATA, " Setio 2,1");
+ verifyCommand(encoder, command, binary("000b6c20536574696F20322C31eb3e"));
+
+ command.set(Command.KEY_DATA, "000b890100000000007fffffff89f0");
+ verifyCommand(encoder, command, binary("000b890100000000007fffffff89f0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java
new file mode 100644
index 000000000..8a4a42b54
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class S168ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ S168ProtocolDecoder decoder = new S168ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "S168#358511139046180#00c9#0009#SYNC:0000"));
+
+ verifyPosition(decoder, text(
+ "S168#000000000000008#0f12#0077#LOCA:G;CELL:1,1cc,2,2795,1435,64;GDATA:A,12,160412154800,22.564025,113.242329,5.5,152,900;ALERT:0000;STATUS:89,98;WAY:0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java
new file mode 100644
index 000000000..7a42a71a0
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class SabertekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SabertekFrameDecoder decoder = new SabertekFrameDecoder();
+
+ assertEquals(
+ binary("2c3939393939393939392c332c34302c36352c372c302c312c2d32352e3738313636362c32382e3235343730322c34302c3236382c313431342c382c35353632332c"),
+ decoder.decode(null, null, binary("022c3939393939393939392c332c34302c36352c372c302c312c2d32352e3738313636362c32382e3235343730322c34302c3236382c313431342c382c35353632332c030d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java
new file mode 100644
index 000000000..20b02841e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SabertekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SabertekProtocolDecoder decoder = new SabertekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ",999999999,3,40,65,7,0,1,-25.781666,28.254702,40,268,1414,8,55623,"));
+
+ verifyPosition(decoder, text(
+ ",999999999,4,356495040613400,89270200120171498287,+27821234123,20180525145412,60,75,15,1,1,-25.781666,28.254702,40,268,1414,8,24844,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java
new file mode 100644
index 000000000..36fd7ecba
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SanavProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SanavProtocolDecoder decoder = new SanavProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "imei=353197040023431&rmc=$GPRMC,015258.000,A,2457.8101,N,12125.5393,E,0.00,0.00,210111,,*18,AUTO,0300,2.1,10,466,97,34E7,3391,74,466,9 7,3F2D,3391,65,466,97,39C9,3391,79,466,97,3F2C,3391,81,466,97,0000,00 00,83,466,97,0000,0000,85,466,97,0000,0000,85,1,24"));
+
+ verifyPosition(decoder, text(
+ "imei=1234567890&rmc=$GPRMC,091950.00,V,5300.10000,N,00900.14000,E,0.160,,200513,,,A*68,STOP,V3.872;67%,S4,H8.3,D2.38"));
+
+ verifyPosition(decoder, text(
+ "imei=352024028982787&rmc=$GPRMC,103048.000,A,4735.0399,N,01905.2895,E,0.00,0.00,171013,,*05,AUTO-4095mv"),
+ position("2013-10-17 10:30:48.000", true, 47.58400, 19.08816));
+
+ verifyPosition(decoder, text(
+ "imei:352024028980000rmc:$GPRMC,093604.354,A,4735.0862,N,01905.2146,E,0.00,0.00,171013,,*09,AUTO-4103mv"));
+
+ verifyPosition(decoder, text(
+ "imei:352024027800000rmc:$GPRMC,000025.000,A,4735.0349,N,01905.2899,E,0.00,202.97,171013,,*03,3950mV,AUTO"));
+
+ verifyPosition(decoder, text(
+ "imei:352024020976845rmc:$GPRMC,000201.000,A,4655.7043,N,01941.3796,E,0.54,159.14,171013,,,A*65,AUTO"));
+
+ verifyPosition(decoder, text(
+ "imei:352024020976845rmc:$GPRMC,000201.000,A,4655.7043,N,01941.3796,E,0.54,159.14,171013,,,A*65,AUTO"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java
new file mode 100644
index 000000000..882a63fe6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SanulProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SanulProtocolDecoder decoder = new SanulProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "aa007020000100000000000033353333353830313831353431313700000000000000000000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java
new file mode 100644
index 000000000..45e919bbb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SatsolProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SatsolProtocolDecoder decoder = new SatsolProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "f0e1bf4cb2ec1600e1005f8791e901000000c959515c2cc24a03aeadcd010e01a800250001090201878e92e901000000cb59515c2dc24a03b8adcd018801a8001d0001080201325993e901000000cc59515c2fc24a03bfadcd01ab01a800220001080201dd8194e901000000cd59515c32c24a03ceadcd015801a8002500010802015f3795e905000900ce59515c32c24a03d8adcd01f600a700250001091401000000000000000000863496e901000000cf59515c34c24a03ddadcd019b00a700280001090201714197e904000600cf59515c34c24a03ddadcd019b00a7002800010a1401becd07001901"));
+
+ verifyPositions(decoder, binary(
+ "22b5bf4cb2ec1600500122b37ba020001500cb5d2f5c68c24a0310aecd016200b1003d000004000177042700a501000000000000000101020100011e00a1657ca020001500cf5d2f5c68c24a03dbadcd013501b10097000004030177042700a501000000000000000101020101011e00816e7da003004800b95e2f5c89c24a03fbadcd010000aa00000000040601011038363133353930333632333039323600383933373530323730313030323335343836363000323537303237303132333534383636004d5453000000000066640101005700e515cf047ea001000000295f2f5c89c24a03fbadcd0100009a0000000106020106dc30a8030048006bc13e5c19c24a03bfadcd010000ad00000000050600011338363133353930333632333039323600383933373530323730313030323335343836363000323537303237303132333534383636004d5453000000000066640101005700e515"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
new file mode 100644
index 000000000..4ab343876
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class SigfoxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SigfoxProtocolDecoder decoder = new SigfoxProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"device\" : \"33827B\", \"data\" : \"1f03198e63807f08836402ff\", \"time\" : \"1574346702\", \"snr\" : \"8.82\", \"station\" : \"140A\", \"avgSnr\" : \"11.28\", \"lat\" : \"52.0\", \"lng\" : \"-8.0\", \"rssi\" : \"-141.00\", \"seqNumber\" : \"3662\"}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"device\": \"49F941\", \"location\": {\"lat\":19.48954345634299,\"lng\":-99.09340606338463,\"radius\":1983,\"source\":2,\"status\":1} }")));
+
+ verifyAttribute(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"device\": \"40D310\", \"payload\": \"62\", \"time\": 1563043532, \"seqNumber\": 1076 }")),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyAttributes(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"device\": \"40D310\", \"payload\": \"20061494480389f956042a\", \"time\": 1563043532, \"seqNumber\": 1076 }")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"device\": \"1CEDCE\", \"payload\": \"2002419b4a91c2c6580e0564\", \"time\": 1559924939, \"seqNumber\": 87 }")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("%7B++%22device%22%3A%222BF839%22%2C++%22time%22%3A1510605882%2C++%22duplicate%22%3Afalse%2C++%22snr%22%3A45.61%2C++%22station%22%3A%2235A9%22%2C++%22data%22%3A%2200bd6475e907398e562d01b9%22%2C++%22avgSnr%22%3A45.16%2C++%22lat%22%3A-38.0%2C++%22lng%22%3A145.0%2C++%22rssi%22%3A-98.00%2C++%22seqNumber%22%3A228+%7D=")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java
new file mode 100644
index 000000000..3a91d8482
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SiwiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SiwiProtocolDecoder decoder = new SiwiProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803932,23992,E,0,,0,1,1,0,5055,0,5,A,22.289887,70.807192,152,168,102922,090317,28,1,12,5,4098,1,0,13,0,0,0,1.0,3.1CHKS_4.82,0!"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,2845,1320,Q,10,airtelgprs.com,1,1,0,0,876578,43,9,A,19.0123456,72.65347,45,0,055929,071107,22,5,1,0,3700,1210,0,2500,1230,321,0,1.1,4.0,1!"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803849,953,R,9,,0,1,1,0,0,0,8,A,19.066145,73.002278,213,178,122738,210217,28,5,12,6,4066,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ verifyPosition(decoder, text(
+ "$EIT,9803849,953,R,9,,0,1,1,0,0,0,8,A,19.066145,73.002278,213,178,122738,210217,28,5,12,6,4066,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803849,954,E,0,,0,1,1,0,0,0,0,V,0.000000,0.000000,0,0,122855,210217,29,5,12,5,4104,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,2845,1320,A,0,,1,1,0,0,876578,43,10,A,19.0123456,72.65347,45,0,055929,071107,22,5,1,0,3700,1210,0,2500,1230,321,0,1.1,4.0,1!"));
+
+ verifyPosition(decoder, text(
+ "$SIWI,9803849,956,E,0,,0,1,1,0,0,0,3,V,19.066935,73.003383,0,111,123037,210217,28,5,12,5,4071,1,0,2,0,0,0,1.0,3.1CHKS_4.82,0"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java
new file mode 100644
index 000000000..bc2dac7e9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java
@@ -0,0 +1,34 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SkypatrolProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SkypatrolProtocolDecoder decoder = new SkypatrolProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "000a02171101303131373232303031333537393833060200000006202020202020202020312020202020202030313137323230303133353739383320"));
+
+ verifyNull(decoder, binary(
+ "000402171101303131373232303031333537393833060200081046202020202020202020392020202020202030313137323230303133353739383320244750524d432c3134303931372e30302c412c333330322e3230313132352c532c30373133352e3837383338332c572c302e302c302e302c3036303731372c322e382c572c412a32370d0a00"));
+
+ verifyPosition(decoder, binary(
+ "0005021004FFFFFFFF0000000D313134373735383300CB000000000E11070C010184D032FB3841370000000016072B000017050032000000000000024E0C071116072C105900050000000000050000000000050000000003100260B7363B6306C11A00B73637F206BF19B73637F106B50EB73638B106BB0BB7363B6106B80AB73637F306B709000000000000000000"));
+
+ verifyNull(decoder, binary(
+ "000500030101383637383434303031373832333336420102000c0000fa07b5e101876c5b0e0a111606131c1b5e"));
+
+ // Enfora TT8750
+ verifyNull(decoder, binary(
+ "000502000000f1143035303031393031d1df002f00000d0187120115e556ff762aa90000000000aae40005d2000ee1bc0e010a042530000000000000070004000002233c096c00ee2a00233c008500f022233c0b0500f21d233c000000fb23000000000000000000000000000000000000000000000000000000"));
+
+ verifyNull(decoder, binary(
+ "00040200202020202020202020382020202020202030313137323230303131383531373820313220244750524d432c3232343833392e30302c412c303332382e3433383830362c4e2c30373633312e3630373731372c572c302e302c302e302c3139303731342c332e382c452c412a32420d0a00"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java
new file mode 100644
index 000000000..183af899b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SmartSoleProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SmartSoleProtocolDecoder decoder = new SmartSoleProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "#GTXRP=359366080000385,8,180514200051,34.041981,-118.255806,60,1,1,7,1.80,180514200051,4.16,11"));
+
+ verifyPosition(decoder, text(
+ "#GTXRP=359372090000290,11,180919105751,46.477173,6.445475,389,0,0,0,1.08,180919105751,4.10,10"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java
new file mode 100644
index 000000000..c2d74b433
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SmokeyProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SmokeyProtocolDecoder decoder = new SmokeyProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "534d0300865101019383025f0403000000000b86250200000c0000028f000102f8cc0900127f08"));
+
+ verifyAttributes(decoder, binary(
+ "534d0300865101019383025f0403000000000bcf260200000c0000028f000102f8cc090012360b"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java
new file mode 100644
index 000000000..b040d4ecf
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SolarPoweredProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SolarPoweredProtocolDecoder decoder = new SolarPoweredProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "7e850256553304728011003e811319130b0b11211f01a2e6be091fa0e10114cc1582020f00831000004e7400000044000000223819020c84114161726f6e34475630312d313931303331127e"));
+
+ verifyPosition(decoder, binary(
+ "7e850256553304728011003e811319130b0d160e2901a2e66f091fa0ab0014c39482020f0083100002f42c00000287000000fc2719021484114161726f6e34475630312d313931303331e67e"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java
new file mode 100644
index 000000000..dd63c07ac
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SpotProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SpotProtocolDecoder decoder = new SpotProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n",
+ "<messageList xmlns=\"http://v2.shared.globalstar.com\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://v2.shared.globalstar.com http://share.findmespot.com/shared/schema/spotXml-v2.xsd\">\n",
+ "<header>\n",
+ "<totalCount>1</totalCount>\n",
+ "<mode>LIVE</mode>\n",
+ "</header>\n",
+ "<message>\n",
+ "<id>891801957</id>\n",
+ "<esn>0-3112123</esn>\n",
+ "<esnName>0-3112123a</esnName>\n",
+ "<messageType>NEWMOVEMENT</messageType>\n",
+ "<messageDetail>SPOT Trace has detected that the asset has moved.</messageDetail>\n",
+ "<timestamp>2017-12-27T13:19:38.000Z</timestamp>\n",
+ "<timeInGMTSecond>1514380778</timeInGMTSecond>\n",
+ "<latitude>-1.28781</latitude>\n",
+ "<longitude>-47.93042</longitude>\n",
+ "<batteryState>GOOD</batteryState>\n",
+ "</message>\n",
+ "</messageList>")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
new file mode 100644
index 000000000..97246a665
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
@@ -0,0 +1,59 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class StarLinkProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ StarLinkProtocolDecoder decoder = new StarLinkProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,55,170518122023,16,,,,,,000000,1,1,0,0,0,0,0,0,10443,32722,12.664,03.910,,0,0,,01000001FDB3A9*BF"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,56,170518122023,20,,,,,,000000,1,1,0,0,0,0,0,0,10443,32722,12.664,03.910,,0,0,2,01000001FDB3A9,00000000000000000000*D9"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,57,170518122038,01,,,,,,000000,1,1,0,0,1,0,0,0,10443,32722,12.669,03.910,,0,0,0,99*6E"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,58,170518122045,19,,,,,,000000,1,1,0,0,1,0,0,0,10443,32722,12.678,03.910,,0,0*7C"));
+
+ verifyAttributes(decoder, text(
+ "$SLU068328,06,59,170518122054,16,,,,,,000000,1,1,0,0,0,0,0,0,10443,32723,12.678,03.910,,0,0,01000001FDB3A9,01000001ACE0A6*BF"));
+
+ verifyPosition(decoder, text(
+ "$SLU031B2B,06,622,170329035057,01,170329035057,+3158.0018,+03446.6968,004.9,007,000099,1,1,0,0,0,0,0,0,,,14.176,03.826,,1,1,1,4*B0"));
+
+ verifyPosition(decoder, text(
+ "$SLU031B2B,06,624,170329035143,01,170329035143,+3158.0171,+03446.6742,006.8,326,000099,1,1,0,0,0,0,0,0,10452,8723,14.212,03.827,,1,1,1,4*6D"));
+
+ verifyPosition(decoder, text(
+ "$SLU0330D5,06,3556,170314063523,19,170314061634,+3211.7187,+03452.8106,000.0,332,015074,1,1,0,0,0,0,0,0,10443,32722,12.870,03.790,,0,0*FC"));
+
+ verifyPosition(decoder, text(
+ "$SLU0330D5,06,3555,170314063453,20,170314061634,+3211.7187,+03452.8106,000.0,332,015074,1,1,0,0,0,0,0,0,10443,32722,12.838,03.790,,0,0,1,,1122*74"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375153,170117051824,01,170117051823,+3203.2073,+03448.1360,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,12.655,04.085,,0,0,0,99*45"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375155,170117052615,24,170117052613,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.290,04.083,,1,1*5B"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375156,170117052616,34,170117052614,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.277,04.084,1,1,1,1*F3"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375154,170117052613,04,170117052612,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.287,04.084,,1,0*5B"));
+
+ decoder.setFormat("#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,#LAC#,#CID#,#VIN#,#VBAT#");
+
+ verifyPosition(decoder, text(
+ "$SLU352353083185436,06,85,190527214903,01,190527214903,+0614.1883,-07535.5033,000.0,000,000082.505,5070,50473,0,12.148,03.507,,100,0.02,35.0,1,1513,60,1,99*30"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java
new file mode 100644
index 000000000..88be86054
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class StarcomProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ StarcomProtocolDecoder decoder = new StarcomProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "|unit=416307,unittype=5,address=186.167.243.28,kind=14,software_version=14.02.18,hardware_type=17,gps_type=6,longitude=-67.85891,latitude=10.21988,datetime_actual=2019/05/07 21:59:38,network=TCPIP.1|\r\n"));
+
+ verifyAttributes(decoder, text(
+ "|unit=934706,unittype=5,address=186.167.251.198,kind=1,pending=0,mileage=202428.416,odometer=0,logic_state=1,reason=2,eventid=0,response=0,longitude=-66.99394,latitude=10.54119,altitude=544,gps_valid=1,gps_connected=1,satellites=7,velocity=29,heading=123,emergency=0,driver=0,ignition=1,door=0,arm=0,disarm=0,extra1=0,extra2=0,extra3=0,siren=0,lock=0,immobilizer=0,unlock=0,fuel=0,rpm=0,modemsignal=0,main_voltage=28.31,backup_voltage=96.00,analog1=0.00,analog2=0.00,analog3=0.37,datetime_utc=1963/08/16 15:43:56,datetime_actual=1899/12/30 00:00:00,network=TCPIP.1|\r\n"));
+
+ verifyPosition(decoder, text(
+ "|unit=111111,unittype=5,address=111.111.111.111,kind=1,pending=1,mileage=23.808,odometer=1300,logic_state=1,reason=1,eventid=52,response=1,longitude=-11.11111,latitude=-11.11111,altitude=786,gps_valid=1,gps_connected=1,satellites=7,velocity=1,heading=0,emergency=0,driver=0,ignition=1,door=1,arm=0,disarm=0,extra1=0,extra2=0,extra3=0,siren=0,lock=0,immobilizer=1,unlock=0,fuel=0,rpm=0,modemsignal=0,main_voltage=12.06,backup_voltage=-1.00,analog1=0.00,analog2=0.00,analog3=0.00,datetime_utc=2017/11/16 03:18:59,datetime_actual=2017/11/16 03:18:59,network=TCPIP.1|\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java
new file mode 100644
index 000000000..e2be2028e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Stl060ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Stl060ProtocolDecoder decoder = new Stl060ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$1,357804048043099,D001,AP29AW0963,23/02/14,14:06:54,17248488N,078342226E,0.08,193.12,1,1,1,1,1,A"),
+ position("2014-02-23 14:06:54.000", true, 17.41415, 78.57038));
+
+ verifyPosition(decoder, text(
+ "$1,357804048043099,D001,AP29AW0963,12/05/14,07:39:57,1724.8564N,07834.2199E,0.00,302.84,1,1,1,1,1,A"));
+
+ verifyPosition(decoder, text(
+ "$1,357804047969310,D001,AP29AW0963,01/01/13,13:24:47,1723.9582N,07834.0945E,00100,010,0,0,0,0,0,A,"));
+
+ verifyPosition(decoder, text(
+ "$1,357804047969310,D001,AP29AW0963,01/01/13,13:24:47,1723.9582N,07834.0945E,00100,010,0,0,0,0,0,0008478660,1450,40,34,0,0,0,A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
new file mode 100644
index 000000000..1c84b5c89
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SuntechFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SuntechFrameDecoder decoder = new SuntechFrameDecoder();
+
+ verifyFrame(
+ binary("81004e05200013383fffff3401000301130a0512080400000000000000000000000047f9d5846a06810072225214010100020300a8002604c1000004b000000470000025a100000000000025c4000000a6"),
+ decoder.decode(null, null, binary("81004e05200013383fffff3401000301130a0512080400000000000000000000000047f9d5846a06810072225214010100020300a8002604c1000004b000000470000025a100000000000025c4000000a6")));
+
+ verifyFrame(
+ binary("5354363030414c563b303038373238333237"),
+ decoder.decode(null, null, binary("5354363030414c563b3030383732383332370d")));
+
+ verifyFrame(
+ binary("53543630305545583b3030383732383332373b32303b3536383b32303139303432343b30383a33333a31313b30626631323833623b3333343b32303b326631393b31383b2b32302e3531343134353b2d3130302e3734333731303b3030302e3030343b3030302e30303b31303b313b31303337373836313b31322e39363b3030303030303b34343b745f303d31333b4e5f303d303839442e303b745f313d31423b4e5f313d304537342e303b515f443d30420d0a3b42343b3032383439383b342e313b31"),
+ decoder.decode(null, null, binary("53543630305545583b3030383732383332373b32303b3536383b32303139303432343b30383a33333a31313b30626631323833623b3333343b32303b326631393b31383b2b32302e3531343134353b2d3130302e3734333731303b3030302e3030343b3030302e30303b31303b313b31303337373836313b31322e39363b3030303030303b34343b745f303d31333b4e5f303d303839442e303b745f313d31423b4e5f313d304537342e303b515f443d30420d0a3b42343b3032383439383b342e313b310d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
new file mode 100644
index 000000000..ad9be942f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -0,0 +1,211 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class SuntechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeTemperature() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ decoder.setHbm(true);
+ decoder.setIncludeAdc(true);
+ decoder.setIncludeTemp(true);
+
+ verifyAttribute(decoder, buffer(
+ "ST600STT;008350848;35;523;20191102;13:49:46;0bf14fdb;334;20;2f19;57;+20.466737;-100.825455;000.006;000.00;11;1;10274175;11.36;00000000;1;0300;018353;4.2;1;0.00;;;;00000000000000;0;28EE56B911160234:+13.7;:;:"),
+ Position.PREFIX_TEMP + 2, 13.7);
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:"));
+
+ verifyPosition(decoder, buffer(
+ "ST300EVT;205173382;07;564;20160322;23:23:18;232e19;+19.288278;-099.128750;000.122;000.00;9;1;478391;11.53;000100;2;1;9498;079324;4.3;1;0.00;0.00;0.00;00000000000000;0;2898E16006000058:-20.8;2861626006000039:+2.5;:"));
+
+ verifyPosition(decoder, buffer(
+ "ST600STT;008349958;35;523;20181112;00:49:30;0bf10d4e;334;20;2f19;22;+20.552718;-100.824478;050.021;095.41;12;1;1303603;14.30;10000000;4;8911;001987;4.1;0;0.00;;;;00000000000000;0;2826C8A70800000C:+26.6;:;:"));
+
+ }
+
+ @Test
+ public void testDecodeRpm() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ decoder.setHbm(true);
+ decoder.setIncludeRpm(true);
+
+ verifyAttribute(decoder, buffer(
+ "ST300STT;907131077;04;706;20190227;23:59:34;cc719;-12.963490;-038.499587;000.067;000.00;7;1;57095;12.50;000000;1;0337;000207;0.0;1;0;012E717F010000;1"),
+ Position.KEY_RPM, 0);
+
+ }
+
+ @Test
+ public void testDecodeHours() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ decoder.setHbm(true);
+
+ verifyAttribute(decoder, buffer(
+ "ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"),
+ Position.KEY_HOURS, 3188 * 60000L);
+
+ }
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "ST410STT;007638094;426;01;24153;724;4;-65;365;0;24161;724;4;365;0;0;24162;724;4;365;0;0;24363;724;4;365;0;0;24151;724;4;365;0;0;24991;724;4;365;0;0;24373;724;4;365;0;0;3.98;1;0176;2;016;20200106;19:18:04;-15.571860;-056.062637;000.852;238.28;6;1;201"));
+
+ verifyPosition(decoder, buffer(
+ "ST390STT;007579860;18;302;20191101;11:28:51;145b49;-23.267030;-047.298142;000.000;000.00;9;1;5;11.93;000000;2;0003;000002;4.03;0;20010000;22470;724;05;-58;5211;1"));
+
+ verifyAttribute(decoder, binary(
+ "81004e05200013383fffff3401000300130b020b2a0500000000000000000000000047f9ec846a06500000000012010200000123a1002904ba00010fb40000000000000000000000000000000000005989"),
+ Position.KEY_IGNITION, false);
+
+ verifyPosition(decoder, binary(
+ "81004e05200013383fffff3401000301130a0512080400000000000000000000000047f9d5846a06810072225214010100020300a8002604c1000004b000000470000025a100000000000025c4000000a6"));
+
+ verifyPosition(decoder, buffer(
+ "ALT;0520000295;3FFFFF;52;1.0.2;0;20190703;01:03:24;00004697;732;101;0002;59;+4.682583;-74.128142;0.00;0.00;6;1;00000000;00000000;9;1;;4.1;12.92;103188"));
+
+ verifyAttribute(decoder, buffer(
+ "ST300UEX;109003241;08;1026;20190425;17:36:04;04402;+04.722553;-074.052583;000.020;000.00;10;1;0;12.04;010000;51;CabAVL\"CabMensaje,0,58.5,-1.0,,,FinMensaje\"FinAVL\r\n;B1;0000000000;4.1;1"),
+ "fuel1", 58.5);
+
+ verifyPosition(decoder, buffer(
+ "ST600UEX;008728327;20;520;20190218;10:56:51;0bf1a893;334;20;2f19;18;+20.514195;-100.743597;000.015;000.00;9;1;3720808;12.89;000000;44;t_0=0D;N_0=0551.0;t_1=14;N_1=039F.0;Q_D=0B\r\n;9E;010440;4.1;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST600UEX;100850000;01;010;20081017;07:41:56;0000004f;450;20;0023;24;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;25;Welcome to Suntech World!;12;0;4.5;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;007238270;40;313;20190220;12:05:04;c99e48;+04.644623;-074.076922;010.390;202.77;20;1;997100;14.10;100000;2;8384;003634;4.1;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;109002029;08;1080;20190220;13:00:55;85405;+04.645710;-074.078525;007.760;005.19;10;1;6520802;13.86;100100;4;1716;0000039863;4.1;1;0.00;0000;0000;0;0"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;608945;129;20190215;15:04:53;3dce071558;+22.006721;-098.771016;001.198;000.00;11;1;2632589;12.21;010000;1;3211"));
+
+ verifyPosition(decoder, buffer(
+ "ST410STT;007272376;408;01;10217;732;103;-87;51511;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;3.8;1;2503;6;20181031;20:12:58;+04.741277;-074.048238;052.375;189.87;20;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST410STT;007272376;408;01;21651;732;123;-65;1824;1;21654;732;123;1824;0;0;22542;732;123;1824;0;0;21656;732;123;1824;0;0;21655;732;123;1824;0;0;22541;732;123;1824;0;0;0;0;0;0;0;0;3.7;1;0156;1;20180816;05:18:52;+04.722322;-074.052776;000.074;000.00;10;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST600STT;008084783;20;419;20180308;18:00:36;0032cc3e;736;3;445c;41;-16.530023;-068.084267;018.640;267.99;10;1;11655;13.33;100000;2;0336;000061;4.5;0;0.00"));
+
+ verifyPosition(decoder, buffer(
+ "ST600STT;107850496;20;419;20180227;14:30:45;00462b08;736;3;4524;50;-16.479091;-068.119119;000.346;000.00;4;1;0;13.89;000000;1;0223;000003;0.0;0;0.00"));
+
+ verifyPosition(decoder, buffer(
+ "ST600STT;100850000;01;010;20081017;07:41:56;0000004f;450;20;0023;24;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;00110000;1;0072;0;4.5;1;12.35"));
+
+ verifyPosition(decoder, buffer(
+ "STT;100850000;3FFFFF;26;010;1;20161117;08:37:39;0000004F;450;0;0014;20;+37.479323;+126.887827;62.03;65.43;10;1;00000101;00001000;1;2;0492"));
+
+ verifyPosition(decoder, buffer(
+ "STT;6009999006;3FFFFF;26;398;0;20170827;20:04:37;087d4760;310;410;0ba0;23;+40.123420;-074.995971;000.031;000.00;8;1;00000001;00000000;1;1;0006"));
+
+ verifyPosition(decoder, buffer(
+ "ST500STT;205450135;07;843;20170816;23:24:45;+19.338432;-099.179817;000.283;000.00;6;1;141121;12.89;0;0;1;4659;002.795;0;001.891;611;4.0"));
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;205170303;12;561;20170816;09:10:34;173f53;+19.082370;-098.214287;006.776;000.00;0;0;52982186;12.75;100000;2;6328;155747;4.2;1;0.00;0;0.00;0.00;00000000000000;0;28F2B7600600005D:+5.2;:;:"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Location;205576803;500;20170319;12:18:17;-22.846014;-046.322176;000.000;000.00;0;3.8;0;1;9159"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Emergency;205576803;500;20170319;12:15:22;-22.846014;-046.322176;000.000;000.00;0;2"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Location;205576803;500;20170312;12:56:52;-22.846014;-046.322176;000.000;000.00;0;3.8;0;0;0019"));
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;1;0072;0;4.5;1;1750;012497F1160000;1;004f001454;450;00;-320;20;255;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;205589913;05;527;20170304;02:21:33;be139;-25.398868;-049.325636;000.476;000.00;6;1;427;12.57;100000010;1;0172;017.159;0;002.327;12;4.0"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;638947;803;20170117;07:40:44;5d309;-01.287213;-047.917462;000.035;000.00;10;1;2036194;12.57;000000;1;0376;010360;4.2;1"));
+
+ verifyPosition(decoder, buffer(
+ "ST300ALT;205174410;14;712;20110101;00:00:07;00000;+20.593923;-100.336716;000.000;000.00;0;0;0;16.57;000000;81;000000;4.0;0;0.00;0000;0000;0;0"));
+
+ verifyNull(decoder, buffer(
+ "SA200ALV;317652"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Alert;123456;410;20141018;18:30:12;+37.478774;+126.889690;000.000;000.00;0;4.0;1;6002"),
+ position("2014-10-18 18:30:12.000", false, 37.47877, 126.88969));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Alert;123456;410;20141018;18:30:12;+37.478774;+126.889690;000.000;000.00;0;4.0;1;6002;02;0;0310000100;450;01;-282;70;255;3;0"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;317652;042;20120718;15:37:12;16d41;-15.618755;-056.083241;000.024;000.00;8;1;41548;12.17;100000;2;1979"),
+ position("2012-07-18 15:37:12.000", true, -15.61876, -56.08324));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;317652;042;20120721;19:04:30;16d41;-15.618743;-056.083221;000.001;000.00;12;1;41557;12.21;000000;1;3125"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;317652;042;20120722;00:24:23;4f310;-15.618767;-056.083214;000.011;000.00;11;1;41557;12.21;000000;1;3205"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;315198;042;20120808;20:37:34;3fac25;-15.618731;-056.083216;000.007;000.00;12;1;48;0.00;000000;1;0127"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;315198;042;20120809;13:43:34;4f310;-15.618709;-056.083223;000.025;000.00;8;1;49;12.10;100000;2;0231"));
+
+ verifyPosition(decoder, buffer(
+ "SA200EMG;317652;042;20120718;15:35:41;16d41;-15.618740;-056.083252;000.034;000.00;8;1;41548;12.17;110000;1"));
+
+ verifyPosition(decoder, buffer(
+ "SA200ALT;317652;042;20120829;14:25:58;16d41;-15.618770;-056.083242;000.029;000.00;0;0;2404240;0.00;000000;10"));
+
+ verifyPosition(decoder, buffer(
+ "SA200STT;430070;133;20130615;22:22:32;151347;+02.860514;-060.653351;000.003;000.00;12;1;0;12.39;000000;1;0208"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Location;344506;017;20130727;14:10:00;-25.398714;-049.296818;000.187;000.00;1;4.32;1;1;0001"));
+
+ verifyPosition(decoder, buffer(
+ "ST300STT;205027329;03;374;20150108;17:54:42;177b38;-23.566052;-046.477588;000.000;000.00;0;0;0;12.11;000000;1;0312"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Emergency;205283272;500;20150716;19:12:01;-23.659019;-046.695403;000.602;000.00;0;4.2;1;1;02;10820;2fdb090736;724;05;0;2311;255;0;100"));
+
+ decoder.setProtocolType(1);
+
+ verifyPosition(decoder, buffer(
+ "ST910;Location;907510186;552;20180504;23:15:45;3af54e5331;+19.301833;-099.190657;000.246;000.00;1;28462;80;1;0;0423;02;334;05;-215;20051;1;4;100"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Alert;485195;20170409;22:37:41;3be0133057;+24.882410;-107.509152;000.070;000.00;1;286734;72;02;295;05;-415;4912;255;10;10"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Location;485195;528;20170410;01:18:57;f1dd134840;+24.787139;-107.434679;000.020;000.00;1;286734;100;1;0;0188;02;295;05;-339;4936;255;4;74"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Location;560266;500;20161207;21:33:11;af910be101;-25.504234;-049.278003;000.080;000.00;1;10054889;70;1;1;1311;02;724;06;-317;3041;2;10;92"));
+
+ verifyPosition(decoder, buffer(
+ "ST910;Emergency;238569;528;20170403;00:02:09;7574160020;+19.661292;-099.144473;000.176;000.00;1;228638;1"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java
new file mode 100755
index 000000000..2a7ab2dee
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SupermateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SupermateProtocolDecoder decoder = new SupermateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "2:359672050130411:1:*,00000000,XT,A,10031b,140b28,80ad4c72,81ba2d2c,06ab,238c,020204010000,12,0,0000,0003e6"));
+
+ verifyPosition(decoder, text(
+ "2:359672050130411:2:*,00000000,UP,A,10031b,140a1c,80ad4bf6,81ba2dc3,0000,0000,020204000000,14,0,0000,0003e6"));
+
+ verifyPosition(decoder, text(
+ "2:359672050130411:1:*,00000000,BJ,A,10031b,140c2f,80ad5012,81ba1f27,0f4c,2e18,020204014000,14,0,0000,0003ed"));
+
+ }
+
+} \ No newline at end of file
diff --git a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java
new file mode 100644
index 000000000..f7b15ca2e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class SviasProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ SviasProtocolDecoder decoder = new SviasProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "[7061,3041,57,20324277,710,40618,141342,-93155840,-371754060,0,20469,0,16,1,0,0,11323,100,9,,32,4695"));
+
+ verifyPosition(decoder, text(
+ "[7041,3041,8629,20856286,710,60618,201027,-92268040,-371346250,7994,31844,38271,16,1,0,0,13416,100,0,0,5089"));
+
+ verifyPosition(decoder, text(
+ "[7051,3041,15270,30179873,710,70618,40335,-94679080,-360604930,0,35454,23148,0,1,0,0,12542,100,13,32,4971"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
new file mode 100644
index 000000000..c0511f2a1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
@@ -0,0 +1,131 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class T55ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T55ProtocolDecoder decoder = new T55ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$DEVID,0x0103846677F21422*41"));
+
+ verifyAttribute(decoder, text(
+ "$GPIOP,01000000,00000000,0.00,0.00,0.00,0.00,4.69,4.24*49"),
+ Position.KEY_BATTERY, 4.24);
+
+ verifyPosition(decoder, text(
+ "660420156A0066AA$GPRMC,122806.0,A,0119.212178,N,10355.000942,E,0.0,,230119,0.0,E,A*27"));
+
+ verifyNull(decoder, text(
+ "$IMEI=355797031609284"));
+
+ verifyNull(decoder, text(
+ "086415031C20"));
+
+ verifyNull(decoder, text(
+ "358244017671308"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,082350.000,5355.0314,N,01044.1271,E,1,10,0.7,-46.0,M,0.0,M,0.0,0000"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,082350.000,A,5355.0314,N,01044.1271,E,26.20,184.27,080518,,"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,192350.000,V,0000.0000,N,00000.0000,E,,,110318,,*12"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,073446.000,A,1255.5125,N,07738.2948,E,0.00,0.53,080316,D*71,11,865733027593268,1,090,086,123,456,789,987,12345"));
+
+ verifyNotNull(decoder, text(
+ "$GPRMC,161223.000,A,2517.0545,S,05739.1788,W,0.0,0.0,011196,,,A*61"));
+
+ verifyPosition(decoder, text(
+ "4711/022789000688081/$GPRMC,133343,A,5308.56325,N,1029.12850,E,0.000000,0.000000,290316,,*2A"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,073446.000,A,1255.5125,N,07738.2948,E,0.00,0.53,080316,D*71,11,865733027593268,1,090,086"));
+
+ verifyNull(decoder, text(
+ "$GPFID,ID123456ABC"));
+
+ verifyNull(decoder, text(
+ "$PGID,359853000144328*0F"));
+
+ verifyNull(decoder, text(
+ "$PCPTI,CradlePoint Test,184453,184453.0,6F*57"));
+
+ verifyNull(decoder, text(
+ "IMEI 351467108700000"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,012006,A,4828.10,N,1353.52,E,0.00,0.00,180915,020.3,E*42"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,094907.000,A,6000.5332,N,03020.5192,E,1.17,60.26,091111,,*33"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,115528.000,A,6000.5432,N,03020.4948,E,,,091111,,*06"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,064411.000,A,3717.240078,N,00603.046984,W,0.000,1,010313,,,A*6C"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,000000.0,4337.200755,N,11611.955704,W,1,05,3.5,825.5,M,-11.0,M,,*6F"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,000000,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"));
+
+ verifyPosition(decoder, text(
+ "$GPRMA,V,0000.00,S,00000.00,E,,,00.0,000.,11.,E*7"));
+
+ verifyPosition(decoder, text(
+ "$TRCCR,20140101001122.333,V,60.0,-100.0,1.1,2.2,3.3,4.4,*00"));
+
+ verifyPosition(decoder, text(
+ "$TRCCR,20140111000000.000,A,60.000000,60.000000,0.00,0.00,0.00,50,*3a"));
+
+ verifyPosition(decoder, text(
+ "$GPRMC,125735.000,A,6010.34349,N,02445.72838,E,1.0,101.7,050509,6.9,W,A*1F"));
+
+ verifyPosition(decoder, text(
+ "$GPGGA,000000.000,6010.34349,N,02445.72838,E,1,05,1.7,0.9,M,35.1,M,,*59"));
+
+ verifyPosition(decoder, text(
+ "123456789$GPGGA,000000.000,4610.1676,N,00606.4586,E,0,00,4.3,0.0,M,50.7,M,,0000*59"));
+
+ verifyPosition(decoder, text(
+ "123456789$GPRMC,155708.252,V,4610.1676,N,00606.4586,E,000.0,000.0,060214,,,N*76"));
+
+ verifyPosition(decoder, text(
+ "990000561287964,$GPRMC,213516.0,A,4337.216791,N,11611.995877,W,0.0,335.4,181214,,,A * 72"));
+
+ verifyPosition(decoder, text(
+ "355096030432529$GPGGA,000000.00,3136.599,S,5213.981,W,1,7,2.13,250.00,M,-16.384,M,3550960304325290.0,1"));
+
+ verifyPosition(decoder, text(
+ "355096030432529$GPGGA,000000.00,3136.628,S,5213.990,W,1,7,2.13,250.00,M,-16.384,M,0.0,1"));
+
+ }
+
+ @Test
+ public void testMaxonDecode() throws Exception {
+
+ // Maxon devices can send NMEA before identification
+
+ T55ProtocolDecoder decoder = new T55ProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "$GPRMC,012006,A,4828.10,N,1353.52,E,0.00,0.00,180915,020.3,E*42"));
+
+ verifyPosition(decoder, text(
+ "$GPFID,ID123456ABC"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java
new file mode 100644
index 000000000..f74d3c350
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class T57FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T57FrameDecoder decoder = new T57FrameDecoder();
+
+ verifyFrame(
+ binary("2a5435372346312354353731313137303031233330313131372330303038343323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e3223"),
+ decoder.decode(null, null, binary("2a5435372346312354353731313137303031233330313131372330303038343323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303038353323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303039303423323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303039313423323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303039323423323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e32232a5435372346312354353731313137303031233330313131372330303038333323323233342e31333033234e2330383832362e313731342345232b302e3234322c2b302e3130392c2d302e37383923302e30303023362e323030303023413223342e3223")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java
new file mode 100644
index 000000000..c457b4602
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class T57ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T57ProtocolDecoder decoder = new T57ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "*T57#F1#T571117001#301117#000843#2234.1303#N#08826.1714#E#+0.242,+0.109,-0.789#0.000#6.20000#A2#4.2#"));
+
+ verifyPosition(decoder, text(
+ "*T57#F1#0123456789#041117#152900#1258.9653#N#07738.4169#E#00000000000000000000#0.000#926.300#A2#4.0#"));
+
+ verifyPosition(decoder, text(
+ "*T57#F2#0123456789#041117#152900#1258.9653#N#07738.4169#E#00000000000000000000#0.000#926.300#A2#4.0#"));
+
+ verifyNull(decoder, text(
+ "*T57#F3#0123456789#041117#152900#1258.9653#N#07738.4169#E#I#9674432345#340#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
new file mode 100644
index 000000000..28b3fc5c6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
@@ -0,0 +1,66 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class T800xProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ T800xProtocolDecoder decoder = new T800xProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "2727040049001b0866425039645728c916190604005240000000007739d2c25b681f420000000080000081000020174105000005458216001e000000f01e00001e30d0000000000000"));
+
+ verifyAttribute(decoder, binary(
+ "272705005e000108664250328807851905301107481054002d004d006f00620069006c006500074341542d4e42310a4c54452042414e4420340f333130323430323030303032333030143839303132343032303531303030323330303746"),
+ Position.KEY_OPERATOR, "T-Mobile");
+
+ verifyPosition(decoder, binary(
+ "272702004904a90866425032880785c800190530080350000000000705eec29bf50842000000000008008090502a003700000a9e358002003c000003841900001e3f90000000000000272702004904aa0866425032880785c800190530081851000000000705eec29bf50842000000000008008090602e003700000a9e358002003c000003841900001e3f90000000000000"));
+
+ verifyNull(decoder, binary(
+ "2727010017000108806168988888881016010207110111"));
+
+ verifyNull(decoder, binary(
+ "252501001504050880061689888888111111250350"));
+
+ verifyAttribute(decoder, binary(
+ "2525810128000108664250328959160149004d00450049003a003800360036003400320035003000330032003800390035003900310036002c005300450054002000560045005200530049004f004e0020004f004b002c00560065007200730069006f006e003a00420061007300690063003a00560031002e0030002e0030002c004100500050003a00560034002e0032002e0033002c004200550049004c0044003a0032003000310039002d00300033002d00330030002c00300038003a00300035002c0050004c0054003a0032003500300033004100560045002c00480057003a00560032002e0031002c004d004f00440045004c003a002c004d004f00440045004d003a0042003900470036004d0041005200300032004100300037004d00310047002300"),
+ Position.KEY_RESULT, "IMEI:866425032895916,SET VERSION OK,Version:Basic:V1.0.0,APP:V4.2.3,BUILD:2019-03-30,08:05,PLT:2503AVE,HW:V2.1,MODEL:,MODEM:B9G6MAR02A07M1G#");
+
+ verifyPosition(decoder, binary(
+ "2525020044a66d0862522030401350001403841409c40064edc000051100960000071701370000003ea7ee0019032010581300000000aad3e1bda6f24d42000000001281"));
+
+ verifyPosition(decoder, binary(
+ "252502004400010880616898888888000A00FF2001000020409600989910101010055501550000101005050005051010050558866B4276D6E342912AB441111500051010"));
+
+ verifyNull(decoder, binary(
+ "232301001500000880316890202968140197625020"));
+
+ verifyNull(decoder, binary(
+ "232303000f00000880316890202968"));
+
+ verifyAttributes(decoder, binary(
+ "232302004200000880316890202968001e02582d00000000000000050000320000018901920000001dc1e2001601081154255d0202005a0053875a00a57e5a00af80"));
+
+ verifyNull(decoder, binary(
+ "232301001500020357367031063979150208625010"));
+
+ verifyNull(decoder, binary(
+ "232303000f00000357367031063979"));
+
+ verifyPosition(decoder, binary(
+ "232304004200030357367031063979003c03842307d00000c80000050100008000008900890100000017b100151022121648b8ef0c4422969342cec5944100000110"));
+
+ verifyPosition(decoder, binary(
+ "232302004200150357367031063979003c03842307d000004a0000050100004001009500940000000285ab001510281350477f710d4452819342d1ba944101160038"));
+
+ verifyAttributes(decoder, binary(
+ "232302004200000357367031063979003c03842307d000008000000501000000010094009400000002a0b90015102814590694015a00620cf698620cf49e620cf498"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
new file mode 100644
index 000000000..9a628cdb6
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class T800xProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ T800xProtocolEncoder encoder = new T800xProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "RELAY,0000,On#");
+
+ verifyFrame(
+ binary("232381001e000101234567890123450152454c41592c303030302c4f6e23"),
+ encoder.encodeCommand(null, command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java
new file mode 100644
index 000000000..0b9eacb8d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java
@@ -0,0 +1,89 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TaipProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TaipProtocolDecoder decoder = new TaipProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ ">RLN25601000+297185103-0955755990+000059150000+0000000012000000000000000000000000000000000000000000000000000000000012;ID=3580;*48<"));
+
+ verifyPosition(decoder, text(
+ ">RGP211217112154-2748332-058946350000000FF7F2100;ID=AA01;#0002;*2D<"));
+
+ verifyPosition(decoder, text(
+ ">RCV12270218010247-3471349-058400030002057F001200020A1D013010600001509+0000FF+0000FF;#1DE2;ID=7196;*03<"));
+
+ verifyPosition(decoder, text(
+ ">RPV03874+3477708-0923453100029212;ID=0017;*71<"));
+
+ verifyNull(decoder, text(
+ ">RAL03874+00185+00012;ID=0017;*4A<"));
+
+ verifyNull(decoder, text(
+ ">RCP03874+347771-092345312;ID=0017;*65<"));
+
+ verifyNull(decoder, text(
+ ">RLN03874000+347770828-0923453071+000608270000+0000292309000000000000000000000000000000000000000000000012;ID=0017;*49<"));
+
+ verifyPosition(decoder, text(
+ ">RPV46640+4197412-0752857900015802;ID=5102;*71<"));
+
+ verifyNull(decoder, text(
+ ">RCP46640+419741-075285802;ID=5102;*6C<"));
+
+ verifyPosition(decoder, text(
+ ">REV001958003965+0307178+1016144900031532;IO=300;SV=8;BL=4159;CF=8161,C,13;AD=14145;IX=10233040;FF=0,0,0,0;VO=338578;ID=357042063052352<"));
+
+ verifyPosition(decoder, text(
+ ">REV011958000369+0307185+1016144400000032;IO=200;SV=9;BL=4158;CF=0,0,0;AD=12347;IX=10213040;FF=0,0,0,0;VO=338572;ID=357042063052352<"));
+
+ verifyPosition(decoder, text(
+ ">REV421942237017+1170957-0701880200000032;ID=356612022463055<"));
+
+ verifyPosition(decoder, text(
+ ">RGP200317010815-3852.9306-06204.88560000003000101;&01;ID=5555;#7AD7*51<"));
+
+ verifyPosition(decoder, text(
+ ">RCQ09000000000000-3460365-058381460000007F0000000000000115000FFFF1099;#0000;ID=555224;*05<"));
+
+ verifyPosition(decoder, text(
+ ">RBR00130217040848-3462200-05846708000175FF0022900003B3C13010800001118410+24061A;ID=555224;*07<"));
+
+ verifyPosition(decoder, text(
+ ">REV451891352379+0307152+1016143700000012;SV=8;BL=4416;VO=8055;ID=356612026322000<"));
+
+ verifyPosition(decoder, text(
+ ">RGP230615010248-2682523-065236820000003007F4101;ID=0005;#0002;*2A<"),
+ position("2015-06-23 01:02:48.000", true, -26.82523, -65.23682));
+
+ verifyPosition(decoder, text(
+ ">RGP190805211932-3457215-058493640000000FFBF0300;ID=8251;#2122;*54<"));
+
+ verifyPosition(decoder, text(
+ ">RPV00000+3739438-1220384601512612;ID=1234;*7F"));
+
+ verifyPosition(decoder, text(
+ "\r\n>REV691615354941+3570173+1397742703203212;ID=Test"));
+
+ verifyPosition(decoder, text(
+ ">REV481599462982+2578391-0802945201228512;ID=Test"),
+ position("2010-09-02 17:29:42.000", true, 25.78391, -80.29452));
+
+ verifyPosition(decoder, text(
+ ">REV131756153215+3359479-0075299001031332;VO=10568798;IO=310;SV=10;BL=4190;CV09=0;AD=0;AL=+47;ID=356612021059680"));
+
+ verifyPosition(decoder, text(
+ ">RPV02138+4555512-0735478000000032;ID=1005;*76<"));
+
+ verifyPosition(decoder, text(
+ ">RPV19105+4538405-0739518900000012;ID=9999;*7A<\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java
new file mode 100644
index 000000000..767f175fe
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TechTltProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TechTltProtocolDecoder decoder = new TechTltProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "002422269*POS=Y,16:21:20,25/11/09,3809.8063N,01444.7438E,4.17,117.23,0.4,09,40076,56341\r\n"),
+ position("2009-11-25 16:21:20.000", true, 38.16344, 14.74573));
+
+ verifyAttributes(decoder, text(
+ "002422269,INFOGPRS,V Bat=13.8,TEMP=23,I TIM,15\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java
new file mode 100644
index 000000000..0446670d8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TekFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TekFrameDecoder decoder = new TekFrameDecoder();
+
+ verifyFrame(
+ binary("020315048715E70861074028023219026200400A0340002C007F0009000000000000000000402842064028420641284206402844064128440640284406402844064028440641284406402844060010010C04052B000253000000000001060A0000000000000228330000FF0000FF360014B394"),
+ decoder.decode(null, null, binary("020315048715E70861074028023219026200400A0340002C007F0009000000000000000000402842064028420641284206402844064128440640284406402844064028440641284406402844060010010C04052B000253000000000001060A0000000000000228330000FF0000FF360014B394")));
+
+ verifyFrame(
+ binary("0501C2828E14750861075021004551047B00019700000082010F0A5B28770A5B28770A5B28760A5B28770A5B28770A5B28770A5B28770A5B28760A5B28760A5B28760A5B28770A5B28760A5B28760A5B28760A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5F2877000000000000000000000000EEBA"),
+ decoder.decode(null, null, binary("0501C2828E14750861075021004551047B00019700000082010F0A5B28770A5B28770A5B28760A5B28770A5B28770A5B28770A5B28770A5B28760A5B28760A5B28760A5B28770A5B28760A5B28760A5B28760A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5F2877000000000000000000000000EEBA")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java
new file mode 100644
index 000000000..f67ae9c3f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TekProtocolDecoder decoder = new TekProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "0501E304E00E76086107502100455111492C33332C3137303935342E302C353235352E393933344E2C30303833322E34333935572C322E312C3133342E382C322C302E30302C302E302C302E302C3234303931352C30362C3C45"));
+
+ verifyAttributes(decoder, binary(
+ "0501C2828E14750861075021004551047B00019700000082010F0A5B28770A5B28770A5B28760A5B28770A5B28770A5B28770A5B28770A5B28760A5B28760A5B28760A5B28770A5B28760A5B28760A5B28760A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5D28770A5F2877000000000000000000000000EEBA"));
+
+ verifyAttributes(decoder, binary(
+ "0509220886157E0863835020373564087B00018C0000018003160A6E28790A6E28790A6E287A0A6E287A0A6E287A0A6E287A0A6E287A0A6E287A0A6E287A0A6E287A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BD35"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java
new file mode 100644
index 000000000..e330600e5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelemaxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelemaxProtocolDecoder decoder = new TelemaxProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "%067374070128"));
+
+ verifyPositions(decoder, text(
+ "Y000007C6999999067374074649003C00A7018074666F60D66818051304321900000000C5"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java
new file mode 100644
index 000000000..5fcbcbb57
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelicFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelicFrameDecoder decoder = new TelicFrameDecoder();
+
+ verifyFrame(
+ binary("303032363230333339337c3232367c31307c303032303034303130"),
+ decoder.decode(null, null, binary("303032363230333339337c3232367c31307c30303230303430313000")));
+
+ verifyFrame(
+ binary("3030333032303333393332352c3139303331373038333035322c302c3138303331373130333132372c3235393932342c3434353133332c332c302c302c392c2c2c39332c31323231303134312c2c303031302c30302c34302c3234302c302c30343036"),
+ decoder.decode(null, null, binary("630000003030333032303333393332352c3139303331373038333035322c302c3138303331373130333132372c3235393932342c3434353133332c332c302c302c392c2c2c39332c31323231303134312c2c303031302c30302c34302c3234302c302c3034303600")));
+
+ verifyFrame(
+ binary("303032363239363231385343434530315f534343457c3232367c31307c30323637"),
+ decoder.decode(null, null, binary("303032363239363231385343434530315f534343457c3232367c31307c3032363700")));
+
+ verifyFrame(
+ binary("30303434323936323138544c4f43303236372c30302c3031313030393030303239363231382c3139303331373038333033362c3235353137382c3434353037322c332c302c38322c2c2c2c3136382c31343734313239362c2c30302c30302c302c323137"),
+ decoder.decode(null, null, binary("6400000030303434323936323138544c4f43303236372c30302c3031313030393030303239363231382c3139303331373038333033362c3235353137382c3434353037322c332c302c38322c2c2c2c3136382c31343734313239362c2c30302c30302c302c32313700")));
+
+ verifyNull(
+ decoder.decode(null, null, binary("00303032363937393238317c3233327c30337c30303230303430313000")));
+
+ verifyFrame(
+ binary("303032363937393238317c3233327c30337c303032303034303130"),
+ decoder.decode(null, null, binary("303032363937393238317c3233327c30337c30303230303430313000")));
+
+ verifyFrame(
+ binary("3030323039373932383139392c3231303231363038313930302c302c3231303231363038313835392c3031333839333338352c34363635383639352c332c302c302c382c2c2c3534312c36313239382c2c303030302c30302c302c3139362c302c30343037"),
+ decoder.decode(null, null, binary("650000003030323039373932383139392c3231303231363038313930302c302c3231303231363038313835392c3031333839333338352c34363635383639352c332c302c302c382c2c2c3534312c36313239382c2c303030302c30302c302c3139362c302c3034303700")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
new file mode 100644
index 000000000..b743cef96
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
@@ -0,0 +1,94 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelicProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelicProtocolDecoder decoder = new TelicProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "0026355565071347499|206|01|001002008"));
+
+ verifyPosition(decoder, text(
+ "052028495198,160917073641,0,160917073642,43879,511958,3,24,223,17,,,-3,142379,,0010,00,64,205,0,0499"));
+
+ verifyPosition(decoder, text(
+ "01302849516,160917073503,0,160917073504,43907,512006,3,11,160,14,,,-7,141811,,0010,00,64,206,0,0499"));
+
+ verifyPosition(decoder, text(
+ "002135556507134749999,010817171138,0,010817171138,004560973,50667173,3,0,0,11,1,1,100,958071,20601,000000,00,4142,0000,0000,0208,10395,0"));
+
+ verifyPosition(decoder, text(
+ "442045993198,290317131935,0,290317131935,269158,465748,3,26,183,,,,184,85316567,226,01,00,68,218"));
+
+ verifyPosition(decoder, text(
+ "673091036017,290317131801,0,290317131801,262214,450536,3,40,199,8,,,154,19969553,,0011,00,59,240,0,0406"));
+
+ verifyPosition(decoder, text(
+ "092020621198,280317084155,0,280317084156,259762,444356,3,42,278,9,,,89,56793311,,0110,00,67,0,,0400"));
+
+ verifyPosition(decoder, text(
+ "502091227598,280317084149,0,280317084149,261756,444358,3,33,286,9,,,77,3143031,,0010,00,171,240,0,0406"));
+
+ verifyPosition(decoder, text(
+ "232027997498,230317083900,0,230317083900,260105,444112,3,22,259,,,,111,61110817,226,01,00,255,218,00000000000000"));
+
+ verifyPosition(decoder, text(
+ "072027997498,230317082635,0,230317082635,260332,444265,3,28,165,,,,124,61107582,226,01,00,255,219,00000000000000"));
+
+ verifyNull(decoder, text(
+ "0026203393|226|10|002004010"));
+
+ verifyPosition(decoder, text(
+ "003020339325,190317083052,0,180317103127,259924,445133,3,0,0,9,,,93,12210141,,0010,00,40,240,0,0406"));
+
+ verifyNull(decoder, text(
+ "0026296218SCCE01_SCCE|226|10|0267"));
+
+ verifyNull(decoder, text(
+ "1242022592TTUV0100,0201,351266000022592,170403114305,0115859,480323,3,30,5,9,3,4,650,250000000,26202,1001,0001,211,233,111,0"));
+
+ verifyPosition(decoder, text(
+ "123002259213,170403114305,1234,170403114305,0115859,480323,3,30,5,9,3,4,650,250000000,26202,1001,0001,211,233,111,0,600"));
+
+ verifyNull(decoder, text(
+ "0044296218TLOC0267,00,011009000296218,190317083036,255178,445072,3,0,82,,,,168,14741296,,00,00,0,217"));
+
+ verifyPosition(decoder, text(
+ "003097061325,220616044200,0,220616044200,247169,593911,3,48,248,8,,,50,1024846,,1111,00,48,0,51,0406"));
+
+ verifyPosition(decoder, text(
+ "003097061325,210216112630,0,210216001405,246985,594078,3,0,283,12,,,23,4418669,,0010,00,117,0,0,0108"));
+
+ verifyPosition(decoder, text(
+ "592078222222,010100030200,0,240516133500,222222,222222,3,0,0,5,,,37,324,,1010,00,48,0,0,0406"));
+
+ verifyPosition(decoder, text(
+ "002017297899,220216111100,0,220216111059,014306446,46626713,3,7,137,7,,,448,266643,,0000,00,0,206,0,0407"));
+
+ verifyPosition(decoder, text(
+ "003097061325,210216112630,0,210216001405,246985,594078,3,0,283,12,,,23,4418669,,0010,00,117,0,0,0108"));
+
+ verifyNull(decoder, text(
+ "0026970613|248|01|004006011"));
+
+ verifyPosition(decoder, text(
+ "032097061399,210216112800,0,210216112759,246912,594076,3,47,291,10,,,46,4419290,,0010,00,100,0,0,0108"));
+
+ verifyPosition(decoder, text(
+ "002017297899,190216202500,0,190216202459,014221890,46492170,3,0,0,6,,,1034,43841,,0000,00,0,209,0,0407"));
+
+ verifyPosition(decoder, text(
+ "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"),
+ position("2013-06-27 04:16:52.000", true, 47.53410, 16.66530));
+
+ verifyPosition(decoder, text(
+ "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java
new file mode 100644
index 000000000..8d2e70bd4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TeltonikaFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TeltonikaFrameDecoder decoder = new TeltonikaFrameDecoder();
+
+ verifyFrame(
+ binary("000F313233343536373839303132333435"),
+ decoder.decode(null, null, binary("FF000F313233343536373839303132333435")));
+
+ verifyFrame(
+ binary("000F313233343536373839303132333435"),
+ decoder.decode(null, null, binary("000F313233343536373839303132333435")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
new file mode 100644
index 000000000..827e12a96
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -0,0 +1,140 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class TeltonikaProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, false);
+
+ verifyNull(decoder, binary(
+ "000F313233343536373839303132333435"));
+
+ verifyPositions(decoder, false, binary(
+ "000000000000004f0c01060000004755555555777730362e343b30342e323b30302e303b30302e303b30302e303b30302e303b30302e303b30302e303b30312e333b30302e303b31302e373b30302e303b5353530d0a010000e371"));
+
+ verifyPositions(decoder, false, binary(
+ "00000000000000100C010600000008010300010015D5C5010000D988"));
+
+ verifyPositions(decoder, binary(
+ "000000000000004c08010000016818d500580009c28d9f1cb3757a00be00c60f0053000f06f0011503c80001011d00fc0007423799180053cdf80dce426f430f88190bb8560bb802f100005aa110002887e000010000ee8d"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000818e0100000166e368a510000f0d8b5b20961c35008d010308000000000014000900ef0000f00100500100150400c800004501001e00002500002900000a00b5000800b60007004230dc0018000000430fcb0044005f001103de001200e50013001200240000000000000001010000113141314a433534343452373235323336370100005e99"));
+
+ verifyPositions(decoder, binary(
+ "000000000000009D10020000013feb55ff74000f0ea850209a690000AE00B90B00000000070A050001000002000003000004000120000200180000004601290200C700000000004C0000000001003E00000000000000000000015B198C7498000F0DBC502095872F00AE00B90B00000000070A050001000002000003000004000120000200180000004601290200C700000000004C0000000001003E000000000000000002000009A5"));
+
+ verifyPositions(decoder, binary(
+ "000000000000009F100100000164D855401800D5E3B744EC11C762023B011A060000000007200A010000010500010600010D00010E00010F00011600011700011800011F001301010000010700000108000001090000010A0000010B0000010C000001100000011100000112000001130000011400000115000001190000011A0000011B0000011C0000011D0000011E000003010200000000010300000000010400000000000100000D3B"));
+
+ verifyPositions(decoder, binary(
+ "000000000000008c08010000013feb55ff74000f0ea850209a690000940000120000001e09FD01FE210300040016014703f0001504c8000c0900730a00460b00501300464306d7440000b5000bb60007422e9f180000cd0386ce000107c700000000f10000601a46000001344800000bb84900000bb84a00000bb84c00000000024e0000000000000000cf00000000000000000100003fca"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000A708010000016269E7D9A8000A5A0F0A1CBF8F3300880046120000001C0801014F005100550F740073007801790103430000440000426F980B540000000056000045275700000047580000022659000000005D0000000068000003D07100007355870000000288000000008A000045270669584C5241534834336A30304731363538326B3600FFFFFF0000008155412055414430308230303039383236368330303000000000000100008396"));
+
+ verifyPositions(decoder, binary(
+ "0000000000000035080100000161f37c50500020de5ba60ef11450000000000000000006040100b300b400ef000109002000014e0000000000000000010000be52"));
+
+ verifyPositions(decoder, binary(
+ "000000000000008c08010000013feb55ff74000f0ea850209a690000940000120000001e09010002000300040016014703f0001504c8000c0900730a00460b00501300464306d7440000b5000bb60007422e9f180000cd0386ce000107c700000000f10000601a46000001344800000bb84900000bb84a00000bb84c00000000024e0000000000000000cf00000000000000000100003fca"));
+
+ verifyPositions(decoder, binary(
+ "0000000000000401080e0000015d12cc211000fadaf627186742f5000d0048080006000a040100f001500515000342318a430fe344000003c700000000f1000068b61000001b05000000015d12c6683800fadaf72118673f82000000000000000007030100f00050040342318a430fe344000001f1000068b6000000015d12bd407800fadaf72118673f82000000000000000007030100f000500403423179430fe144000001f1000068b6000000015d12b414d000fadaf72118673f82000400900c0000fa120a0100f00050051502080007010552090e6f4bfa000542316a430fe14400000600006202b203c700002328f1000068b61000001b05000000015d12b3074800fadaf2821867436a000400890d00170011090100f00150011502081007010553090e6f4d054231fb430fe14400000603ae6202a003c700002328f1000068b61000001b05000000015d12b2ff7800fadaee89186747c60005009a0d001d0011090100f00150011502081b07010553090e6f4d05423125430fe144000006050862029e03c700002328f1000068b61000001b05000000015d12b2e42000fadae8cf18675e0a000300a60d00210011090101f00150011502082407030554090e6f4d0542310a430fe14400000606cf62029703c700002328f1000068b61000001b05000000015d12b2d48000fadae05818676a16000400930c00240011090100f00150011502082207010554090e6f4e05423738430fe144000006066a62029303c700002328f1000068b61000001b05000000015d12b2a1b800fadac33e18678e48000600940d00150011090101f00150051500081907030553090e6f4e054239cc430fe14400000607c662028603c700002328f1000000001000001b05000000015d12b29dd000fadac19d18678fc8000700820d00110011090100f00150051500081607030553090e6f4e054238c8430fe14400000606d962028503c700002328f1000000001000001b05000000015d12b299e800fadabfa9186790e3000700670d00110011090101f00150051500081407030553090e6f4e054231e5430fe144000006060a62028403c700002328f1000000001000001b05000000015d12b2960000fadabd4018679104000600510d00120011090101f00150051500081207030553090e6f4e054231ce430fe144000006057062028303c700002328f1000000001000001b05000000015d12b27aa800fadaa96518678b7c000600470d00120011090101f00150051500081807030551090e6f4e05423a70430fe144000006074462027c03c700002328f1000000001000001b05000000015d12b276c000fadaa73f18678ae60006003b0d000e0011090101f00150051500081607030551090e6f4e05423a5a430fe14400000606b762027b03c700002328f1000000001000001b05000e000007a4"));
+
+ verifyPositions(decoder, binary(
+ "00000000000002cb08080000015a71ccbec00002fc9bfc1e53a1e00016004cf80005001914150216056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d20003480100c6000ac500ce02c80000654ec700004ee8000000015a725aaac80002fc933c1e539d4000150049f80000001914150116056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f4c6000ac500ce02c80000654ec700004ee8000000015a75a42c900002fc97d01e539640001d0008020000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f8c6000ac500ce02c80006ba5ac700004ee8000000015a75a440180002fc931c1e539b60001d00b9020001001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800fac6000ac500ce02c80006ba5ac700004ee8000000015a75a453a00002fc93601e539cc0001d015d0c0000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f9c6000ac500ce02c80006ba5ac700004ee8000000015a75a467280002fc93801e539cc0001d013c0c0000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f9c6000ac500ce02c80006ba5ac700004ee8000000015a75a47ab00002fc92cc1e539c80001d00b00c0000001914150016056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f8c60004c5000a02c800003085c70006ba5a000000015a75a48e380002fc92ec1e539c40001d00410c0000001914150116056500ee00ef00f0009d029e029f02a002a102a202a302a402a502a602020003000164d200034800f8c6000ac500ce02c80000c83dc700004ee800080000e0b2"));
+
+ verifyPositions(decoder, binary(
+ "0000000000000000080100000113fc208dff00209cca800f14f650006f00d60400040004030101150316030001460000015d000100000000")); // invalid length and checksum
+
+ verifyPositions(decoder, binary(
+ "000000000000009f080400000159738f76b8012e13b796110ab27600d700000b00004e01000000014e000000000000000000000159738f6ee8012e13b796110ab27600d700000a00004e01000000014e01000b00791c179300000159738f6b00012e13b796110ab27600d700000a00004e01000000014e000000000000000000000159738f5f48012e13b796110ab27600d700000b00004e01000000014e01000b00791c17930400009671"));
+
+ verifyPositions(decoder, false, binary(
+ "00000000000000710c0106000000694154244d5347534e443d342c225354474234302c50522c3335363630313036303236353035302c313630343232313531372c313630343232313531382c432c2b3032332e332c302c2b3032332e312c302c4445414354492c302c4445414354492c302c312c30220d0a010000d8db"));
+
+ verifyPositions(decoder, false, binary(
+ "0000000000000055070450aa14320201f00150aa17f3031f42332a4c4193d68c008d00020901f00150aa1b6a031f423383f54193624f009d00000a01f00150aa1c230fc01a0000552b040164f400dd00f0010143100c0105000000050400006846"));
+
+ verifyPositions(decoder, binary(
+ "000000000000003508010000014f8e016420002141bbaf0f4e96a7fffa0000120000000602010047030242669c92000002c7000000009100000000000100002df3"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000A7080400000113fc208dff000f14f650209cca80006f00d60400040004030101150316030001460000015d0000000113fc17610b000f14ffe0209cc580006e00c00500010004030101150316010001460000015e0000000113fc284945000f150f00209cd200009501080400000004030101150016030001460000015d0000000113fc267c5b000f150a50209cccc0009300680400000004030101150016030001460000015b00040000"));
+
+ verifyPositions(decoder, binary(
+ "000000000000014708060000013e5a60a4cb003fa7b780fc424518004200000a000000090501010200b300b400f000034268a746011818000001c700000000000000013e5dc8ba28003fa7c080fc4246040001000005000000090501010200b300b400f001034268b44600ef18000001c700000000000000013e5dc90455003fa7b640fc424388003a0000070000f0090501010200b300b400f000034268dc4600f718000001c70000001d000000013e5dc9d368003fa7b800fc4244300049000004000000090501010200b300b400f001034267de46010718000001c700000000000000013e5dca311d003fa7b680fc4243cc00420000070000f0090501010200b300b400f0000342685346010b18000001c700000000000000013e5dcfafe9003fa7b600fc4242f0003d000008000000090501010200b300b400f0000342685246011918000001c700000000000600000275"));
+
+ verifyPositions(decoder, binary(
+ "000000000000002c08010000013eff8d6f9800173295002111f400008100ae0b0000000401010003090016432980422f7200000100007a5d"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000c7070441bf9db00fff425adbd741ca6e1e009e1205070001030b160000601a02015e02000314006615000a160067010500000ce441bf9d920fff425adbb141ca6fc900a2b218070001030b160000601a02015e02000314006615000a160067010500000cc641bf9d740fff425adbee41ca739200b6c91e070001030b1f0000601a02015f02000314006615000a160066010500000ca841bf9cfc0fff425adba041ca70c100b93813070001030b1f0000601a02015f02000314002315000a160025010500000c3004000000"));
+
+ verifyPositions(decoder, binary(
+ "000000000000003107024c61410b013f4231c2c141d0beb9003d000005006483ff4c6140eb013f4231c2c141d0beb9003d000005006483ff02000041df"));
+
+ verifyPositions(decoder, binary(
+ "000000000000002b080100000140d4e3ec6e000cc661d01674a5e0fffc00000900000004020100f0000242322318000000000100007a04"));
+
+ verifyPositions(decoder, false, binary(
+ "000000000000002d0c01060000002523464d323d3236323033323736313732313339362c32363230332c30372e30322e30350d0a0100009a2e"));
+
+ verifyPositions(decoder, binary(
+ "00000000000000a608010000013f14a1d1ce000f0eb790209a778000ab010c0500000000000000000100003390"));
+
+ verifyPositions(decoder, binary(
+ "000000000000004508010000015eb70a86d00024089d4d21dee3860137005f12005f000e06ef01f00150011503c800450108b5000bb6000642382718005fcd057ace19d3430f57440000000001000002bf"));
+
+ verifyPositions(decoder, binary(
+ "000000000000004a08010000015ebc1da508002411926621f15246010b00b913005e000f06ef01f00150011505c800450108b5000bb6000642381b18005ecd0318ce19cd430f5844000001f1000061a900010000c8e1"));
+
+ decoder.setExtended(true);
+
+ verifyPositions(decoder, false, binary(
+ "0000000000000158080b0000015d4b4dc07a00d5dbd13eec04324e020e0000120000000000000000000000015d4b4cd5e800d5dbd13eec04324e020e0000120000000000000000000000015d4b4beb8800d5dbd13eec04324e020e0000130000000000000000000000015d4b4b012800d5dbd13eec04324e020e0000120000000000000000000000015d4b4a16c800d5dbd13eec04324e020f0000110000000000000000000000015d4b492c6800d5dbd13eec04324e020f0000110000000000000000000000015d4b48420800d5dbd13eec04324e020f0000120000000000000000000000015d4b4757a800d5dbd13eec04324e020f00000f0000000000000000000000015d4b466d4800d5dbd13eec04324e020f0000100000000000000000000000015d4b4582e800d5dbd13eec04324e020f0000110000000000000000000000015d4b44988800d5dbd13eec04324e020f0000110000000000000000000b0000ec10"));
+
+ verifyPositions(decoder, false, binary(
+ "00000000000003b5080b0000015ab5642a8800d5db1769ec01d70a020a00e3040004000e0501010200030004006000060900100a00010b0000130000422f1318000302c700000000f7000000000001cb000000000000000000000000000000000000015ab5642e7000d5db178aec01d6d6020a0070040003000e0501010200030004006000060900100a00000b0000130000422f1318000302c700000000f7000000000001cb000000000000000000000000000000000000015ab567050000d5db1805ec01da3c02060101040006000e05010102000300040060000609000f0a00010b0000130000422f0c18000302c700000046f7000000000001cb000000000000000000000000000000000000015ab56708e800d5db1723ec01d9ec020600e5040006000e05010102000300040060000609000f0a00010b0000130000422f1018000502c700000003f7000000000001cb000000000000000000000000000000000000015ab5685cc000d5db20f7ec01d8fa02080033050007000e05010102000300040060000609000f0a00010b0000130000422f0a18000502c700000030f7000000000001cb000000000000000000000000000000000000015ab5693b6800d5db2367ec01d9430211011b040006000e05010102000300040060000609000f0a00010b0000130000422f0c18000302c700000027f7000000000001cb000000000000000000000000000000000000015ab569433800d5db1fb2ec01d9310211008b040006000e0501010200030004006000060900110a00000b0000130000422f1318000402c700000009f7000000000001cb000000000000000000000000000000000000015ab56a0e5800d5db22a2ec01da5502100041050007000e05010102000300040060000609000f0a00000b0000130000422f1118000602c70000000ef7000000000001cb000000000000000000000000000000000000015ab56a700000d5db2afcec01ddb2020a0012050008000e05010102000300040060000609000e0a00000b0000130000422f0918000502c700000026f7000000000001cb000000000000000000000000000000000000015ab56a73e800d5db2ad8ec01de65020a014e050008000e05010102000300040060000609000f0a00010b0000130000422f0818000702c700000002f7000000000001cb000000000000000000000000000000000000015ab56a7bb800d5db2971ec01e00e020a013f040008000e0501010200030004006000060900100a00020b0000130000422f0b18000802c700000004f7000000000001cb000000000000000000000000000000000b00007c5f"));
+
+ }
+
+
+ @Test
+ public void testDecodePhoto() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, false);
+
+ verifyNull(decoder, binary(
+ "000F313233343536373839303132333435"));
+
+ verifyNull(decoder, binary(
+ "00000000000000090c010D00000001000100000CD8"));
+
+ verifyNull(decoder, binary(
+ "000000000000000D0c010D0000000501598493ED01000018B2"));
+
+ verifyNull(decoder, binary(
+ "00000000000008130c010d0000080b02598493ED000000000800ffd8ffe000104a46494600010201006000600000ffdb0084000202020202020202020202020202020403020202020504040304060506060605060606070908060709070606080b08090a0a0a0a0a06080b0c0b0a0c090a0a0a01020202020202050303050a0706070a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0affc401a20000010501010101010100000000000000000102030405060708090a0b0100030101010101010101010000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffc000110801e002d003012200021101031101ffdd00040054ffda000c03010002110311003f00fc918d555b0c0007a8cd4e3054a8c807a64540a32d900f07e626ac2fce0f4014718ae652ba68e87ef222238da4f20f415226d00823a838045210149cf24fbd30305c31391cf18a52574269ad2c49b9be518c60d4dbb0194003232323b542092509e840c0a9304120e4827068924e5a8db761a0971b402011c1229ea02608f5ebeb4a09fba00001eb520552a001c83d09a1493d1149a48455009dc4020f029f210a98079a52a32a091b8f24d23042c41c93fc38a517a89b6f61b19dc371c923a1a9631f2b8639c1e326a280124ae0804fe55232153819c93924d17698452488c079380792dc1c5491a80e10825327f3a7b60052a36e4f069e9d77b1c053d4d24d3634aeee3769f31890060f03da9f364c6029cee5e09a7125db790013d69a49200000eb4ecbd0a5749d86a10ab1a827716c64d3d57639181ce4e29c880216246e43c12296360e0c8c4865e9c76a7169bb762534da18b1b3fdfc003a0a951004236e5b77ca49a8959c48a013f33723daa74604c84f0a0e011d8d36d27a83936c67987718c819cf20fa54ae018f6648048c0c74a810b1901009603a9ed53005958900943920f7a9d131deeb41854064519c01c934e000914124865e49a7901ca903af63da918a96418c91c311495a49836e438f240ebb4fcbf4a7a16219828000232691f3953c023a01446e49008da83ef0f5a6acb71249a41b4642331e0f5a7b8023193f311c81e94e5da496750016ea476a64b2050495279f9463b51aa2ac86a200c1412081c9a9810d2384206178c1a8d010a1ca92d8fc853e37551900167079a4db6ef70bc799362f963682c3073dfbd2e49508848e7e6a79264894e3033900543138569093b8b82001d8d5dd2d985d262801880bcaaf1c1ef52b92c91c600c27041f4a6292acaa9c0c724fad293f3161938eb93509b6f713695d844586e00e060e38a72260b9e8aab9247a546a30c40242b8e07a54b9fbca092ae3814ee9ec4ab45a60c140c81b8b918149b0b2ba3120eee707a5354b052554e54f04f6a7a9561b413bcb0249345d25a17a5ee3c81e5a804860723029502bec2fdb8201ea69db39c90403f74fb5359580057ef6780294527aafe98ad1487ae017278dab900d47390c8072188f978ef52e0b48c41fbc3048a6381bd41390a791449a6c4af263f0ab124472485cb0f53532c8137a6727193c77a81582caace7200e38a7be7cc9b6a819192cc78a77567b84795a240ac0463000232e734b127254fde4078a7021d22232d82371f4a69251caa900b3e4367b526928bbf512516eec91103204271d720fd6a605427cbc04e066aaa615d482483d81ef53306024503049c939e94dd96a55d5c6c6518b316e431c71d6a78c99033138c1f947ad5451f3aae7193f301eb5ad02a44c430071cb023a524d3d413e87b0e829245f0f3c73242e229e4f095e209ca062a1e278fa3023a39edeb5f13ce144ae01dc3770718afb5bc3e88fe0bf1c1b8252de4f09de9322a92630b113b801d4fe07815f155cb0695982ed19e00aceed55934fa47ff6ef989abc48c0cf23800f5cd3d4f24e38238a8949c00304e79a901e7a678a1b5ad898b492255c024824e48e2a55272307041a8548c01d077352a9c923ae2a559bd4a6d264c0e060f04f7a9800001ebd6a01b7007383d2a75008039a4d3be80f995c82f0916f290707674f5aa5e1c19b9949e3e4e3f3ab97b9fb34a31c05e9557c3a019a50724003b576615e8ccab5940ee62c123af5e302aeaf0463a8355225276e3a0ee2ad82474e83b115d8d7bda9cea4921aec76b678c83d6a9d9f319c1c658f22adc9cab6464e0e00aab60018723b93835c59838ac3dcebc124f106cdaa8dcbcf39afa13e0fc65b5e848c83b3e519ef91fe35f3f5a801973d8f15f48fc148fcdf10da2f003ca8a491dcbafaf7e2be2f3549e1a4bb9f474928c6ecfdd5d1a309a7d928fe1b68c0c7fbb5d1c28db01c77ac7d3d156de054185f2d76fd315d25a85d801e7938cd7d2609bf6d7f53e66ac7f77a010000b7cd"));
+
+ }
+
+ @Ignore
+ @Test
+ public void testDecodeConnectionless() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, true);
+
+ verifyPositions(decoder, false, binary(
+ "0049cafe0122000f33353734353430373237313339373508010000015d3766f6a800003eef961ec6215e0063006d09003100070401000200f001c8000242381c18003201c7000000e10001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java
new file mode 100644
index 000000000..0318b9896
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class TeltonikaProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ TeltonikaProtocolEncoder encoder = new TeltonikaProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+
+ command.set(Command.KEY_DATA, "setdigout 11");
+ verifyCommand(encoder, command, binary("00000000000000160C01050000000E7365746469676F75742031310D0A010000E258"));
+
+ command.set(Command.KEY_DATA, "03030000000185E8");
+ verifyCommand(encoder, command, binary("00000000000000100c01050000000803030000000185e8010000da8b"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java
new file mode 100644
index 000000000..a30d17502
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ThinkRaceProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ThinkRaceProtocolDecoder decoder = new ThinkRaceProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "48415349483031343730303134382C8000100134363030303134363139363239343806FF"));
+
+ verifyPosition(decoder, binary(
+ "48415349483031343730303134382C90001755701674D70155466406CBB813003D24A410F5000000770B4C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java
new file mode 100644
index 000000000..f1ba7c593
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tk102ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tk102ProtocolDecoder decoder = new Tk102ProtocolDecoder(null);
+
+ verifyNull(decoder, buffer(
+ "[\u00800000000000\u000821315452]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00f00000000000\u000821315452]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u00900100100001\u0036(ONE025857A2232.0729N11356.0030E000.02109110100000000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u00900100100001\u0036(ITV025857A2232.0729N11356.0030E000.02109110100000000)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00210000000081\u0072(353327023367238,TK102-W998_01_V1.1.001_130219,255,001,255,001,0,100,100,0,internet,0000,0000,0,0,255,0,4,1,11,00)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u004c0000001323\u004e(GSM,0,0,07410001,20120101162600,404,010,9261,130,0,2353,130,35,9263,130,33,1)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00250000000082\u001d(100100000000000600-30-65535)]"));
+
+ verifyNull(decoder, buffer(
+ "[\u00230000000004\u0018(062100000000000600-0-0)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u003d0000000083\u0036(ITV013939A4913.8317N02824.9241E000.90018031310010000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u003d0000000036\u0036(ITV012209A4913.8281N02824.9258E000.32018031310010000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u003b0000000010\u0036(ONE200834A5952.8114N01046.0832E003.93212071305010000)]"));
+
+ verifyPosition(decoder, buffer(
+ "[\u00930000000000\u0046(ITV153047A1534.0805N03233.0888E000.00029041500000400&Wsz-wl001&B0000)]"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java
new file mode 100644
index 000000000..1f42e588f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java
@@ -0,0 +1,44 @@
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tk103FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tk103FrameDecoder decoder = new Tk103FrameDecoder();
+
+ verifyFrame(
+ binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429"),
+ decoder.decode(null, null, binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ ByteBuf buf = binary("283836343535353535353535353535352C445735422C3231302C362C353939352C34373730312C352C33303A45453A43433A45373A38363A44442A2D35392A31312C34433A36303A43433A45413A42423A45452A2D36382A312C34323A41413A44453A45413A42423A30302A2D36392A312C33323A43443A42423A43333A34463A43432A2D38362A332C31303A30303A34333A42413A32323A31352A2D38382A312C3135313131372C31363337323229283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529");
+
+ verifyFrame(
+ binary("283836343535353535353535353535352C445735422C3231302C362C353939352C34373730312C352C33303A45453A43433A45373A38363A44442A2D35392A31312C34433A36303A43433A45413A42423A45452A2D36382A312C34323A41413A44453A45413A42423A30302A2D36392A312C33323A43443A42423A43333A34463A43432A2D38362A332C31303A30303A34333A42413A32323A31352A2D38382A312C3135313131372C31363337323229"),
+ decoder.decode(null, null, buf));
+
+ verifyFrame(
+ binary("283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529"),
+ decoder.decode(null, null, buf));
+
+ verifyFrame(
+ binary("283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429"),
+ decoder.decode(null, null, binary("676172626167652540232A5E242D2B3C3E3F2429292924242D2D283836343735353535353535353535352C445733422C3133313131372C412C353536322E30323837304E2C30313334382E3038313934452C312E3539372C3232333730372C3239312E36352C2D302E31302C3429283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ verifyNull(decoder.decode(null, null, binary("67")));
+
+ verifyNull(decoder.decode(null, null, binary("676172626167652540232a5e242d2b3c3e3f24")));
+
+ verifyFrame(
+ binary("2838363437353535353535352C5A4330332C3139313131372C3233343432312C24294E6F746963653A0D0A446576696365732073657269616C206E756D6265723A200D0A3538303535353535353535292E0D0A536F6674776172652076657273696F6E3A0D0A56322E3030302C323031362F30382F32332031313A3137292429"),
+ decoder.decode(null, null, binary("610D0A676172626167652540232A5E242D2B3C3E3F2429292924242D2D2838363437353535353535352C5A4330332C3139313131372C3233343432312C24294E6F746963653A0D0A446576696365732073657269616C206E756D6265723A200D0A3538303535353535353535292E0D0A536F6674776172652076657273696F6E3A0D0A56322E3030302C323031362F30382F32332031313A3137292429283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ verifyNull(decoder.decode(null, null, binary("610D0A676172626167652540232A5E242D2B3C3E3F2429292924242D2D2838363437353535353535352C5A4330332C3139313131372C3233343432312C24294E6F746963653A0D0A446576696365732073657269616C206E756D6265723A200D0A3538303535353535353535292E0D0A536F6674776172652076657273696F6E3A0D0A56322E3030302C323031362F30382F32332031313A31372929283836343735353535353535353535352C5A4332302C3133313131372C3232333730362C362C3339342C36353533352C32353529")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java
new file mode 100644
index 000000000..4e654239d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java
@@ -0,0 +1,205 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Tk103ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tk103ProtocolDecoder decoder = new Tk103ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(BALLESTEROS3BR00190408A4113.5700N00140.3100E000.0162431000.0001000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(094625928000BR00190213A1156.0431S07705.6145W000.000023521.40000000007L00000314T113)"));
+
+ verifyPosition(decoder, text(
+ "(019358704260BR00180725A2300.0957N07235.2748E032.412092187.58001100166L000D9779)"));
+
+ verifyPosition(decoder, text(
+ "(358511020000026,DW5B,310,6,29876,30393,0,041217,102211)"));
+
+ verifyPosition(decoder, text(
+ "(007611121184BR00170816A2401.5217N07447.0788E000.0221352232.340000004FL0030F14F)"));
+
+ verifyNull(decoder, text(
+ "(027044702512BP00027044702512HSO01A4)"));
+
+ verifyPosition(decoder, text(
+ "(864768011069660,ZC11,250517,V,0000.0000N,00000.0000E,000.0,114725,000.0,0.00,11)"));
+
+ verifyPosition(decoder, text(
+ "(864768011069660,ZC17,250517,A,3211.7118N,03452.8086E,0.68,115525,208.19,64.50,9)"));
+
+ verifyPosition(decoder, text(
+ "(357593060760397BP02,G,2,170304A6015.7466N01101.8460E001.609445591.048,7)"));
+
+ verifyPosition(decoder, text(
+ "(325031693849BR00170228A5750.8012N02700.7476E000.2154529000.0000000200L00000000,170228,194530)"));
+
+ verifyAttribute(decoder, text(
+ "(087073803649BR00170221A6142.0334N02712.2197E000.3203149000.00,00000000L00000000)"),
+ Position.KEY_FUEL_LEVEL, 0);
+
+ verifyPosition(decoder, text(
+ "(864768010869060,DW30,050117,A,5135.82713N,00001.17918E,0.089,154745,000.0,43.40,12)"));
+
+ verifyNotNull(decoder, text(
+ "(087073104337BZ00,740,000,3bf7,0425,3bf7,0bf5,3bf7,09e7,3bf7,cbad,3bf7,0dcf,3bf7,c7b2,01000000)"));
+
+ verifyNull(decoder, text(
+ "(087073005534BP00HSO)"));
+
+ verifyNull(decoder, text(
+ "(027028258309BQ86,0,05550c21b10d1d0f431008bd114c0ea5078400010007a100423932,161117005322,01000001)"));
+
+ verifyNull(decoder, text(
+ "(027028258309BQ86,0,05470c0eb20d040f4410022911360e92077e00010007a1004237c7,161117005232,01000001)"));
+
+ verifyPosition(decoder, text(
+ "(01602009983BR00160830V1855.7022S4817.8731W000.0002729000.0010000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(088046338039BR00160727A3354.7768N03540.7258E000.0140832068.4700000000L00BEB0D4+017.7)"));
+
+ verifyPosition(decoder, text(
+ "(088046338039BP05000088046338039160727A3354.7768N03540.7258E000.0140309065.1000000000L00BEB0D4+017.3)"));
+
+ verifyAttributes(decoder, text(
+ "(013632651491,ZC20,180716,144222,6,392,65535,255)"));
+
+ verifyAttributes(decoder, text(
+ "(087072009461BR00000007V0000.0000N00000.0000E000.00014039900000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(013612345678BO012061830A2934.0133N10627.2544E040.0080331309.6200000000L000770AD)"));
+
+ verifyNotNull(decoder, text(
+ "(088047194605BZ00,510,010,36e6,932c,43,36e6,766b,36,36e6,7668,32)"));
+
+ verifyAttributes(decoder, text(
+ "(013632651491,ZC20,040613,040137,6,421,112,0)"));
+
+ verifyAttributes(decoder, text(
+ "(864768010159785,ZC20,291015,030413,3,362,65535,255)"));
+
+ verifyPosition(decoder, text(
+ "(088047365460BR00151024A2555.3531S02855.3329E004.7055148276.1701000000L00009AA3)"),
+ position("2015-10-24 05:51:48.000", true, -25.92255, 28.92222));
+
+ verifyPosition(decoder, text(
+ "(088047365460BP05354188047365460150929A3258.1754S02755.4323E009.4193927301.9000000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(088048003342BP05354188048003342150917A1352.9801N10030.9050E000.0103115265.5600010000L000003F9)"));
+
+ verifyPosition(decoder, text(
+ "(088048003342BR00150917A1352.9801N10030.9050E000.0103224000.0000010000L000003F9)"));
+
+ verifyPosition(decoder, text(
+ "(088048003342BR00150807A1352.9871N10030.9084E000.0110718000.0001010000L00000000)"));
+
+ verifyNull(decoder, text(
+ "(090411121854BP0000001234567890HSO)"));
+
+ verifyPosition(decoder, text(
+ "(01029131573BR00150428A3801.6382N02351.0159E000.0080729278.7800000000LEF9ECB9C)"));
+
+ verifyPosition(decoder, text(
+ "(035988863964BP05000035988863964110524A4241.7977N02318.7561E000.0123536356.5100000000L000946BB)"));
+
+ verifyPosition(decoder, text(
+ "(013632782450BP05000013632782450120803V0000.0000N00000.0000E000.0174654000.0000000000L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(013666666666BP05000013666666666110925A1234.5678N01234.5678W000.002033490.00000000000L000024DE)"));
+
+ verifyPosition(decoder, text(
+ "(013666666666BO012110925A1234.5678N01234.5678W000.0025948118.7200000000L000024DE)"));
+
+ verifyPosition(decoder, text(
+ "(088045133878BR00130228A5124.5526N00117.7152W000.0233614352.2200000000L01B0CF1C)"));
+
+ verifyPosition(decoder, text(
+ "(008600410203BP05000008600410203130721A4152.5790N01239.2770E000.0145238173.870100000AL0000000)"));
+
+ verifyPosition(decoder, text(
+ "(013012345678BR00130515A4843.9703N01907.6211E000.019232800000000000000L00009239)"));
+
+ verifyPosition(decoder, text(
+ "(012345678901BP05000012345678901130520A3439.9629S05826.3504W000.1175622323.8700000000L000450AC)"));
+
+ verifyPosition(decoder, text(
+ "(012345678901BR00130520A3439.9629S05826.3504W000.1175622323.8700000000L000450AC)"));
+
+ verifyPosition(decoder, text(
+ "(352606090042050,BP05,240414,V,0000.0000N,00000.0000E,000.0,193133,000.0)"));
+
+ verifyPosition(decoder, text(
+ "(352606090042050,BP05,240414,A,4527.3513N,00909.9758E,4.80,112825,155.49)"),
+ position("2014-04-24 11:28:25.000", true, 45.45586, 9.16626));
+
+ verifyPosition(decoder, text(
+ "(013632782450,BP05,101201,A,2234.0297N,11405.9101E,000.0,040137,178.48,00000000,L00000000)"));
+
+ verifyPosition(decoder, text(
+ "(864768010009188,BP05,271114,V,4012.19376N,00824.05638E,000.0,154436,000.0)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,BP05,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC07,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC11,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC12,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC13,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC17,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyPosition(decoder, text(
+ "(094050000111BP05000094050000111150808A3804.2418N04616.7468E000.0201447133.3501000011L0028019DT000)"));
+
+ verifyPosition(decoder, text(
+ "(864555555555555,DW3B,131117,A,5544.02870N,01315.08194E,1.597,223707,291.65,-0.10,4)"));
+
+ verifyPosition(decoder, text(
+ "(864555555555555,DW3B,131117,A,5544.02870N,01315.08194E,1.597,223707,291.65,0.10,8)"));
+
+ verifyPosition(decoder, text(
+ "(013632651491,ZC07,040613,A,2234.0297N,11405.9101E,000.0,040137,178.48)"));
+
+ verifyAttributes(decoder, text(
+ "(013632651491,ZC20,040613,040137,6,42,112,0)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,DW51,200,1,3215,43370,2,58:F3:BB:3B:AA:82*-65*1,1C:6A:BB:AA:81:95*-78*1,151117,154419)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,DW5B,210,6,5995,47701,5,30:EE:CC:E7:86:DD*-59*11,4C:60:CC:EA:BB:EE*-68*1,42:AA:DE:EA:BB:00*-69*1,32:CD:BB:C3:4F:CC*-86*3,10:00:43:BA:22:15*-88*1,151117,163722)"));
+
+ verifyNotNull(decoder, text(
+ "(013632651491,DW50,460,0,0,6,2,aa:bb:cc:dd:ee:ff*-8*0,aa:bb:cc:dd:ee:ff*-8*0,040613,040137)"));
+
+ verifyNotNull(decoder, text(
+ "(013632651491,DW50,460,0,0,6,0,040613,040137)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,ZC03,191117,234207,$Notice: Device version: 1.0$)"));
+
+ verifyNotNull(decoder, text(
+ "(864555555555555,ZC03,191117,234207,$1 .Sensor sensitivity: 1\r\n2 .Alert status: Off\r\n3 .Check interval is set to 240 minute(s).\r\n4 .Checkgsm interval is set to 60 minute(s).\r\n5 .SOS SMS Alert: On\r\n6 .SOS Call Alert: On\r\n7 . Power: 95%$)"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
new file mode 100644
index 000000000..82e0e0d88
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
@@ -0,0 +1,282 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Tk103ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodeOutputControl() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_DATA, "1");
+
+ assertEquals("(123456789012345AV001)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeEngineStop() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ assertEquals("(123456789012345AV010)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionSingle() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ assertEquals("(123456789012345AP00)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionPeriodic() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 60);
+
+ assertEquals("(123456789012345AR00003C0000)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionStop() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_STOP);
+
+ assertEquals("(123456789012345AR0000000000)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeGetVersion() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_GET_VERSION);
+
+ assertEquals("(123456789012345AP07)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeRebootDevice() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+
+ assertEquals("(123456789012345AT00)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSetOdometer() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_ODOMETER);
+
+ assertEquals("(123456789012345AX01)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionSingleAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ assertEquals("[begin]sms2,*getposl*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionPeriodicAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+
+ assertEquals("[begin]sms2,*routetrack*99*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodePositionStopAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_STOP);
+
+ assertEquals("[begin]sms2,*routetrackoff*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeGetVersionAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_GET_VERSION);
+
+ assertEquals("[begin]sms2,*about*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeRebootDeviceAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+
+ assertEquals("[begin]sms2,88888888,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeIdentificationAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_IDENTIFICATION);
+
+ assertEquals("[begin]sms2,999999,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSosOnAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_SOS);
+ command.set(Command.KEY_ENABLE, true);
+
+ assertEquals("[begin]sms2,*soson*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSosOffAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_SOS);
+ command.set(Command.KEY_ENABLE, false);
+
+ assertEquals("[begin]sms2,*sosoff*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "AA00");
+
+ assertEquals("(123456789012345AA00)", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeCustomAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "any text is ok");
+
+ assertEquals("[begin]sms2,any text is ok,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSetConnectionAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_CONNECTION);
+ command.set(Command.KEY_SERVER, "1.2.3.4");
+ command.set(Command.KEY_PORT, "5555");
+
+ assertEquals("[begin]sms2,*setip*1*2*3*4*5555*,[end]", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeSosNumberAlternative() {
+
+ Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, "0");
+ command.set(Command.KEY_PHONE, "+55555555555");
+ command.set(Command.KEY_DEVICE_PASSWORD, "232323");
+
+ assertEquals("[begin]sms2,*master*232323*+55555555555*,[end]", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
new file mode 100644
index 000000000..216e65a5d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
@@ -0,0 +1,74 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class Tlt2hProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tlt2hProtocolDecoder decoder = new Tlt2hProtocolDecoder(null);
+
+ verifyPositions(decoder, text(
+ "#860425040088567#MT600+#0000#0#1#129#40#0#AUTOLOW#1\r\n",
+ "#000321901$GPRMC,172030.00,A,4845.2906,N,01910.2742,E,0.01,,041219,,,A*43\r\n"));
+
+ verifyAttribute(decoder, text(
+ "#869260042149724#MP90_4G#0000#AUTOLOW#1\r\n" +
+ "#02201be0000$GPRMC,001645.00,A,5333.2920,N,11334.3857,W,0.03,,250419,,,A*5E\r\n"),
+ Position.KEY_IGNITION, false);
+
+ verifyPositions(decoder, text(
+ "#867962040161955#MT600#0000#0#0#137#41#0#AUTO#1\r\n" +
+ "#00019023402$GPRMC,084702.00,A,3228.6772,S,11545.9684,E,,159.80,251018,,,A*56\r\n"));
+
+ verifyPositions(decoder, text(
+ "#868323028789359#MT600#0000#AUTOLOW#1\r\n",
+ "#07d8cd5198$GPRMC,164934.00,A,1814.4854,N,09926.0566,E,0.03,,240417,,,A*4A\r\n"));
+
+ verifyNull(decoder, text(
+ "#861075026000000#\r\n",
+ "#0000#AUTO#1\r\n",
+ "#002c4968045$GPRMC,001556.00,A,3542.1569,N,13938.9814,E,7.38,185.71,160417,,,A*55\r\n"));
+
+ verifyPositions(decoder, text(
+ "#863835026938048#MT500#0000#AUTO#1\r\n",
+ "#67904917c0e$GPRMC,173926.00,A,4247.8476,N,08342.6996,W,0.03,,160417,,,A*59\r\n"));
+
+ verifyPositions(decoder, text(
+ "#357671030108689##0000#AUTO#1\r\n",
+ "#13AE2F8F$GPRMC,211452.000,A,0017.378794,S,03603.441981,E,0.000,0,060216,,,A*68\r\n"));
+
+ verifyPositions(decoder, text(
+ "#357671030946351#V500#0000#AUTO#1\r\n",
+ "#$GPRMC,223835.000,A,0615.3545,S,10708.5779,E,14.62,97.41,070313,,,D*70\r\n"),
+ position("2013-03-07 22:38:35.000", true, -6.25591, 107.14297));
+
+ verifyPositions(decoder, text(
+ "\r\n#357671030946351#V500#0000#AUTO#1\r\n",
+ "#$GPRMC,223835.000,A,0615.3545,S,10708.5779,E,14.62,97.41,070313,,,D*70\r\n"));
+
+ verifyPositions(decoder, text(
+ "#357671030938911#V500#0000#AUTOSTOP#1\r\n",
+ "#00b34d3c$GPRMC,140026.000,A,2623.6452,S,02828.8990,E,0.00,65.44,130213,,,A*4B\r\n"));
+
+ verifyPositions(decoder, text(
+ "#123456789000001#V3338#0000#SMS#3\r\n",
+ "#25ee0dff$GPRMC,083945.180,A,2233.4249,N,11406.0046,E,0.00,315.00,251207,,,A*6E\r\n",
+ "#25ee0dff$GPRMC,083950.180,A,2233.4249,N,11406.0046,E,0.00,315.00,251207,,,A*6E\r\n",
+ "#25ee0dff$GPRMC,083955.180,A,2233.4249,N,11406.0046,E,0.00,315.00,251207,,,A*6E"));
+
+ verifyPositions(decoder, text(
+ "#353686009063310#353686009063310#0000#AUTO#2\r\n",
+ "#239757a9$GPRMC,150252.001,A,2326.6856,S,4631.8154,W,,,260513,,,A*52\r\n",
+ "#239757a9$GPRMC,150322.001,A,2326.6854,S,4631.8157,W,,,260513,,,A*55"));
+
+ verifyPositions(decoder, text(
+ "#357671031289215#V600#0000#AUTOLOW#1\r\n",
+ "#00735e1c$GPRMC,115647.000,A,5553.6524,N,02632.3128,E,0.00,0.0,130614,0.0,W,A*28"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java
new file mode 100644
index 000000000..0aaf567e8
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TlvProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TlvProtocolDecoder decoder = new TlvProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "30430f383630323437303330303934333931ff10393233323132323030303834353433340f533636385f415f56312e30315f454eff1130303a30433a45373a30303a30303a30300132"));
+
+ verifyNull(decoder, binary(
+ "30410f383630323437303330303934333931"));
+
+ verifyNull(decoder, binary(
+ "30420f3836303234373033303039343339310131"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java
new file mode 100644
index 000000000..4f7be1b28
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TmgFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TmgFrameDecoder decoder = new TmgFrameDecoder();
+
+ verifyFrame(
+ binary("24696f662c3836343530323033373939393630342c323238323133323031372c383132343238302c302c3239393133363231362e2d3438323235383537362ca52c313337393234353339382e3831383733343637362c142c2d36393936393937332e302c313135333435343433372e2d313938363833343039322c3439323039373739392c32302c302c2d3332302c302c4c4c4c4c2c4e4e544e2c48482c302e31372c332e30312c3330313131363030312c302c56455230302e3161"),
+ decoder.decode(null, null, binary("111538360b383634353032303337393939363034eb0b1c8d00ffff23000000000000000000000000000000001c9401008c320c00188901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000001d940100c8950100bd0024696f662c3836343530323033373939393630342c323238323133323031372c383132343238302c302c3239393133363231362e2d3438323235383537362ca52c313337393234353339382e3831383733343637362c142c2d36393936393937332e302c313135333435343433372e2d313938363833343039322c3439323039373739392c32302c302c2d3332302c302c4c4c4c4c2c4e4e544e2c48482c302e31372c332e30312c3330313131363030312c302c56455230302e31610a000195010090960100bd0024746d702c3836343530323033373939393630342c323238323133323031372c383132343238302c302c3239393133363231362e2d3438323235383537362ca52c313337393234353339382e3831383733343637362c142c2d36393936393937332e302c313135333435343433372e2d313938363833343039322c3439323039373739392c32302c302c2d3237372c302c4c4c4c4c2c4e4e544e2c48482c302e31372c332e30312c3330313131363030312c302c56455230302e31610a00c9950100289701008b002462616b2c3836343530323033373939393630342c32313131323031372c3132303230312c312c323832362e353938312c4e2c30373731382e363436352c452c3030302e302c3239382e35322c35323834372c32322c332c2d3233392c302c4c4c4c4c2c4e4e544e2c48482c302e32342c332e30332c3330313131363030312c302c56455230302e31610a00000091960100c09701008b002462616b2c3836343530323033373939393630342c32313131323031372c3132303231322c312c323832362e353938312c4e2c30373731382e363437322c452c3030302e302c3037392e34322c35323834372c32332c332c2d3230352c302c4c4c4c4c2c4e4e544e2c48482c302e31352c332e30332c3330313131363030312c302c56455230302e31610a00000029970100589801008a002462616b2c3836343530323033373939393630342c32313131323031372c3132303232322c312c323832362e353938302c4e2c30373731382e363438352c452c3030302e302c3037372e332c35323834372c32332c332c2d3137342c302c4c4c4c4c2c4e4e544e2c48482c302e31382c332e30332c3330313131363030312c302c56455230302e31610a00000000c1970100f09801008a002462616b2c3836343530323033373939393630342c32313131323031372c3132303233312c312c323832362e353936342c4e2c30373731382e363437312c452c3030302e302c3133312e352c35323834372c32322c342c2d3134362c302c4c4c4c4c2c4e4e544e2c48482c302e31392c332e30332c3330313131363030312c302c56455230302e31610a0000000059980100889901008b002462616b2c3836343530323033373939393630342c32313131323031372c3132303234332c312c323832362e353935382c4e2c30373731382e363436382c452c3030302e302c3133392e36362c35323834372c32322c342c2d3132312c302c4c4c4c4c2c4e4e544e2c48482c302e31322c332e30332c3330313131363030312c302c56455230302e31610a000000f1980100209a01008a002462616b2c3836343530323033373939393630342c32313131323031372c3132303235332c312c323832362e353934392c4e2c30373731382e363436")));
+
+ verifyFrame(
+ binary("246c6f632c3836343530323033303335323734342c32393131323031372c3038333034392c312c323533342e363733312c4e2c30383733342e363735352c452c3033382e302c3037372e31392c35343234312c362c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c332c56455230302e3161"),
+ decoder.decode(null, null, binary("246c6f632c3836343530323033303335323734342c32393131323031372c3038333034392c312c323533342e363733312c4e2c30383733342e363735352c452c3033382e302c3037372e31392c35343234312c362c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c332c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333035392c312c323533342e363634322c4e2c30383733342e373333352c452c3032392e302c3132322e37302c35343234312c362c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c342c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333130392c312c323533342e363531362c4e2c30383733342e373938312c452c3034342e302c3039342e39382c36303631322c31312c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31372c332e31302c3330313131363030312c342c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333131392c312c323533342e363432312c4e2c30383733342e383639312c452c3033352e302c3130312e37332c36303631322c31322c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31342c332e31312c3330313131363030312c342c56455230302e31610a246c6f632c3836343530323033303335323734342c32393131323031372c3038333132392c312c323533342e363335352c4e2c30383733342e393135322c452c3032302e302c3131312e31322c36303631322c31322c31312c3130302c302c48484c4c2c4e4e4e4e2c48482c302e31362c332e31312c3330313131363030312c342c56455230302e31610a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java
new file mode 100644
index 000000000..5df2378d9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TmgProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TmgProtocolDecoder decoder = new TmgProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$loc,869309013800417,08032014,094459,1,2826.1956,N,07659.7690,E,0.0,2.5,4441,31,6,95,1,LLLL,NNTN,HH,0.15,0.26,HR38AU1389,0,SW0.1a"));
+
+ verifyPosition(decoder, text(
+ "$bak,864502037999604,21112017,120243,1,2826.5958,N,07718.6468,E,000.0,139.66,52847,22,4,-174,0,LLLL,NNTN,HH,0.12,3.03,301116001,0,VER00.1a"));
+
+ verifyNull(decoder, text(
+ "$iof,864502037999604,2282132017,8124280,0,299136216.-482258576,¥,1379245398.818734676,,-69969973.0,1153454437.-1986834092,492097799,20,0,-320,0,LLLL,NNTN,HH,0.17,3.01,301116001,0,VER00.1a"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,868325023006341,14022017,103947,1,2836.6542,N,07706.2504,E,0.0,0.0,0.0,0.0,0,22,VODAFONE - DELH,15,49B7,1,2.57,13.2,00000010,00000000,0111,00.0,00.0,0.0,SW10.12,NA,#"));
+
+ verifyPosition(decoder, text(
+ "$rid,L,868325023006341,14022017,103706,1,2836.6542,N,07706.2504,E,0.0,0.0,0.0,0.0,0,22,VODAFONE - DELH,15,49B7,1,2.57,13.2,00000011,00000000,0111,00.0,00.0,0.0,SW10.12,0004909463,#"));
+
+ verifyPosition(decoder, text(
+ "$ion,H,868324023777431,27012017,101057,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,1.22,8,20,N.A,0,N.A,1,4.09,00.0,00000111,00000000,1101,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$iof,H,868324023777431,27012017,101111,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,0.87,11,21,N.A,25,N.A,0,4.09,00.0,00000111,00000000,1110,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$rmv,L,868324023777431,27012017,101141,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,0.86,12,21,VODAFONE - DELH,24,3220,0,4.11,00.0,00000111,00000000,1110,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$rnc,H,868324023777431,27012017,101013,4,2830.2923,N,07705.2551,E,0.0,9.65,226.0,0.88,12,21,VODAFONE - DELH,28,3220,0,4.14,07.4,00000111,00000000,1111,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$ebl,H,868324023777431,27012017,101046,4,2830.2923,N,07705.2551,E,0.0,9.65,226.0,0.97,11,21,VODAFONE - DELH,25,3220,0,4.11,00.0,00000111,00000000,1110,00.0-00.0,00.0-0.0,4.42,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,868324023777431,17012017,001023,4,2830.2977,N,07705.2478,E,0.0,207.07,229.2,0.97,11,22,IDEA CELLULAR L,18,DCDE,0,4.09,12.9,00000111,00000000,1111,00.0-00.0,00.0-0.0,3.59,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,868324023777431,17012017,001523,4,2830.2939,N,07705.2527,E,0.0,50.96,236.5,1.05,11,21,IDEA CELLULAR L,18,DCDE,0,4.09,12.8,00000111,00000000,1111,00.0-00.0,00.0-0.0,3.59,01.02,#"));
+
+ verifyPosition(decoder, text(
+ "$nor,L,869309999985699,24062015,094459,4,2826.1956,N,07659.7690,E,67.5,2.5,167,0.82,15,22,airtel,31,4441,1,4.1,12.7,00000011,00000011,1111,0.0,0.0,21.3,SW00.01,#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java
new file mode 100644
index 000000000..666a48bfa
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TopflytechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TopflytechProtocolDecoder decoder = new TopflytechProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(880316890094910BP00XG00b600000000L00074b54S00000000R0C0F0014000100f0130531152205A0706.1395S11024.0965E000.0251.25"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java
new file mode 100644
index 000000000..08bc8f699
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TopinProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TopinProtocolDecoder decoder = new TopinProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "78780d0103593390754169634d0d0a"));
+
+ verifyNotNull(decoder, binary(
+ "7878001719111120141807019456465111aa3c465111ab464651c1a550465106b150465342f750465342f65a465111a95a000d0a"));
+
+ verifyPosition(decoder, binary(
+ "787812100a03170f32179c026b3f3e0c22ad651f34600d0a"));
+
+ verifyAttributes(decoder, binary(
+ "78780713514d0819640d0a"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java
new file mode 100644
index 000000000..8fb5f8d54
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TotemFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TotemFrameDecoder decoder = new TotemFrameDecoder();
+
+ verifyFrame(
+ binary("24243030323542423836323031303033373239343836313345"),
+ decoder.decode(null, null, binary("24243030323542423836323031303033373239343836313345")));
+
+ verifyFrame(
+ binary("24243030363545363836313137323033353932363639357c3137303931323135333235372c2d37392e3337333835332c34332e3736353631392c302c302c7c3441"),
+ decoder.decode(null, null, binary("24243030363545363836313137323033353932363639357c3137303931323135333235372c2d37392e3337333835332c34332e3736353631392c302c302c7c3441")));
+
+ verifyFrame(
+ binary("24243031303841413836343234343032363036333433377c3141303030303030313430313031303130313031343131313030303032374241304535373030333130303030303030302e3030303030303030303030302e303030304e30303030302e3030303045303438313536"),
+ decoder.decode(null, null, binary("24243031303841413836343234343032363036333433377c3141303030303030313430313031303130313031343131313030303032374241304535373030333130303030303030302e3030303030303030303030302e303030304e30303030302e3030303045303438313536")));
+
+ verifyFrame(
+ binary("242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c34463945"),
+ decoder.decode(null, null, binary("242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c344639450d0a")));
+
+ verifyFrame(
+ binary("242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c34463945"),
+ decoder.decode(null, null, binary("0d0a242442393335363839353033373537383531387c4141244750524d432c3036313730382e3030302c412c333734302e323033332c4e2c30323132382e383132312c452c33382e38352c3237322e33362c3132313131332c2c2c412a35327c30322e337c30312e337c30312e397c3030303030303030303030307c32303133313131323036313730387c31343034313430327c30303030303030307c30303245323137317c303030307c302e323137327c383930327c344639450d0a")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
new file mode 100644
index 000000000..287c54968
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java
@@ -0,0 +1,116 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TotemProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TotemProtocolDecoder decoder = new TotemProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$0113AA862010037348253|588040001901220851494212000000753AE901655121700100000.800000002632.6084S02803.3289E29497E"),
+ position("2019-01-22 08:51:49.000", true, -26.54347, 28.05548));
+
+ verifyPosition(decoder, text(
+ "$$011602867119025755430|50099800180420045019401400000000000000B8797D110816811201.500002132615.7037S02801.8099E056149"));
+
+ verifyPosition(decoder, text(
+ "$$0108AB863835028447675|5004C0001710250234064214059828A058AE121010604000.600000320304.7772N10134.8238E11625B"));
+
+ verifyPosition(decoder, text(
+ "$$0108AA863835028447675|5004C0001710250234134114057728A058AE112108305100.600000660304.7787N10134.8719E116458"));
+
+ verifyPosition(decoder, text(
+ "$$0112AA864244026065291|180018001409160205244011000027BA0E57063100000001.200000002237.8119N11403.5075E05202D"));
+
+ verifyPosition(decoder, text(
+ "$$0116AA864244026065291|18001800140916020524401100000000000027BA0E57063100000001.200000002237.8119N11403.5075E052020"));
+
+ verifyPosition(decoder, text(
+ "$$0116AA867119025683137|108000001611020925324112000000000000616027F7001300000099.900000000000.0000N00000.0000E531824"));
+
+ verifyPosition(decoder, text(
+ "$$0128AA864244026065291|18001800140916020524401100000000000000000000000027BA0E57063100000001.200000002237.8119N11403.5075E05202D"));
+
+ verifyPosition(decoder, text(
+ "$$0128AA867965024919124|10010800160223032415401203270321032103270189000027BA0E4E001800200001.000000002237.7581N11403.5088E000957"),
+ position("2016-02-23 03:24:15.000", false, 22.62930, 114.05848));
+
+ verifyPosition(decoder, text(
+ "$$0108AA863835024426319|18004000160216160756411100007DCD0000111000000000.800000000316.3519N10228.5086E126522"));
+
+ verifyPosition(decoder, text(
+ "$$0128AA867521029231005|1880100015101802314842140000000000000000000000001AB48366093127600000.900000000806.1947N09818.4795E080355"));
+
+ verifyPosition(decoder, text(
+ "$$0108AA864244026063437|1A0000001401010101014111000027BA0E57003100000000.000000000000.0000N00000.0000E048156"));
+
+ verifyPosition(decoder, text(
+ "$$BE863771024392112|AA$GPRMC,044704.000,A,1439.3334,N,12059.1417,E,0.00,0.00,200815,,,A*67|01.7|00.8|01.4|000000000000|20150820044704|14291265|00000000|4EECBF8B31|0000|0.0000|0002|00000|56E7"),
+ position("2015-08-20 04:47:04.000", true, 14.65556, 120.98570));
+
+ verifyPosition(decoder, text(
+ "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324"),
+ position("2015-03-14 05:10:02.000", true, 4.65437, 101.14907));
+
+ verifyPosition(decoder, text(
+ "$$AE860990002922822|AA$GPRMC,051002.00,A,0439.26245,N,10108.94448,E,0.023,,140315,,,A*71|02.98|01.95|02.26|000000000000|20150314051003|13841157|105A3B1C|0000|0.0000|0005|5324\r"));
+
+ verifyNull(decoder, text(
+ "$$BB862170017856731|AA$GPRMC,000000.00,V,0000.0000,N,00000.0000,E,000.0,000.0,000000,,,A*73|00.0|00.0|00.0|000000001000|20000000000000|13790000|00000000|00000000|00000000|0.0000|0007|8C23"));
+
+ verifyPosition(decoder, text(
+ "$$B8862170017856731|AA$GPRMC,171849.00,A,3644.9893,N,01012.9927,E,0.049,51,200813,,,A*73|1.59|0.97|1.25|100000001000|20130820171849|13690000|00000000|019BD508|00000000|0.0000|0026|1B2C"));
+
+ verifyPosition(decoder, text(
+ "$$B2359772032984289|AA$GPRMC,104446.000,A,5011.3944,N,01439.6637,E,0.00,,290212,,,A*7D|01.8|00.9|01.5|000000100000|20120229104446|14151221|00050000|046D085E|0000|0.0000|1170|29A7"));
+
+ verifyPosition(decoder, text(
+ "$$8B862170017861566|AA180613080657|A|2237.1901|N|11402.1369|E|1.579|178|8.70|100000001000|13811|00000000|253162F5|00000000|0.0000|0014|2B16"),
+ position("2013-06-18 08:06:57.000", true, 22.61984, 114.03562));
+
+ verifyPosition(decoder, text(
+ "$$72862170017856731|3913090911165280000370000000000000000019BD508A0400000003.400000093644.9817N01012.9944E00506F2E"));
+
+ verifyPosition(decoder, text(
+ "$$B0456123|61$GPRMC,114725.00,A,1258.68276,N,07730.60237,E,0.410,,080113,,,A*79|1.44|0.66|1.27|000000000000|20130108114425|03600000|00000000|053C2BFE|0000|0.3325|0063|2005"));
+
+ verifyNull(decoder, text(
+ "$$AE359772033395899|AA000000000000000000000000000000000000000000000000000000000000|00.0|00.0|00.0|000000000000|20090215000153|13601435|00000000|00000000|0000|0.0000|0007|2DAA"));
+
+ verifyNull(decoder, text(
+ "$$AE359772033395899|AA000000000000000000000000000000000000000000000000000000000000|00.0|00.0|00.0|00000000|20090215001204|14182037|00000000|0012D888|0000|0.0000|0016|5B51"));
+
+ verifyNull(decoder, text(
+ "$$AE359772033395899|AA00000000000000000000000000000000000000000000000000000000000|00.0|00.0|00.0|00000000000|20090215001337|14182013|00000000|0012D888|0000|0.0000|0017|346E"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|60$GPRMC,094859.000,A,3648.2229,N,01008.0976,E,0.00,,221211,,,A*79|02.3|01.3|02.0|000000000000|20111222094858|13360808|00000000|00000000|0000|0.0000|0001||A977"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|09$GPRMC,094905.000,A,3648.2229,N,01008.0976,E,0.00,,221211,,,A*71|02.1|01.3|01.7|000000000000|20111222094905|03210533|00000000|00000000|0000|0.0000|0002||FA58"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,093911.000,A,3648.2146,N,01008.0977,E,0.00,,140312,,,A*7E|02.1|01.1|01.8|000000000000|20120314093910|04100057|00000000|0012D887|0000|0.0000|1128||C50E"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,094258.000,A,3648.2146,N,01008.0977,E,0.00,,140312,,,A*7F|02.1|01.1|01.8|000000000000|20120314094257|04120057|00000000|0012D887|0000|0.0000|1136||CA32"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,234603.000,A,3648.2179,N,01008.0962,E,0.00,,030412,,,A*74|01.8|01.0|01.5|000000000000|20120403234603|14251914|00000000|0012D888|0000|0.0000|3674||940B"));
+
+ verifyPosition(decoder, text(
+ "$$B3359772032399074|AA$GPRMC,234603.000,A,3648.2179,N,01008.0962,E,0.00,,030412,,,A*74|01.8|01.0|01.5|000000000000|20120403234603|14251914|00000000|0012D888|0000|0.0000|3674|940B"));
+
+ verifyPosition(decoder, text(
+ "$$B2356895037578518|AA$GPRMC,173829.000,A,3740.4107,N,02129.9815,E,0.00,,111113,,,A*7B|02.6|01.6|02.1|000000000000|20131111173829|14041251|00000000|002E0DD7|0000|0.0240|6010|8128"));
+
+ verifyPosition(decoder, text(
+ "$$B2356895037578518|AA$GPRMC,203823.000,A,3740.3285,N,02129.9295,E,0.00,,111113,,,A*79|01.5|01.0|01.1|000000000000|20131111203823|14041251|00000000|002E0DD7|0000|0.0000|6371|3824"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
new file mode 100644
index 000000000..d8c54afbe
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class TotemProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ TotemProtocolEncoder encoder = new TotemProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(2);
+ command.setType(Command.TYPE_ENGINE_STOP);
+ command.set(Command.KEY_DEVICE_PASSWORD, "000000");
+
+ assertEquals("*000000,025,C,1#", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java
new file mode 100644
index 000000000..76355066b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tr20ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tr20ProtocolDecoder decoder = new Tr20ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "%%123456789012345,A,120101121800,N6000.0000E13000.0000,0,000,0,01034802,150,[Message]"));
+
+ verifyNull(decoder, text(
+ "%%TRACKPRO01,1"));
+
+ verifyPosition(decoder, text(
+ "%%868873457748532,A,181109121248,N2237.4181E11403.2857,000,282,NA,47010000,108"));
+
+ verifyPosition(decoder, text(
+ "%%TR-10,A,050916070549,N2240.8887E11359.2994,0,000,NA,D3800000,150,CFG:resend|"),
+ position("2005-09-16 07:05:49.000", true, 22.68148, 113.98832));
+
+ verifyPosition(decoder, text(
+ "%%TR-10,A,050916070549,N2240.8887E11359.2994,0,000,NA,D3800000,150,CFG:resend|"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java
new file mode 100644
index 000000000..92fe0da29
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tr900ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tr900ProtocolDecoder decoder = new Tr900ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ ">00001001,4,1,150626,131252,W05830.2978,S3137.2783,,00,348,18,00,003-000,0,3,11111011*3b!"),
+ position("2015-06-26 13:12:52.000", true, -31.62131, -58.50496));
+
+ verifyPosition(decoder, text(
+ ">12345678,1,1,070201,144111,W05829.2613,S3435.2313,,00,034,25,00,126-000,0,3,11111111*2d!"));
+
+ verifyPosition(decoder, text(
+ ">00001001,4,1,150626,131252,W05830.2978,S3137.2783,,00,348,18,00,003-000,0,3,11111011*3b!\r\n"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java
new file mode 100644
index 000000000..e83824fb4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TrackboxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TrackboxProtocolDecoder decoder = new TrackboxProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "a=connect&v=11&i=111111111111111"));
+
+ verifyPosition(decoder, text(
+ "183457.999,5126.0247N,00002.8686E,5.2,70.4,3,57.63,32.11,17.32,150507,05"),
+ position("2007-05-15 18:34:57.999", true, 51.43375, 0.04781));
+
+ verifyPosition(decoder, text(
+ "183558.999,5126.3979N,00003.0745E,5.2,70.4,3,57.63,32.11,17.32,150507,05"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java
new file mode 100644
index 000000000..4352fc935
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TrakMateProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TrakMateProtocolDecoder decoder = new TrakMateProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "^TMSTP|352984083995323|116|13.07809|77.55979|131508|131118|0.0|146.51|7|0|71 -2 248|0|13.1|0.0|10.5|1|0|0|0|#"));
+
+ verifyPosition(decoder, text(
+ "^TMPER|354678456723764|1|12.59675|77.56789|123456|030414|2.3|34.0|1|0|0|0.015|3.9|12.0|23.4|23.4|1|1|0|#"));
+
+ verifyPosition(decoder, text(
+ "^TMALT|354678456723764|3|2|1|12.59675|77.56789|123456|030414|1.2|34.0|#"));
+
+ verifyPosition(decoder, text(
+ "^TMSRT|354678456723764|12.59675|77.56789|123456|030414|1.03|1.01|#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java
new file mode 100644
index 000000000..f482a00bb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TramigoFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TramigoFrameDecoder decoder = new TramigoFrameDecoder();
+
+ verifyFrame(
+ binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46"),
+ decoder.decode(null, null, binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46")));
+
+ verifyFrame(
+ binary("80003d1ac0001c00010100000367152b13bc1d5970696e6720454f46"),
+ decoder.decode(null, null, binary("80003d1ac0001c00010100000367152b13bc1d5970696e6720454f46")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java
new file mode 100644
index 000000000..d35c5c54e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java
@@ -0,0 +1,63 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TramigoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TramigoProtocolDecoder decoder = new TramigoProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "8000c426b000a6000101c557037598050d5c8a595472616d69676f3a204d6f76696e672c20302e3132206b6d2045206f66204c617275742054696e2049736c616d6963205072696d617279205363686f6f6c2c2054616970696e672c20506572616b2c204d592c20342e38333134392c203130302e37333038352c204e572077697468207370656564203130206b6d2f682c2030303a34393a30382041756720392020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000c526b000a6000101f17d03759805115c8a595472616d69676f3a204d6f76696e672c20302e3133206b6d205345206f66204c617275742054696e2049736c616d6963205072696d617279205363686f6f6c2c2054616970696e672c20506572616b2c204d592c20342e38333132322c203130302e37333037382c204e4520776974682073706565642039206b6d2f682c2030303a34383a35332041756720392020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000d426b0009f00010184f20375980593638a595472616d69676f3a204d6f76696e672c20302e3039206b6d204e57206f66204a616c616e2053696d70616e672042617475204d61726b65742c2054616970696e672c20506572616b2c204d592c20342e38333034332c203130302e37323230342c20532077697468207370656564203130206b6d2f682c2030313a32313a31322041756720392020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000d626b0007f0001013c0b037598051d648a595472616d69676f3a2053746f707065642c206174204a616c616e2053696d70616e672042617475204d61726b65742c2054616970696e672c20506572616b2c204d592c20342e38323937322c203130302e37323233322c2030313a32323a34342041756720392020454f46"));
+
+ verifyNull(decoder, binary(
+ "80003d1ac0001c00010100000367152b13bc1d5970696e6720454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000d316b000860001018f8703771bee11fdf2585472616d69676f3a205061726b65642c20302e3131206b6d2053206f6620492e452e532e2050756572746120426f6e6974612c204361726162616e6368656c2c204d61647269642c2045532c2034302e33373736362c202d332e37333833352c2030353a3131204170722031362020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80009e08b00086000101bc1803778a59c58dea57546573742054323320547261636b65723a204d6f76696e672c20312e3639206b6d204e57206f66205574656b6f6e2c2045646f2c204e472c20362e34363137302c20352e36313938322c20452077697468207370656564203333206b6d2f682c2031363a3138205365702032372020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000853eb000b8000101fcff032f14665a89e2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c206d6f76696e672c20302e3135206b6d205357206f66204261626120416e696d61736861756e205374726565742d426f64652054686f6d61732053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383736352c20332e33343735352c2031303a3031204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000973eb000b90001012128032f14667794e2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c2073746f707065642c20302e3134206b6d205357206f66204261626120416e696d61736861756e205374726565742d426f64652054686f6d61732053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383736372c20332e33343737332c2031303a3438204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000b73eb000ad000101fdd2032f1466c9cbe2564176656e7369732053797353657276653a2049676e6974696f6e206f6e2064657465637465642c206d6f76696e672c20302e3131206b6d2045206f6620416c68616a69204d6173686120526f616420466f6f746272696467652c20537572756c6572652c204c61676f7320436974792c204e472c20362e35303031342c20332e33353434332c2031343a3434204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000883eb000d3000101b223032f1466fc89e2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30323a34312c2073746f707065642c20302e3039206b6d205345206f66204a696e616475205072696d617279205363686f6f6c20416465204f6e6974696d6572696e2053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383639332c20332e33343636302c2031303a3033204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80009a3eb000d300010109ff032f1466b195e2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30353a31342c2073746f707065642c20302e3039206b6d205345206f66204a696e616475205072696d617279205363686f6f6c20416465204f6e6974696d6572696e2053742e2c20537572756c6572652c204c61676f7320436974792c204e472c20362e34383639312c20332e33343636322c2031303a3533204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000bc3eb000ba000101622c032f1466bacce2564176656e7369732053797353657276653a2049676e6974696f6e206f66662064657465637465642c2049676e4f6e506572696f643a2030303a30343a30302c206d6f76696e672c20617420416b6572656c6520526f61642d4f67756e6c616e612044726976652c20537572756c6572652c204c61676f7320436974792c204e472c20362e35303630332c20332e33353232382c2031343a3438204d6172203131202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80001d3cb000b3000101160f032f1466b475e0564176656e7369732053797353657276653a205374617475732c204750533a203931252c2047534d3a203737252c20475052533a20436f6e6e65637465642c20626174746572793a20313030252c207265706f7274733a2049676e6974696f6e20286f6666292c205374617475732028352c322e302c3732302c3330292c20362e34393239382c20332e33343836352c2031393a3038204d6172203920454f46"));
+
+ verifyAttributes(decoder, binary(
+ "80005408b000af000101b23903677f00c8436d3842616c697365204f6e653a20416c6c756d616765206d61726368652064e974656374e92c20676172e92c20302e3735206b6d20452064652045636f6c65204175746f726f757465206465204b696e73686173612c2056696c6c65206465204b696e73686173612c204b696e73686173612c2043442c202d342e33343130362c2031352e33343931352c2030313a3030204a616e2031202020454f46"));
+
+ verifyAttributes(decoder, binary(
+ "8000011bb0009e0001015b93032ef6f35994a9545472616d69676f3a204d6f76696e672c20302e3930206b6d205345206f66204372616e6562726f6f6b20466972652053746174696f6e2c2050656e726974682c205379646e65792c2041552c202d33332e37303732322c203135302e37313735392c2053452077697468207370656564203337206b6d2f682c2031393a3438204a616e20342020454f46"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java
new file mode 100644
index 000000000..aceb9e122
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java
@@ -0,0 +1,70 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TrvProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TrvProtocolDecoder decoder = new TrvProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "TRVAP00352121088015548"));
+
+ verifyPosition(decoder, text(
+ "TRVYP03190805A1828.9242N07353.9423E000.0150716029.0010000810020201112,404,27,184,10229"));
+
+ verifyNotNull(decoder, text(
+ "IWAP02,zh_cn,0,6,260,1,11002|39252|9,11002|35112|23,11002|11043|24,11002|39253|24,11002|13751|24,11018|8102|26,3,a|c0-4a-00-b6-9c-f5|64&a|c0-4a-00-b6-9c-f5|64&a|18-a6-f7-92-35-da|84"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01170905A5227.1382N00541.4256E001.7095844000.0008100610020100,204,8,3230,13007"));
+
+ verifyAttributes(decoder, text(
+ "TRVCP01,07800010010000602001206001120124"));
+
+ verifyNull(decoder, text(
+ "IWAP00353456789012345"));
+
+ verifyPosition(decoder, text(
+ "IWAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97& Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97& Home3|74-DE-2B-44-88-8C|97"));
+
+ verifyNotNull(decoder, text(
+ "IWAP02,zh_cn,0,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|3675|9,9520|3676|8,9520|3677|7,4,1|D8-24-BD-79-FA-1F|59&2|3C-46-D8-6D-CE-01|81&3|0C-4C-39-1A-7C-65|69&4|70-A8-E3-5D-D7-C0|65"));
+
+ verifyPosition(decoder, text(
+ "IWAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00,HOME|74-DE-2B-44-88-8C|97&HOME1|74-DE-2B-44-88-8C|97&HOME2|74-DE-2B-44-88-8C|97&HOME3|74-DE-2B-44-88-8C|97"));
+
+ verifyNull(decoder, text(
+ "IWAP03,06000908000102,5555,30"));
+
+ verifyNull(decoder, text(
+ "TRVAP00353456789012345"));
+
+ verifyAttributes(decoder, text(
+ "TRVCP01,06000908000102"));
+
+ verifyAttributes(decoder, text(
+ "TRVCP01,100007100000001020151060011"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01160211A2544.5118N05553.7586E105.711185941.52010001010010000,424,030,3011,27003"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01160209A2540.8863N05546.6125E005.6075734123.7910000810010000,424,030,3012,27323"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671"));
+
+ verifyPosition(decoder, text(
+ "TRVAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671"),
+ position("2008-05-24 06:18:30.000", true, 22.54968, 114.08226));
+
+ verifyPosition(decoder, text(
+ "TRVAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
new file mode 100644
index 000000000..e3833bcc7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tt8850ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tt8850ProtocolDecoder decoder = new Tt8850ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,007F,0,GTFRI,020102,867844000667538,4142726856,0,0,1,3,1.6,0,997.3,-66.830786,10.483394,20171212171418,0734,0004,041A,4220,69,20171212171657,FF61"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTFRI,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTGEO,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTNMR,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,0017,0,GTNMR,,867844000400914,,0,41,1,2,0.0,0,1504.2,-75.569202,6.242850,20150404162835,,,,,97,20150404162836,05EF"));
+
+ verifyNull(decoder, text(
+ "\u0000\u0004,0017,0,GTPNA,,867844000400914,,0,0,1,0,,,,0,0,,,,,,99,20150404190153,0601"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,0017,0,GTEPN,,867844000400914,,0,0,1,0,0.0,0,1717.4,-75.598445,6.278578,20150405003116,,,,,95,20150405003358,0607"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java
new file mode 100644
index 000000000..ed75cee38
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TytanProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TytanProtocolDecoder decoder = new TytanProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "B500192000001405125652CA9B1A325FC98D11A9990018020118FC0D"));
+
+ verifyPositions(decoder, binary(
+ "B500197800007422125652D7AC32325FD08D11A69900180200188280"));
+
+ verifyPositions(decoder, binary(
+ "B500181000001405115652DEEB2A325FC68D11A7D00005012A2AE1"));
+
+ verifyPositions(decoder, binary(
+ "B5005690000068494F561CEAE932325FD28D11A299000702000063045532030066013567018768014B6901286B0240396C04030785986D013E7F040000A7CE81040000A76C82027EAB83080FA01068FFFF0F3C880202583156"));
+
+ verifyPositions(decoder, binary(
+ "b50069a00000689d315604512b32378f1a8e9fe094005a04d7c84b41020300ab250402140c0702c0006501006601006b0280646c0402883db0315604525732378f1d8e9fdd94005a04d7c84b41020300ab250402140c0702c0006501006601006b0280646c0402883db08887"));
+
+ verifyPositions(decoder, binary(
+ "b50028080000689d215602772f00378f1b8e9fdd98005a042efb3e4102030000000402140c070200000901"));
+
+ verifyPositions(decoder, binary(
+ "b500280a0000689d215602772f00378f1b8e9fdd98005a042efb3e4102030000000402140c07020000da20"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
new file mode 100644
index 000000000..f90497292
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
@@ -0,0 +1,56 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TzoneProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TzoneProtocolDecoder decoder = new TzoneProtocolDecoder(null);
+
+ verifyAttributes(decoder, binary(
+ "545A004B2424041302000000086706003324776413030C0A1A2900180513030C0A1A25080F7E1028CAC830000A000F0000000005000AA53201633D05046000010009AA201737019408973B0032B0260D0A"));
+
+ verifyAttributes(decoder, binary(
+ "545a005b24240406010800000866050033819630120911071824000472bd8e5b0008aac01b07019b04bb002f00040b06161154000e100132ff2006161152000e080096ff4606161151000e1e0101ff1406161156000db6405bff490024469e0d0a"));
+
+ verifyAttributes(decoder, binary(
+ "545a006624240406010800000866050033819630120911070e1d000472bd8e5b0008aac01b17019b04bc003a00050b06161151000e1e00ffff1406161152000e08008aff4706161154000e100134ff1f06161156000db0406cff4906161155000df44011ff4e0023811a0d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a005624240111010e0000086169303626931411091b151d2600160801de26ec002f633411091b151d2500000000160c0000040d2a34df000eaa4000001b37016000000000319c0000000000000000000000000000003a84240d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a005024240153011000000863835025559464110103080a22001609011bed79245964a9110103080a22000a0000550c00000604396f04222c000daac000151701a204870000000000000003000959000546190d0a"));
+
+ verifyPosition(decoder, binary(
+ "545A005224240153010E000008638350256668551008130616090016050079F63D2527FAF710081306160900A000002F0D33000803015B07013D7976000DAAE0400537016C049E000000000000000300800002B65EEA0D0A"));
+
+ verifyPosition(decoder, binary(
+ "545a00582424010b022000000860041028904798100803030c2700160a007da96203356669100803030c2700000000000e000004002813730010aa4000000617017100000000000080000000000000000000000000000000007701fe0d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a00582424010b022000000860041028904798100803030d1a001609007da9620335666a100803030d1900000000000e000004002813730010aa400000063701720000000000008000000000000000000000000000000000787f0c0d0a"));
+
+ verifyPosition(decoder, binary(
+ "545a00582424010b021e000008637710239476270f080b0a3228001600000000000000000000000000000000000000000000000401a00822001088c00020183701a6053800000000800000000000000000000000000000000077c9860d0a"),
+ position("1999-11-30 00:00:00.000", false, 0.0, 0.0));
+
+ verifyPosition(decoder, binary(
+ "545A00912424010B021E000008661040203754350F061807083800160400CE5ADC041447620F0618070838000A0000060C7C0004253378370010AAC000000C37018504E500000000800000000000000000390B0A0014061113000000051200140610600014061220001000133800140610070010001473001000151100101500640010000920001000148400000000000000F2EF570D0A"),
+ position("2015-06-24 07:08:56.000", true, 22.53946, 114.06310));
+
+ verifyAttributes(decoder, binary(
+ "545A009E2424010A0205000008637710225481290F010F081E33000000000010A0C000310E35000005840000000000000000000000000066140A00140612200010001511001406101000140612490014061308001015006400051400170014061012000000050200140612470000000504001406100700140612510014061260001015012000000005080014061252001406130900101501410000000506000853A40D0A"));
+
+ verifyAttributes(decoder, binary(
+ "545A00992424010A0205000008637710225481290F010F082634000000000010A0C000311035000005870000000000000000000000000061130A000000050800101500640014061251001406130800051400170010150141001406101000140612200014061309000000050200140610070014061260001406124900140612470014061012001406125200100015110010150120000000050400183E8A0D0A"));
+
+ verifyAttributes(decoder, binary(
+ "545A00942424010A0205000008637710225481290F010F091C1F000000000010A1C000310F3500000586000000000000000000000000005C120A001406101000140612490014061012001406125200000005040000000502001015012000000005080010001511001406122000140612600014061247001406130900140610070010150141000514001700140612510010150064007A907C0D0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
new file mode 100644
index 000000000..01d63bfa3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
@@ -0,0 +1,29 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class UlbotechFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ UlbotechFrameDecoder decoder = new UlbotechFrameDecoder();
+
+ verifyFrame(
+ binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"),
+ decoder.decode(null, null, binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8")));
+
+ verifyFrame(
+ binary("2a545330312c33353430343330353133383934363023"),
+ decoder.decode(null, null, binary("2a545330312c33353430343330353133383934363023")));
+
+ verifyFrame(
+ binary("f8010108679650230646339de69054010e015ee17506bde2c60000000000ac0304024000000404000009f705060390181422170711310583410c0000310d00312f834131018608040003130a100101136cf8"),
+ decoder.decode(null, null, binary("f8010108679650230646339de69054010e015ee17506bde2c60000000000ac0304024000000404000009f70005060390181422170711310583410c0000310d00312f834131018608040003130a100101136cf8")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
new file mode 100644
index 000000000..8c820183b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
@@ -0,0 +1,94 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class UlbotechProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ UlbotechProtocolDecoder decoder = new UlbotechProtocolDecoder(null);
+
+ verifyNull(decoder, buffer(
+ "*TS01,353323081464660#"));
+
+ verifyAttributes(decoder, buffer(
+ "*TS01,868323025245751,134955140317,WFE:0#"));
+
+ verifyPosition(decoder, binary(
+ "f801010868323028799515251e10d3010e03b52df8ff99fde500000000270f030402020000040402c62a7e0506057c1929220d060800000000000000000f040071eb621001018536f8"),
+ position("2019-09-25 11:49:39.000", false, 62.20543, -6.68521));
+
+ verifyPosition(decoder, binary(
+ "f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"));
+
+ verifyPosition(decoder, binary(
+ "F8010103596580420045259CFB3329010E015ED91506BDE5A800000000009E030402420000040400492AA405060344197E220D071131058F410C1591310D48312F8F413107C60804027666B00C138254D182607A826EE083BE554385F50019423CAD1DF8"));
+
+ verifyNotNull(decoder, binary(
+ "F8010108683230231070781EA3676E020BFFFFFFFFFFFFFFFFFFFF780304000000030404000002C20506032A1790220E100101AC72F8"));
+
+ verifyNotNull(decoder, binary(
+ "f8010108683230220996561ea6ce1c020bffffffffffffffffffff78030400000000040400087b710506035519ad2214060800000000000000006220f8"));
+
+ verifyAttributes(decoder, binary(
+ "f8010108683230220996561ea6ce3f020b02cc00114e86000002f153030400000000040400087b710506035619a4221406080000000000000000fbcff8"));
+
+ verifyAttributes(decoder, binary(
+ "f8010108683230211861161e9d8c48020b00de0063eb730128b56161030400010001040400127d0705060174179422021005e000000001db06f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108683230220996561ea6cdf9010eff47465cfb68d7a000000000270f030400000000040400087b710506035119ba22140608000000000000000022cef8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108683230220996561ea74274010eff47477bfb68d89000000000270f030400000000040400087b710506035419472214060800000000000000006661f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103515790566431569e5fbb9d010e015ee2b906bde4a000000000009f03040a4000000404000115fe05060340173f22030711310583410c0000310d00312f834131000008040000b78c09077320290082c021100101120af8"));
+
+ verifyNull(decoder, buffer(
+ "*TS01,354043051389460#"));
+
+ verifyPosition(decoder, binary(
+ "f8010108679650230646339de69054010e015ee17506bde2c60000000000ac0304024000000404000009f705060390181422170711310583410c0000310d00312f834131018608040003130a100101136cf8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108679650230651689dc8e45b010e01194a26fbd47fa6001f003c0054030402420000040400024d7b0506037c18692212071131057f410c0ee0310d1b312f41413112ef0804000dd59fcc32f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103596580419465449da89d16010efe5580fe0923d82100140129005903040242000004040001a7f10506037818be220e070e31057b410c1324310d144131fa3208040020b1418297f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103596580419465449da8564e010efe55a1800923d04b0000000000710304000000000404000178d2050603571876220ec3caf8"));
+
+ verifyPosition(decoder, binary(
+ "f8010103545500500179009ccb4b62010e00144db906310d3f0000000000cb0304000000000404000a8123050603211860221006080000000100000000ef97f8"));
+
+ verifyPosition(decoder, binary(
+ "F80101035785203457289495D60235010E016175A506C2C838000000000064"));
+
+ verifyPosition(decoder, binary(
+ "F8010108621060211481299C4247FA010E015EE1D606BDE797000301370081030402420000040400523CAF050603921743220706080000000000000000071131058E410C0E30310D48312F8E4131046A080402C8F2545445F8"));
+
+ verifyPosition(decoder, binary(
+ "F8010108621060211481299C4249FA010E015EE27506BDE80900020000008F030402420000040400523CAF05060392173F220706080000000000000000071131058E410C0E40310D48312F8E41310884080402CA60E43872F8"));
+
+ verifyNotNull(decoder, binary(
+ "f8010108653280262660481cdacf830209ffffffffffffffff780304000300000404000000030506017418a021f99697f8"));
+
+ verifyNotNull(decoder, binary(
+ "f801010865328026266048fffeae800209ffffffffffffffff7803040200000004040000000005060375175421f3060800000000000000009c28f8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108653280262660489ce260b4010e01e757bd022340d7002b010d01570304020200000404000000260506036a17d42200060800000000000000000a0101ab9ff8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108653280262660489ce260df010e01e756f30223384a0003010a02a80304020200000404000001280506036217fe22010608000000000000000005aaf8"));
+
+ verifyPosition(decoder, binary(
+ "f8010108653280262660489ce26128010e01e769ac022336290014010300730304020200000404000003c905060371181c2201060800000000000000000a0140e471f8"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
new file mode 100644
index 000000000..e758725f2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
@@ -0,0 +1,85 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class UproProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ UproProtocolDecoder decoder = new UproProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "*HQ201861909268000132,BA&A1820223307024309650492530000311019&B0100000000&F0000&V0036&R0500&J000182&M0052&W00000091&I231026027BD39090827BD5ACA04&X(501E0)(B0000)(E0136)(J01E0)(L3)(k8937204016201240376F)&K00200&T85&N01#"));
+
+ verifyAttribute(decoder, buffer(
+ "*VK200867282036729446,BA&A1759265051877702037465660022210819&B0000000000&W00&G000030&M830&N26&O1706&o11&T0783#"),
+ Position.KEY_BATTERY_LEVEL, 83.0);
+
+ verifyAttributes(decoder, buffer(
+ "*VK201867282035754650,AH&B0000000000&W00&M990&N31&Z02&b2&T0458#"));
+
+ verifyAttributes(decoder, buffer(
+ "*VK201867282035455779,AH&B0000000000&W00&M940&N30&Z02&Y12922&T0268#"));
+
+ verifyPosition(decoder, buffer(
+ "*VK200867282035455779,BA&A0850065052928902036605660013170719&B0000000000&W00&G000030&M850&N20&O1808&o10&Y12922&T0081#"));
+
+ verifyAttributes(decoder, buffer(
+ "*VK200867282035455779,BA&X260,6,1016,13931,60;1016,13929,81;1016,14174,82;1016,13930,82&E190717103920&B0100000000&W00&G000030&M900&N23&O0000&o07&Y14014&T0015#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ200861810538000002,BA&A0206033302618209658563620115180119&B0100000040&C6328680=&F0039&R2710&V0036&T09&K50000&N04&P0200#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ200999999,AB1&A1656512233362911356523660000230618&B0100060010&C00000<6<&F0000&R2405&V0109&W0000003E&K00100&T65&X(k89860045191536000374)#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ20113800138000,YAA&A0732142233550011405829060520190314&B0100000000&C00001234&R3109&T80#"));
+
+ verifyPosition(decoder, binary(
+ "2a4d473230313836383530303032303030343836372c414226413035303032343138313438373536303636303131373732323030303031313132313626583331302c3236302c34383837322c353639312c37333b34383837322c3732322c38363b34383837322c353639332c38383b34383837322c323336332c39303b34383837322c323336322c393726423030303030303030303026573030264e3230265a31342659313430303323"));
+
+ verifyPosition(decoder, binary(
+ "2a4d473230303639333530323030303033353537332c42412641303834313237333332363334353230373033383933373630303030303235313131362642303130303030303030302647303036323030264d393930264e3235264f3035303026433030313a363b363926510411058c0c125c0d0a2fff4237ee614d66454c140826555f50000000000300000000000000000026543139333723"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000035441,BA&A1213073325458307036690710000151116&P0730000032ce4fb3&D1&B0000000000&C005799?7&S3,20161115120025,07035.54659E,3324.87721N,3000,0,0,0,0,847,599,8,40,0,19,20&U_P\0\0\0\0\0\0\0\0\0\0\0\0\0\0&T0107"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000034964,AB&A0800253335360507036975710000091116&P0730000032d2a94d&B0000000000&N13&Z12&U_P\0\0\0\u0004\0\0\0\0\0\0\0\0\0\0"),
+ position("2016-11-09 08:00:25.000", true, -33.58934, -70.61626));
+
+ verifyNull(decoder, buffer(
+ "*MG20113800138000,AH"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000034964,AB&A0200183324418107033792800009051116&B0000000000&N15&Z94&U_P\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
+
+ verifyPosition(decoder, buffer(
+ "*MG201693502000034964,AB&A0200543324412007033805910000051116&P0730000032d66785&B0000000000&N15&Z92&U_P\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905447674,BA&A2003064913201201845107561627121016&B0100000000&C05>8=961&F0333&K023101002154A7"));
+
+ verifyPosition(decoder, buffer(
+ "*AI200905300036,AH&A0317264913209801844913060000251115&B0500000000&C0;4?72:9&F0000"),
+ position("2015-11-25 03:17:26.000", false, 49.22016, 18.74855));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AS&A1647304913209801844913060000251115&B0400000000&C0;4?72:9&F0000"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AC1&A1648014913209801844913060000251115&B0400000000&C0;4?72:9&F0000"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AB1&A1702464913231101844949860000251115&B0500000000&C0;4?72:9&F0000"));
+
+ verifyPosition(decoder, buffer(
+ "*AI2000905300036,AD1&A1703054913231101844949860000251115&B0500000000&C0;4?72:9&F0000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java
new file mode 100644
index 000000000..e3761c3ef
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java
@@ -0,0 +1,77 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class V680ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ V680ProtocolDecoder decoder = new V680ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "#867967020910610#01234567890#1#0000#AUT#1#0500000000120000#114.036291,E,22.665795,N,111.00,000.00#111116#193333##"),
+ position("2016-11-11 19:33:33.000", true, 22.66579, 114.03629));
+
+ verifyPosition(decoder, text(
+ "#355488020168617##1#0000#AUT#01#260001a412966f#1834.790700,E,5302.748800,N,0.00,0.00#310316#174538.000##"));
+
+ verifyPosition(decoder, text(
+ "#355488020168617##1#0000#AUT#01##1834.770100,E,5302.742800,N,0.62,0.00#310316#211537.000##"));
+
+ verifyNull(decoder, text(
+ "#353588102019155"));
+
+ verifyPosition(decoder, text(
+ "#135790246811222#13486119277#1#0000#SOS#1#27bc10af#11407.4182,E,2232.7632,N,0.00,79.50#070709#134147.000##"));
+
+ verifyPosition(decoder, text(
+ "#356823031193431##0#0000#SF#1#72403#V#04702.3025,W,2252.18380,S,008.18,0#090413#134938"),
+ position("2013-04-09 13:49:38.000", false, -22.86973, -47.038375));
+
+ verifyPosition(decoder, text(
+ "#356823033219838#1000#0#1478#AUT#1#66830FFB#03855.6628,E,4716.6821,N,001.41,259#130812#143905"));
+
+ verifyPosition(decoder, text(
+ "#353588102019155##1#0000#AUT#01#7240060be7873f#4849.079800,W,2614.458200,S,0.00,0.00#130413#182110.000"));
+
+ verifyPosition(decoder, text(
+ "#353588302045917##1#0000#AUT#01#7243141c2b14c3#4738.442300,W,2334.874000,S,0.00,0.30#170413#004831.000"));
+
+ verifyPosition(decoder, text(
+ "#352897045085282##0#0000#AUT#1#72400510730208,00d36307,10734fc4#4647.8922,W,2339.1956,S,2.60,63.74#200413#094310.000"));
+
+ verifyPosition(decoder, text(
+ "#356823033537791##0#0000#AUT#1#V#03610.2179,E,5004.5796,N,000.01,349#180513#073758"));
+
+ verifyPosition(decoder, text(
+ "#356823031236214##0#0000#AUT#1#V#01904.5491,E,6941.0085,N,000.09,248#170513#160140"));
+
+ verifyNull(decoder, text(
+ "#353588550032869##1#0000#AUT#01#72400401cd01a5#00000.0000,E,0000.0000,N,0.00,#000000#000000.000"));
+
+ verifyPosition(decoder, text(
+ "#352897045085282##0#0000#AUT#1#72400510730208,00d36307,10734fc4#4647.8922,W,2339.1956,S,2.60,63.74#200413#094310.000##"));
+
+ verifyPosition(decoder, text(
+ "#352165050199210##13#0000#AUT#1#72400605471305,054712fd,054712ff#05144.0008,W,3005.5011,S,0.11,201.46#260713#172647.000##"));
+
+ verifyPosition(decoder, text(
+ "#356823031166908#13001190527#0#0000#AUT#4#V#07136.4070,W,1040.0575,N,000.35,257#280813#142836#V#07136.4088,W,1040.0580,N,000.49,288#280813#142846#V#07136.4098,W,1040.0590,N,000.59,264#280813#142856#V#07136.4093,W,1040.0605,N,000.30,264#280813#142906##"));
+
+ verifyPosition(decoder, text(
+ "#355488020132015##1#0000#AUT#01#510089246a34c0#10641.338800,E,619.427100,S,0.00,0.00#011113#161942.000##"));
+
+ verifyPosition(decoder, text(
+ "#359094025419110#bigfriend#0#1234#AUTO#1##04632.8846,W,2327.2264,S,0.00,0.00#220913#234808##"));
+
+ verifyPosition(decoder, text(
+ "#353588102031599##1#0000#AUT#01#41300304843fc1#7955.124400,E,642.095500,N,5.28,95.21#041213#074431.000##"));
+
+ verifyPosition(decoder, text(
+ "1#0000#AUT#01#23403007fa650e#16.747700,W,5136.356500,N,0.00,0.00#040415#002051.000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java
new file mode 100644
index 000000000..11596945e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java
@@ -0,0 +1,40 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class VisiontekProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VisiontekProtocolDecoder decoder = new VisiontekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$1,117,28,01,16,15,05,48,1725.0518N,07824.5298E,0620,11,0,185,2062,0,0,0,1,1,1,1,24,00.0000,00.3740,00.0000,VAJRA V1.00,A"));
+
+ verifyPosition(decoder, text(
+ "$1,VMC,358072044271838,26,10,15,10,43,20,17.066418N,080.395667E,000.0,285,00.8,0074,6390,0,0,0,0,0,0,0,0,00.00,00.00,00,00,0000,12.7,4.0,24,10,0000000000000,A,0"));
+
+ verifyNull(decoder, text(
+ "$1,VMC,358072044271838,25,10,15,09,19,40,00.0000000,000.0000000,000.0,000,00.0,0000,6070,0,0,0,0,0,0,0,0,00.00,00.00,00,00,0000,12.5,4.0,99,00,0000000000000,V,0"));
+
+ verifyPosition(decoder, text(
+ "$1,AP116,05,06,15,11,48,32,1725.0460N,07824.5289E,0617,07,0,030,2091,0,0,0,1,1,1,1,20,00.0000,00.3820,00.0000,VAJRA V1.00,A"));
+
+ verifyPosition(decoder, text(
+ "$1,AP09BU9397,861785006462448,20,06,14,15,03,28,17267339N,078279407E,060.0,073,0550,11,0,1,0,0,1,1,26,A,0000000000"),
+ position("2014-06-20 15:03:28.000", true, 17.44556, 78.46567));
+
+ verifyNull(decoder, text(
+ "$1,AP09BU9397,861785006462448,20,06,14,15,03,28,000000000,0000000000,000.0,000,0000,00,0,1,0,0,1,1,24,V,0000000000"));
+
+ verifyNull(decoder, text(
+ "$1,1234567890,02,06,11,17,07,45,00000000,000000000,00.0,0,0,V"));
+
+ verifyPosition(decoder, text(
+ "$1,1234567890,02,06,11,17,07,45,17267690N,078279340E,060.0,113,0,A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java
new file mode 100644
index 000000000..3281aae34
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class VnetProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VnetProtocolDecoder decoder = new VnetProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "24240000140029111909062986818303379282604c452e322e30302ea32b020f0000d3552323"));
+
+ verifyPosition(decoder, binary(
+ "242433001200290615174213211489861061060690070B0001020304700005001E382323"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java
new file mode 100644
index 000000000..9422f6d74
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Vt200FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Vt200FrameDecoder decoder = new Vt200FrameDecoder();
+
+ verifyFrame(
+ binary("28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129"),
+ decoder.decode(null, null, binary("28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129")));
+
+ verifyFrame(
+ binary("28631037309456208400340102f51306171327294418267501208948170231071f0000044300200100005f02180000667500000000000000000000080000624629"),
+ decoder.decode(null, null, binary("28631037309456208400340102f513061713273d144418267501208948170231071f0000044300200100005f02180000667500000000000000000000080000624629")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java
new file mode 100644
index 000000000..9c224bc8e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java
@@ -0,0 +1,60 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Vt200ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Vt200ProtocolDecoder decoder = new Vt200ProtocolDecoder(null);
+
+ verifyPosition(decoder, binary(
+ "28192030961807208200210101b919011818375801245774036424612500160917000003aa008800007b00aa3429"));
+
+ verifyNull(decoder, binary(
+ "286310373094563082002701033d010817143327c68a14841e00c27f550e9a000000000c000000084700200120007d01af260b29"));
+
+ verifyAttributes(decoder, binary(
+ "2863103730945630880062033d862631037309456f222014604362936f010817140954010817144135076b00002a3800003b7d6127cc91040000000000000000000000005a0000088e000001ce02630000263b000009b401ff00000cb40000069c02af000018190200000102019729"));
+
+ verifyPosition(decoder, binary(
+ "286310373094562086002101033d010817143328441790420114817637207d090a00000847002001207f00d6f229"));
+
+ verifyPosition(decoder, binary(
+ "286310373094562086002101033d0108171433354417932101148139772c9d080a00000847002001207f00dc6729"));
+
+ verifyNull(decoder, binary(
+ "2863103730945600880012180108171433004418103801148375470000dd29"));
+
+ verifyNull(decoder, binary(
+ "28631037309456108800002e29"));
+
+ verifyAttributes(decoder, binary(
+ "2863103730945630880062033c862631037309456f222014604362936f01081713365601081713571904c800001b2c000034f66827f0840000000000000000000000000047000006e7000001b9022a000023ff000007f2018a00000a10000003f300cd00000d8d0300000302002729"));
+
+ verifyNull(decoder, binary(
+ "28631037309456008e000801042307171804584229"));
+
+ verifyNull(decoder, binary(
+ "28631037309456108800002e29"));
+
+ verifyPosition(decoder, binary(
+ "28631037309456208200210103302307171805444417097301147188170198090f0000073a002000007e00074429"));
+
+ verifyNull(decoder, binary(
+ "286310373094563089001200032f2107171740144417075001147188872c29"));
+
+ verifyAttributes(decoder, binary(
+ "2863103730945630880062032f862631037309456f222014604362936f21071717373221071717401400a100000cd700000004020d3c8e0000000000000000000000000000000000000000000000000000000000000000000a000000040000000e009700000cc9000000000000e929"));
+
+ verifyPosition(decoder, binary(
+ "28631037309456208400340102dc0906171616454415760201144494473f920a0c0000030500200100417c1f383a9d1090510000006a00007000000e00180ee129"));
+
+ verifyPosition(decoder, binary(
+ "28631037309456208400340102dc090617161654441577230114439597368c0a0c0000030500200100417c1baa349d3290510000006a00007000003d15004c11c629"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java
new file mode 100644
index 000000000..a5eb0b49b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class VtfmsFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VtfmsFrameDecoder decoder = new VtfmsFrameDecoder();
+
+ assertEquals(
+ buffer("(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105"),
+ decoder.decode(null, null, buffer("(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java
new file mode 100644
index 000000000..ede5dc7ac
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class VtfmsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ VtfmsProtocolDecoder decoder = new VtfmsProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "(861359037432331,0EF87,00,0,21,2,01,,A,154559,230119,1101.4046,07656.3859,241,000,00078,00000,K,0000812,1,12.7,,,,,,1,0,0,0,1,1,1,+919566531111*+919994462226,)054"),
+ position("2019-01-23 15:45:59.000", true, 11.02341, 76.93977));
+
+ verifyPosition(decoder, text(
+ "(865733028143493,00I76,00,000,,,,,A,133755,210617,10.57354,077.24912,SW,000,00598,00000,K,0017368,1,12.7,,,0.000,,,0,0,0,0,1,1,0,,)074"));
+
+ verifyPosition(decoder, text(
+ "(863071010087648,0HK44,00,000,14,2,9,,A,114946,180313,11.0244,076.9768,282,000,00000,00000,K,0000128,1,12.8,,200,2.501,,4.001,0,0,0,0,0,0,0,,)105"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java
new file mode 100644
index 000000000..4e40eea86
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WatchFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WatchFrameDecoder decoder = new WatchFrameDecoder();
+
+ verifyFrame(
+ binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d"),
+ decoder.decode(null, null, binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d")));
+
+ verifyFrame(
+ binary("5b33472a383330383430363237392a303030382a72636170747572655d"),
+ decoder.decode(null, null, binary("5b33472a383330383430363237392a303030382a72636170747572655d")));
+
+ verifyFrame(
+ binary("5b33472a383330383430363237392a303030392a4c4b2c302c302c38345d"),
+ decoder.decode(null, null, binary("5b33472a383330383430363237392a303030392a4c4b2c302c302c38345d")));
+
+ verifyFrame(
+ binary("5b5a4a2a3031343131313030313335303330342a303033342a303030392a4c4b2c302c302c31395d"),
+ decoder.decode(null, null, binary("5b5a4a2a3031343131313030313335303330342a303033342a303030392a4c4b2c302c302c31395d")));
+
+ verifyFrame(
+ concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), binary("7d5b5d2c2aff"), buffer("]")),
+ decoder.decode(null, null, concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), binary("7d017d027d037d047d05ff"), buffer("]"))));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
new file mode 100644
index 000000000..88acd8222
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
@@ -0,0 +1,137 @@
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import org.junit.Test;
+import org.traccar.Context;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+import static org.junit.Assert.assertEquals;
+
+public class WatchProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WatchProtocolDecoder decoder = new WatchProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "[3G*8809008845*00C0*AL,271219,094744,V,00.000000,N, 0.0000000,E,0.00,0.0,0.0,0,100,81,0,0,00010000,7,0,460,0,9336,3981,141,9336,3912,141,9336,3982,140,9765,4233,134,9765,4071,134,9765,4321,134,9336,4353,132,0,0.0]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*2104134718*00A1*UD_WCDMA,161019,134938,A,43.373367,N,71.157615,W,22.0,350.206,279.717,17,28,79,0,0,00000000,1,1,310,410,23999,132013696,28,1,Home2,60:45:cb:cb:34:68,-93,8.263865]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001332708*0075*0064*AL,040418,052156,A,22.536207,N,113.938673,E,0,0,0,5,100,82,1000,50,00100000,1,255,460,0,9340,3663,35]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*352661090143150*006C*UD,150817,132115,V,28.435142,N,81.354333,W,2.2038,000,99,00,70,100,0,50,00000000,1,1,310,260,1091,30082,139,,00]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*4700609403*0013*bphrt,120,79,73,,,,]"));
+
+ verifyAttributes(decoder, buffer(
+ "[ZJ*357653059860416*0007*000c*BLOOD,109,68]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*8308373902*0080*AL,230817,095346,A,47.083950,N,15.4821850,E,7.60,273.8,0.0,4,15,44,0,0,00200010,2,255,232,1,7605,42530,118,7605,58036,119,0,65.8]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9051007430*006C*UD,150817,132115,V,28.435142,N,81.354333,W,2.2038,000,99,00,70,100,0,50,00000000,1,1,310,260,1091,30082,139,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*6005412902*011F*WT,170517,133811,V,18.512200,N,73.7750283,E,0.00,0.0,0.0,0,92,82,4262,0,00000010,2,1,404,22,10125,8301,141,10125,13921,122,5,Skynet,28:c6:8e:be:87:c0,-60,Intel Wi-Fi,4c:60:de:32:3d:38,-70,Nirvanic-2,40:e3:d6:4a:d9:c2,-73,A4-Guest,40:e3:d6:4a:d9:c4,-73,A4Idatix,40:e3:d6:4a:d9:c3,-73,13.8]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*8308406279*00CC*UD3,170417,190930,V,54.739618,N,25.273213,E,0.0,323.53,175.1,6,51,83,0,0,00000000,1,1,246,01,200,13242758,51,3,TEO-189835,00:8c:54:58:1d:64,-84,Cgates_7137,78:54:2e:e3:71:37,-85,ASUS,9c:5c:8e:b8:d4:78,-93]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9051004074*0058*AL,120117,145602,V,40.058413,N,76.336618,W,11.519,188,99,00,01,80,0,50,00000000,0,1,0,0,,10]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9051000884*009B*UD,030117,161129,V,52.745450,N,0.369512,,0.1481,000,99,00,70,5,0,50,00000000,5,1,234,15,893,3611,135,893,3612,132,893,3993,131,893,30986,129,893,40088,126,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*6430073509*00E7*UD2,241016,081622,V,09.951861,N,-84.1422119,W,0.00,0.0,0.0,0,39,94,0,0,00000000,1,0,712,3,2007,18961,123,4,Luz,00:23:6a:34:ee:76,-70,familia,b0:c5:54:b9:90:ef,-78,fam salas delgado,fc:b4:e6:5d:50:ea,-81,QWERTY,c8:3a:35:43:0f:e8,-93]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*6105117105*008D*UD2,210716,231601,V,-33.480366,N,-70.7630692,E,0.00,0.0,0.0,0,100,34,0,0,00000000,3,255,730,2,29731,54315,167,29731,54316,162,29731,54317,145]"),
+ position("2016-07-21 23:16:01.000", false, -33.48037, -70.76307));
+
+ verifyPosition(decoder, buffer(
+ "[3G*4700222306*0077*UD,120316,140610,V,48.779045,N, 9.1574736,E,0.00,0.0,0.0,0,25,83,0,0,00000000,2,255,262,1,21041,9067,121,21041,5981,116]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*4700222306*011F*UD2,120316,140444,A,48.779045,N, 9.1574736,E,0.57,12.8,0.0,7,28,77,0,0,00000000,2,2,262,1,21041,9067,121,21041,5981,116,5,WG-Superlativ,34:31:c4:c8:a9:22,-67,EasyBox-28E858,18:83:bf:28:e8:f4,-70,MoMaXXg,be:05:43:b7:19:15,-72,MoMaXX2,bc:05:43:b7:19:15,-72,Gastzugang,18:83:bf:28:e8:f5,-72]"));
+
+ verifyNull(decoder, buffer(
+ "[SG*9081000548*0009*LK,0,100]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*9081000548*00A9*UD,110116,113639,V,16.479064,S,68.119072,,0.7593,000,99,00,80,80,0,50,00000000,5,1,736,2,10103,10732,153,10103,11061,152,10103,11012,152,10103,10151,150,10103,10731,143,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*2256002206*0079*UD2,100116,153723,A,38.000000,N,-9.000000,W,0.44,299.3,0.0,7,100,86,0,0,00000008,2,0,268,3,3010,51042,146,3010,51043,132]"));
+
+ verifyNull(decoder, buffer(
+ "[3G*8800000015*0003*TKQ]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*4700186508*00B1*UD,301015,084840,V,45.853100,N,14.6224899,E,0.00,0.0,0.0,0,84,61,0,11,00000008,7,255,293,70,60,6453,139,60,6432,139,60,6431,132,60,6457,127,60,16353,126,60,6451,121,60,16352,118]"));
+
+ verifyNull(decoder, buffer(
+ "[SG*8800000015*0002*LK]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*4700186508*000B*LK,0,10,100]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0087*UD,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0000,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"),
+ position("2014-04-22 13:46:52.000", true, 22.57171, 113.86140));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0087*UD,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0000,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0088*UD2,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0000,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"));
+
+ verifyPosition(decoder, buffer(
+ "[SG*8800000015*0087*AL,220414,134652,A,22.571707,N,113.8613968,E,0.1,0.0,100,7,60,90,1000,50,0001,4,1,460,0,9360,4082,131,9360,4092,148,9360,4091,143,9360,4153,141]"));
+
+ verifyAttributes(decoder, buffer(
+ "[CS*8800000015*0008*PULSE,72]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*6005412902*0007*heart,0]"));
+
+ verifyAttributes(decoder, buffer(
+ "[3G*6005412902*0008*heart,71]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001350304*0033*0064*UD,070318,020827,V,00.000000,N,000.000000,E,0,0,0,0,100,19,1000,50,00000000,1,255,460,0,9346,5223,42]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001350304*0035*0097*UD,070318,020857,V,00.000000,N,000.000000,E,0,0,0,0,100,19,1000,50,00000000,5,255,460,0,9346,5223,42,9346,5214,21,9784,4083,13,9346,5222,11,9346,5221,8]"));
+
+ verifyPosition(decoder, buffer(
+ "[ZJ*014111001350304*0038*008a*UD,070318,021027,V,00.000000,N,000.000000,E,0,0,0,0,100,18,1000,50,00000000,4,255,460,0,9346,5223,42,9346,5214,20,9784,4083,11,9346,5221,5]"));
+
+ }
+
+ @Test
+ public void testDecodeVoiceMessage() throws Exception {
+
+ WatchProtocolDecoder decoder = new WatchProtocolDecoder(null);
+
+ verifyNull(decoder.decode(null, null, buffer("[CS*1234567890*0004*TK,1]")));
+
+ ByteBuf data = binary("7d5b5d2c2aff");
+
+ Object decodedObject = decoder.decode(null, null, concatenateBuffers(buffer("[CS*1234567890*000e*TK,#!AMR"), data.resetReaderIndex(), buffer("]")));
+ assertEquals("1234567890/mock.amr", ((Position) decodedObject).getAttributes().get("audio"));
+
+ verifyFrame(concatenateBuffers(buffer("#!AMR"), data.resetReaderIndex()), ((MockMediaManager) Context.getMediaManager()).readFile("1234567890/mock.amr"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
new file mode 100644
index 000000000..a4a795050
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
@@ -0,0 +1,87 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class WatchProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ WatchProtocolEncoder encoder = new WatchProtocolEncoder(null);
+
+ Command command;
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_REBOOT_DEVICE);
+ verifyFrame(buffer("[CS*123456789012345*0005*RESET]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, 1);
+ command.set(Command.KEY_PHONE, "123456789");
+ verifyFrame(buffer("[CS*123456789012345*000e*SOS1,123456789]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_VOICE_MESSAGE);
+ command.set(Command.KEY_DATA, "2321414d520a2573");
+ verifyFrame(buffer("[CS*123456789012345*000b*TK,#!AMR\n%s]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_VOICE_MESSAGE);
+ command.set(Command.KEY_DATA, "7d5b5d2c2a");
+ verifyFrame(concatenateBuffers(buffer("[CS*123456789012345*000d*TK,"), binary("7d017d027d037d047d05"), buffer("]")), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_VOICE_MESSAGE);
+ command.set(Command.KEY_DATA, "ff");
+ verifyFrame(concatenateBuffers(buffer("[CS*123456789012345*0004*TK,"), binary("ff"), buffer("]")), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_MESSAGE);
+ command.set(Command.KEY_MESSAGE, "text");
+ verifyFrame(buffer("[CS*123456789012345*0018*MESSAGE,0074006500780074]"), encoder.encodeCommand(null, command));
+
+ command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "WORK,6-9,11-13,13-15,17-19");
+ verifyFrame(buffer("[CS*123456789012345*001a*WORK,6-9,11-13,13-15,17-19]"), encoder.encodeCommand(null, command));
+
+ }
+
+ @Test
+ public void testEncodeTimezone() {
+
+ WatchProtocolEncoder encoder = new WatchProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SET_TIMEZONE);
+
+ command.set(Command.KEY_TIMEZONE, "Europe/Amsterdam");
+ verifyFrame(buffer("[CS*123456789012345*0006*LZ,,+1]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_TIMEZONE, "GMT+01:30");
+ verifyFrame(buffer("[CS*123456789012345*0008*LZ,,+1.5]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_TIMEZONE, "Atlantic/Azores");
+ verifyFrame(buffer("[CS*123456789012345*0006*LZ,,-1]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_TIMEZONE, "GMT-11:30");
+ verifyFrame(buffer("[CS*123456789012345*0009*LZ,,-11.5]"), encoder.encodeCommand(null, command));
+
+ command.set(Command.KEY_LANGUAGE, 0);
+ command.set(Command.KEY_TIMEZONE, "GMT+05:45");
+ verifyFrame(buffer("[CS*123456789012345*000a*LZ,0,+5.75]"), encoder.encodeCommand(null, command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java
new file mode 100644
index 000000000..72e56fb44
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java
@@ -0,0 +1,74 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WialonProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WialonProtocolDecoder decoder = new WialonProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "#L#2.0;42001300083;;CE45"));
+
+ verifyNull(decoder, text(
+ "#L#123456789012345;test"));
+
+ verifyNull(decoder, text(
+ "#L#2002;NA"));
+
+ verifyNull(decoder, text(
+ "#P#"));
+
+ verifyPosition(decoder, text(
+ "#D#101118;061143;0756.0930;N;12338.6403;E;18.223;99.766;-4.000;10;0.800;NA;NA;NA;NA;101_521347:1:521249,101_521126:1:6593598,101_521127:1:774780,101_521072_21.1:1:0,101_521072_21.2:1:71353;F24A"));
+
+ verifyPosition(decoder, text(
+ "99999999#D#101118;061143;0756.0930;N;12338.6403;E;18.223;99.766;-4.000;10;0.800;NA;NA;NA;NA;101_521347:1:521249,101_521126:1:6593598,101_521127:1:774780,101_521072_21.1:1:0,101_521072_21.2:1:71353;F24A"));
+
+ verifyPosition(decoder, text(
+ "#D#151216;135910;5321.1466;N;04441.7929;E;87;156;265.000000;12;1.000000;241;NA;NA;NA;odo:2:0.000000,total_fuel:1:430087,can_fls:1:201,can_taho:1:11623,can_mileage:1:140367515"));
+
+ verifyPosition(decoder, text(
+ "#D#151216;140203;5312.59514;N;04830.37834;E;53;273;NA;10;NA;NA;NA;NA;NA;EvId:1:1,Board:2:12.81,Accum:2:4.28"));
+
+ verifyPosition(decoder, text(
+ "#SD#270413;205601;5544.6025;N;03739.6834;E;1;2;3;4"),
+ position("2013-04-27 20:56:01.000", true, 55.74338, 37.66139));
+
+ verifyPosition(decoder, text(
+ "99999999#SD#270413;205601;5544.6025;N;03739.6834;E;1;2;3;4"),
+ position("2013-04-27 20:56:01.000", true, 55.74338, 37.66139));
+
+ verifyPosition(decoder, text(
+ "#SD#021214;065947;2237.7552;N;11404.8851;E;0.000;;170.9;5"));
+
+ verifyPosition(decoder, text(
+ "#D#270413;205601;5544.6025;N;03739.6834;E;1;2;3;4;0.0;0;0;14.77,0.02,3.6;NA;count1:1:564,fuel:2:45.8,hw:3:V4.5"));
+
+ verifyPosition(decoder, text(
+ "#D#190114;051312;4459.6956;N;04105.9930;E;35;306;204.000000;12;NA;452986639;NA;106.000000;NA;sats_gps:1:9,sats_glonass:1:3,balance:2:12123.000000,stay_balance:1:0"));
+
+ verifyPosition(decoder, text(
+ "#D#021214;065947;2237.7552;N;11404.8851;E;0.000;;170.9;5;1.74;NA;NA;NA;NA;NA"));
+
+ verifyPosition(decoder, text(
+ "#D#021214;065947;2237.7552;N;11404.8851;E;0.000;;170.9;5;1.74;NA;NA;;NA;NA"));
+
+ verifyPositions(decoder, text(
+ "#B#080914;073235;5027.50625;N;03026.19321;E;0.700;0.000;NA;4;NA;NA;NA;;NA;Батарея:3:100 %|080914;073420;5027.50845;N;03026.18854;E;1.996;292.540;NA;4;NA;NA;NA;;NA;Батарея:3:100 %"));
+
+ verifyPositions(decoder, text(
+ "#B#110914;102132;5027.50728;N;03026.20369;E;1.979;288.170;NA;NA;NA;NA;NA;;NA;Батарея:3:100 %"));
+
+ verifyPositions(decoder, text(
+ "#B#110315;045857;5364.0167;N;06127.8262;E;0;155;965;7;2.40;4;0;;NA;Uacc:2:3.4,Iacc:2:0.000,Uext:2:13.2,Tcpu:2:14.4,Balance:2:167.65,GPS:3:Off"));
+
+ verifyPositions(decoder, text(
+ "#B#110315;045857;5364.0167;N;06127.8262;E;0;155;965;7;2.40;4;0;14.77,0.02,3.6;AB45DF01145;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
new file mode 100644
index 000000000..642473f2d
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class WondexFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WondexFrameDecoder decoder = new WondexFrameDecoder();
+
+ assertNull(
+ decoder.decode(null, null, binary("f0d70b0001ca9a3b")));
+
+ assertEquals(
+ binary("313034343938393630312c32303133303332333039353531352c31332e3537323737362c35322e3430303833382c302c3030302c37322c302c32"),
+ decoder.decode(null, null, binary("313034343938393630312c32303133303332333039353531352c31332e3537323737362c35322e3430303833382c302c3030302c37322c302c320d0a")));
+
+ assertEquals(binary("d0d70b0001ca9a3b"),
+ decoder.decode(null, null, binary("d0d70b0001ca9a3b")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java
new file mode 100644
index 000000000..b51ca03f3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java
@@ -0,0 +1,65 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WondexProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WondexProtocolDecoder decoder = new WondexProtocolDecoder(null);
+
+ verifyPosition(decoder, buffer(
+ "2005010051,19990925063830,26.106181,44.440225,0,0,0,7,2,0.0,0,,,0"));
+
+ verifyPosition(decoder, buffer(
+ "2000000108,20151030145404,76.948633,43.354700,0,140,15,100,1,1325,125.4,10.5,0.0"),
+ position("2015-10-30 14:54:04.000", true, 43.35470, 76.94863));
+
+ verifyPosition(decoder, buffer(
+ "2000000257,20151030145351,69.379976,53.283905,0,0,16,2,0,0,469.1,58.9,0.0"),
+ position("2015-10-30 14:53:51.000", true, 53.28390, 69.37998));
+
+ verifyPosition(decoder, buffer(
+ "2000000232,20151030145206,51.166900,43.651353,0,132,11,2,0,0,0.0,0.0,0.0"));
+
+ verifyPosition(decoder, buffer(
+ "2000000259,20151030145653,69.380826,53.283890,9,10,15,2,1,695,1002.6,108.2,0.0"));
+
+ verifyPosition(decoder, buffer(
+ "1044989601,20130323074605,0.000000,90.000000,0,000,0,0,2"));
+
+ verifyPosition(decoder, buffer(
+ "123456789000001,20120101123200,130.000000,60.000000,0,000,0,0,0,0"));
+
+ verifyPosition(decoder, buffer(
+ "210000001,20070313170040,121.123456,12.654321,0,233,0,9,2,0.0,0,0.00,0.00,0"));
+
+ verifyPosition(decoder, buffer(
+ "1044989601,20130322172647,13.572583,52.401070,22,204,49,0,2"));
+
+ verifyPosition(decoder, buffer(
+ "1044989601,20130322172647,13.572583,52.401070,22,204,-49,0,2"));
+
+ verifyPosition(decoder, buffer(
+ "3997324533,20140326074908,28.797603,47.041635,0,48,0,6,2,3.90V,0"));
+
+ verifyPosition(decoder, buffer(
+ "2000000001,20140529213210,-63.179111,9.781493,0,0,54.0,8,2,0.0,0,0.01,0.01,0,0,0,0"));
+
+ verifyNotNull(decoder, buffer(
+ "$OK:VER=M7 2.003 DVB rev02c,V2"));
+
+ verifyNotNull(decoder, buffer(
+ "$OK:REBOOT"));
+
+ verifyNotNull(decoder, buffer(
+ "$ERR:GETLOCATION=1"));
+
+ verifyNull(decoder, binary(
+ "d0d70b0001ca9a3b"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
new file mode 100644
index 000000000..41fee2723
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class WondexProtocolEncoderTest extends ProtocolTest {
+ @Test
+ public void testEncode() throws Exception {
+
+ WondexProtocolEncoder encoder = new WondexProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(2);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+ command.set(Command.KEY_DEVICE_PASSWORD, "0000");
+
+ assertEquals("$WP+GETLOCATION=0000", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java
new file mode 100644
index 000000000..5635ce3d4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WristbandProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WristbandProtocolDecoder decoder = new WristbandProtocolDecoder(null);
+
+ verifyNotNull(decoder, binary(
+ "000102004459583836383730343034343735303035357c56312e307c317c7b4630342331382c30372c332c3539303139322c33303a31382c30372c332c3539303139322c33307d0d0afffefc"));
+
+ verifyNotNull(decoder, binary(
+ "000102009c59583836383730343034343735303035357c56312e307c317c7b4630332330383a30353a38313a64383a31383a38372c2d37365f30303a37663a32383a63373a62613a63312c2d37375f39633a33643a63663a65643a62643a36622c2d36335f64383a65623a39373a65653a37373a32342c2d38327c31382c30372c332c3539303735342c33303a31382c30372c332c3539303735342c33307d0d0afffefc"));
+
+ verifyNull(decoder, binary(
+ "000102002259583836383730343034343735303035357c56312e307c307c7b46363423317d0d0afffefc"));
+
+ verifyPositions(decoder, binary(
+ "00010200bc59583836383730343034343735303035357c56312e307c317c7b4630322337372e3437373831372c2d33382e3839363239322c3230313831323239313235352c302e35387c37372e3437373739362c2d33382e3839363234352c3230313831323239313235352c302e30307c37372e3437373738392c2d33382e3839363233322c3230313831323239313235352c302e30307c37372e3437373737362c2d33382e3839363232322c3230313831323239313235352c302e30307d0d0afffefc"));
+
+ verifyNull(decoder, binary(
+ "000102004759583836383730343034343735303035357c56312e307c317c7b463931233331305f30307c30307c30307c30307c57414e444149323031382f31322f31342031353a35367d0d0afffefc"));
+
+ verifyNull(decoder, binary(
+ "000102004159583336373535313631303030303934347c56312e307c317c7b4639312330317c30307c30307c33475f7065745f323031382f30352f31362031313a30307d0d0afffefc"));
+
+ verifyPositions(decoder, false, binary(
+ "000102003559583836383730343034343735303035357c56312e307c317c7b4630312339342c312c3130302c302c33313030302c3930307d0d0afffefc"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
new file mode 100644
index 000000000..8fc628bdb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class XexunFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ XexunFrameDecoder decoder = new XexunFrameDecoder();
+
+ assertEquals(
+ binary("4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c"),
+ decoder.decode(null, null, binary("313531303132313435392c2b33313635323435343932372c4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c31323249")));
+
+ assertEquals(
+ binary("4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c"),
+ decoder.decode(null, null, binary("3135303631353132333733312c2b33333634373338343631312c4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c30342c333532322e392c463a332e3732562c302c3134322c32313734342c3230382c30312c303730322c394338430a0d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java
new file mode 100644
index 000000000..8b0f245a2
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java
@@ -0,0 +1,118 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class XexunProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ XexunProtocolDecoder decoder = new XexunProtocolDecoder(null, false);
+
+ verifyAttributes(decoder, text(
+ "GPRMC,.000,A,0.000000,S,0.0000,W,0.00,0.00,,00,0000.0,A*55,L,,imei:353579010727036,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,113518.000,A,5303.4150,N,10.2368,E,60.73,207.42,260216,00,0000.0,A*74,F,,imei:351525018007873,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,215853.000,A,5304.9600,N,6.7907,E,1.43,80.67,250216,00,0000.0,A*47,F,,imei:351525018007873,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,121535.000,A,5417.2666,N,04822.1264,E,1.452,30.42,031014,0.0,A*4D\r\n,L,imei:355227042011730,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,150120.000,A,3346.4463,S,15057.3083,E,0.0,117.4,010911,,,A*76,F,imei:351525010943661,"),
+ position("2011-09-01 15:01:20.000", true, -33.77411, 150.95514));
+
+ verifyPosition(decoder, text(
+ "GPRMC,010203.000,A,0102.0003,N,00102.0003,E,1.02,1.02,010203,,,A*00,F,,imei:10000000000000,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,233842.000,A,5001.3060,N,01429.3243,E,0.00,,210211,,,A*74,F,imei:354776030495631,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,080303.000,A,5546.7313,N,03738.6005,E,0.56,160.13,100311,,,A*6A,L,imei:354778030461167,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,220828.678,A,5206.1446,N,02038.2403,,0,0,160912,,,E*23,L,imei:358948012501019,"));
+
+ verifyPosition(decoder, text(
+ "GNRMC,134418.000,A,5533.8973,N,03745.4398,E,0.00,308.85,160215,,,A*7A,F,, imei:864244028033115,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,093341.000,A,1344.5716,N,10033.6648,E,0.00,0.00,240215,,,A*68,F,,imei:865328028306149,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,103731.636,A,4545.5266,N,00448.8259,E,21.12,276.01,150615,,,A*57,L,, imei:013949002026675,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,014623.000,A,4710.8260,N,1948.1220,E,0.11,105.40,111212,00,0000.0,A*49,F,,imei:357713002048962,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,043435.000,A,811.299200,S,11339.9500,E,0.93,29.52,160313,00,0000.0,A*65,F,,imei:359585014597923,"));
+
+ decoder = new XexunProtocolDecoder(null, true);
+
+ verifyPosition(decoder, text(
+ "171007160505,,GPRMC,160505.000,A,5323.4680,N,00252.4202,W,000.0,129.7,071017,,,A*7A,F,ACCStart, imei:864504031916915,10,41.1,F:4.28V,1,135,19824,234,15,0062,B7D5"));
+
+ verifyPosition(decoder, text(
+ "171007160525,,GPRMC,160525.000,A,5323.4680,N,00252.4202,W,000.0,129.7,071017,,,A*78,F,ACCStop, imei:864504031916915,10,41.1,F:4.28V,1,134,42896,234,15,0062,B7D5"));
+
+ verifyPosition(decoder, text(
+ "170505103845,TELKOMSEL,GPRMC,103845.000,A,0340.2482,N,09841.9689,E,0.00,68.23,050517,,,A*5D,F,ACC On, imei:013227002782161,05,-8.2,F:4.22V,1,141,44712,510,10,2BE5,EC47"));
+
+ verifyPosition(decoder, text(
+ "170607031932,+6282167979090,GPRMC,031932.000,A,0347.2515,N,09841.9433,E,0.00,261.22,070617,,,A*6C,F,ACC OFF, imei:013226004613135,11,23.1,F:4.25V,1,148,44989,510,10,2B34,0268"));
+
+ verifyNull(decoder, text(
+ ",+48606717068,,L,, imei:012207005047292,,,F:4.28V,1,52,11565,247,01,000E,1FC5"));
+
+ verifyPosition(decoder, text(
+ "130302125349,+79604870506,GPRMC,085349.000,A,4503.2392,N,03858.5660,E,6.95,154.65,020313,,,A*6C,F,, imei:012207007744243,03,-1.5,F:4.15V,1,139,28048,250,01,278A,5072"),
+ position("2013-03-02 08:53:49.000", true, 45.05399, 38.97610));
+
+ verifyPosition(decoder, text(
+ "111111120009,+436763737552,GPRMC,120009.590,A,4639.6774,N,01418.5737,E,0.00,0.00,111111,,,A*68,F,, imei:359853000144328,04,481.2,F:4.15V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,help me!, imei:123456789012345,04,481.2,F:4.15V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,help me!, imei:123456789012345,04,481.2,L:3.5V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,help me!, imei:123456789012345,04,481.2,L:3.5V,0,139,2689,232,03,2725,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+1234,GPRMC,204530.4,A,6000.0000,N,13000.0000,E,0.0,,010112,0.0,E,A*68,F,imei:123456789012345,04,123.5,F:3.55V,0,139,,232,03,272CE1,0576"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+1234,GPRMC,204530.4,A,6000.000,N,01000.6288,E,0.0,0.00,230713,0.0,E,A*3C,F,imei:123456789012345,00,,F:3.88V,0,125,,262,01,224CE1,379B"));
+
+ verifyPosition(decoder, text(
+ "111111120009,+1234,GPRMC,215840.7,A,6000.000,N,01000.6253,E,0.0,0.00,230713,0.0,E,A*34,F,imei:123456789012345,00,,F:3.9V,0,124,,262,01,224CE1,379B"));
+
+ verifyPosition(decoder, text(
+ "130725134142,,GPRMC,134142.591,A,3845.6283,N,00909.8876,W,2.08,287.33,250713,,,A*71,F,, imei:013227000526784,03,-50.7,L:3.69V,0,128,65337,268,03,177A,119F"));
+
+ verifyPosition(decoder, text(
+ "140602152533,TESCO_INFO,GPRMC,152533.000,A,5145.4275,N,00000.3448,E,0.00,0.00,020614,,,A*66,F,, imei:013227002781643,06,35.1,F:4.15V,1,135,38950,234,10,10B4,5235"));
+
+ verifyPosition(decoder, text(
+ "150216154418,5277,GNRMC,134418.000,A,5533.8973,N,03745.4398,E,0.00,308.85,160215,,,A*7A,F,, imei:864244028033115,10,169.8,F:4.28V,1,132,48269,250,99,6D0D,8572"));
+
+ verifyPosition(decoder, text(
+ "150224173341,+66961544651,GPRMC,093341.000,A,1344.5716,N,10033.6648,E,0.00,0.00,240215,,,A*68,F,,imei:865328028306149,05,106.4,F:4.01V/ADC1=0.20V/ADC2=0.00V,0,159,955,520,01,5DE8,0399,6.21km"));
+
+ verifyPosition(decoder, text(
+ "150316182840,07872167745,GPRMC,182840.000,A,5126.1310,N,00055.5573,W,0.00,0.00,160315,,,A*7C,F,,imei:865328023469306,06,54.3,F:4.10V/ADC1=0.76V/ADC2=0.00V,0,157,38486,234,10,34DC,48A6,3.70km"));
+
+ verifyPosition(decoder, text(
+ "150615123731,+33647384611,GPRMC,103731.636,A,4545.5266,N,00448.8259,E,21.12,276.01,150615,,,A*57,L,, imei:013949002026675,04,3522.9,F:3.72V,0,142,21744,208,01,0702,9C8C"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
new file mode 100644
index 000000000..d85364d22
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java
@@ -0,0 +1,79 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class XirgoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeCustom() throws Exception {
+
+ XirgoProtocolDecoder decoder = new XirgoProtocolDecoder(null);
+
+ decoder.setForm("UID,EV,D,T,LT,LN,AL,GSPT,HD,SV,HP,BV,CQ,GS,SI,IG,OT");
+
+ verifyNull(decoder, text(
+ "$$184800619,6115,Y1z1.1179AA2.3.7c79d34,,,000##"));
+
+ verifyPosition(decoder, text(
+ "$$183900034,4002,03/30/2019,02:15:22,46.848577,-114.022213,978,0.0,172.3,16,1.2,13.291,20,3,2,2,1##"));
+
+ verifyPosition(decoder, text(
+ "$$184800793,4002,03/30/2019,02:10:13,46.848600,-114.022256,9723,0.0,1645,17,1.2,13.283,18,3,89011703278246523594,2,0##"));
+
+ decoder.setForm("UID,EV,D,T,LT,LN,AL,GSPT,SV,HP,BV,CQ,MI,GS,SI,IG,OT");
+
+ verifyPosition(decoder, text(
+ "$$184800793,4002,03/15/2019,21:30:21,46.848582,-114.022237,9733,0.0,18,1.1,13.605,20,0,3,89011703278246523602,2,0##"));
+
+ }
+
+ @Test
+ public void testDecodeNew() throws Exception {
+
+ XirgoProtocolDecoder decoder = new XirgoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$352054058132185,4001,2017/04/21,00:01:05,32.54659,-116.90670,143.2,0,0,0,598,0.0,12,0.9,765840,7.0,14.5,19,1,1,0011,8.5,63.2,5,21999,184,255,671,207,100,185##"));
+
+ verifyPosition(decoder, text(
+ "$$352054058132185,6011,2017/04/21,04:57:10,32.49658,-116.85957,250.9,0,0,0,602,0.0,12,0.8,765876,7.0,14.1,21,1,1,0011,10.1,0.0,5,170917890,280,255,627,0,100,167##"));
+
+ verifyPosition(decoder, text(
+ "$$355922061611345,6001,2016/08/25,20:10:51,51.13042,-114.22752,1197,44.7,0.0,0.0,2622,27,12,0.8,1,0.0,13.9,24,1,0,0.0,-70,-809,688##"));
+
+ verifyPosition(decoder, text(
+ "$$355922061611345,6001,2016/08/25,20:10:38,51.12948,-114.22637,1203,34.8,0.0,0.0,1377,215,12,0.8,1,0.0,13.8,28,1,0,0.0,-309,-566,754##"));
+
+ verifyPosition(decoder, text(
+ "$$354898045650537,6031,2015/02/26,15:47:26,33.42552,-112.30308,287.8,0,0,0,0,0.0,7,1.2,2,0.0,12.2,22,1,0,82.3"));
+
+ verifyPosition(decoder, text(
+ "$$355922060162167,6015,2016/04/21,17:26:52,39.83267,-76.66139,230,0.0,0.0,0.0,779,0,8,1.2,0,0.0,13.0,19,1,1C4BJWDG4GL191009,X0z1-1137CD1,0402,3GATT,0,83.9,-70,-715,738##"));
+
+ verifyPosition(decoder, text(
+ "$$355922060162167,4002,2016/04/21,17:04:50,39.83253,-76.66102,232,0.0,0.0,0.0,0,0,12,1.2,0,0.0,9.2,15,1,0,0.0,35,-8,1059##"));
+
+ }
+
+ @Test
+ public void testDecodeOld() throws Exception {
+
+ XirgoProtocolDecoder decoder = new XirgoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$354660046140722,6001,2013/01/22,15:36:18,25.80907,-80.32531,7.1,19,165.2,11,0.8,11.1,17,1,1,3.9,2##"),
+ position("2013-01-22 15:36:18.000", true, 25.80907, -80.32531));
+
+ verifyPosition(decoder, text(
+ "$$357207059646786,4003,2015/05/19,15:54:56,-20.21422,-70.14927,37.5,1.8,0.0,11,0.8,12.9,31,297,1,0,0.0,0.0,0,1,1,1##"));
+
+ verifyPosition(decoder, text(
+ "$$354898045650537,6031,2015/02/26,15:47:26,33.42552,-112.30308,287.8,0,0,0,0,0.0,7,1.2,2,0.0,12.2,22,1,0,82.3"));
+
+ verifyPosition(decoder, text(
+ "$$357207059646786,4003,2015/05/19,15:55:27,-20.21421,-70.14920,33.6,0.4,0.0,11,0.8,12.9,31,297,1,0,0.0,0.0,0,1,1,1##"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
new file mode 100644
index 000000000..0ff47cad3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java
@@ -0,0 +1,26 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class XirgoProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ XirgoProtocolEncoder encoder = new XirgoProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 0);
+ command.set(Command.KEY_DATA, 1);
+
+ assertEquals("+XT:7005,2,1", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
new file mode 100644
index 000000000..20adacd6b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xrb28ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xrb28ProtocolDecoder decoder = new Xrb28ProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "*SCOR,OM,123456789123456,Q0,412,80,28#"));
+
+ verifyPosition(decoder, text(
+ "*SCOR,OM,867584030387299,D0,0,012102.00,A,0608.00062,S,10659.70331,E,12,0.69,151118,30.3,M,A#"));
+
+ verifyAttributes(decoder, text(
+ "*SCOR,OM,863158022988725,H0,0,412,28,80,0#"));
+
+ verifyAttributes(decoder, text(
+ "*HBCR,OM,123456789123456,R0,0,55,1234,1497689816#"));
+
+ verifyPosition(decoder, text(
+ "*HBCR,OM,123456789123456,D0,0,124458.00,A,2237.7514,N,11408.6214,E,6,0.21,151216,10,M,A#"));
+
+ verifyPosition(decoder, text(
+ "*SCOR,OM,863158022988725,D0,0,124458.00,A,2237.7514,N,11408.6214,E,6,0.21,151216,10,M,A#"));
+
+ verifyPosition(decoder, text(
+ "*SCOR,NG,868020030308430,D0,1,020455.00,A,2359.36129,S,04615.24677,W,12,0.72,201119,8.5,M,A#"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
new file mode 100644
index 000000000..27efbd6db
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class Xrb28ProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodePositionPeriodic() {
+
+ Xrb28ProtocolEncoder encoder = new Xrb28ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_PERIODIC);
+ command.set(Command.KEY_FREQUENCY, 300);
+
+ assertEquals("\u00ff\u00ff*SCOS,OM,123456789012345,D1,300#\n", encoder.encodeCommand(null, command));
+
+ }
+
+ @Test
+ public void testEncodeCustom() {
+
+ Xrb28ProtocolEncoder encoder = new Xrb28ProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "S7,0,3,0,0,20,25");
+
+ assertEquals("\u00ff\u00ff*SCOS,OM,123456789012345,S7,0,3,0,0,20,25#\n", encoder.encodeCommand(null, command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java
new file mode 100644
index 000000000..aa44929ab
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xt013ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xt013ProtocolDecoder decoder = new Xt013ProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "TK,862950021650364,150131090859,+53.267863,+5.767363,0,38,12,0,F,204,08,C94,336C,24,,4.09,1,,,,,,,,"),
+ position("2015-01-31 09:08:59.000", true, 53.26786, 5.76736));
+
+ verifyPosition(decoder, text(
+ "TK,862950021650364,150118113832,+53.267722,+5.767143,0,86,12,0,F,204,08,C94,336C,22,,4.21,1,,,,,,,,"));
+
+ verifyPosition(decoder, text(
+ "HI,862950021650364TK,862950021650364,150118113832,+53.267722,+5.767143,0,86,12,0,F,204,08,C94,336C,22,,4.21,1,,,,,,,,"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
new file mode 100644
index 000000000..6d49f2516
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xt2400ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xt2400ProtocolDecoder decoder = new Xt2400ProtocolDecoder(null);
+
+ decoder.setConfig("\n::wycfg pcr[1] 012801030405060708090a1213c8545657585a656e7d2cd055595d5e71797a7b7c7e7f80818285866b\n");
+
+ verifyPosition(decoder, binary(
+ "010ae85be10801a05d52d590030b12d1f9330be9290a0000ff10008b00000000000000000000000000000000000000000000000000000000000000000000000000003839333032363930323031303036363039373733000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000"));
+
+ decoder.setConfig("\n:wycfg pcr[0] 001001030406070809570a13121714100565\n");
+
+ verifyPosition(decoder, binary(
+ "000a344f1f0259766ae002074289f8f1c4b200e80000026712068000130000029300883559464255524845364650323433343235"));
+
+ decoder.setConfig("\n:wycfg pcr[0] 000f01030406070809570a131217141005\n");
+
+ verifyPosition(decoder, binary(
+ "0009c4fb9b0b58a771e4020742d9f8f1c4c300bc0000000011077c0015000000000001"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java
new file mode 100644
index 000000000..332d15fa5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java
@@ -0,0 +1,31 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class YwtProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ YwtProtocolDecoder decoder = new YwtProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "%RP,1222102985:1,170509033842,E102.146563,N14.582175,,0,320,10,0,00-00-00-00-00-00-00-00-00-00-00-00,,1db2-02b3-52004,3>941.523-32,7>1,19>-16,20>30.9V"));
+
+ verifyNull(decoder, text(
+ "%SN,0417061042:0,0,140117041203,404"));
+
+ verifyPosition(decoder, text(
+ "%GP,3000012345:0,090723182813,E114.602345,N22.069725,,30,160,4,0,00,,2794-10FF-46000,3>0-0"));
+
+ verifyPosition(decoder, text(
+ "%RP,3000012345:0,090807182815,E114.602345,N22.069725,,30,160,4,0,00"),
+ position("2009-08-07 18:28:15.000", true, 22.06973, 114.60235));
+
+ verifyPosition(decoder, text(
+ "%KP,3000012345:0,090807183115,E114.602345,N22.069725,,30,160,5,0,00;"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/reports/ReportUtilsTest.java b/src/test/java/org/traccar/reports/ReportUtilsTest.java
new file mode 100644
index 000000000..01b9c276f
--- /dev/null
+++ b/src/test/java/org/traccar/reports/ReportUtilsTest.java
@@ -0,0 +1,390 @@
+package org.traccar.reports;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.junit.Test;
+import org.traccar.BaseTest;
+import org.traccar.TestIdentityManager;
+import org.traccar.model.Position;
+import org.traccar.reports.model.StopReport;
+import org.traccar.reports.model.TripReport;
+import org.traccar.reports.model.TripsConfig;
+
+public class ReportUtilsTest extends BaseTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ private Position position(String time, double speed, double totalDistance) throws ParseException {
+
+ Position position = new Position();
+
+ position.setTime(date(time));
+ position.setValid(true);
+ position.setSpeed(speed);
+ position.set(Position.KEY_TOTAL_DISTANCE, totalDistance);
+
+ return position;
+ }
+
+ @Test
+ public void testCalculateDistance() {
+ Position startPosition = new Position();
+ startPosition.set(Position.KEY_TOTAL_DISTANCE, 500.0);
+ Position endPosition = new Position();
+ endPosition.set(Position.KEY_TOTAL_DISTANCE, 700.0);
+ assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 200.0, 10);
+ startPosition.set(Position.KEY_ODOMETER, 50000);
+ endPosition.set(Position.KEY_ODOMETER, 51000);
+ assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 1000.0, 10);
+ }
+
+ @Test
+ public void testCalculateSpentFuel() {
+ Position startPosition = new Position();
+ Position endPosition = new Position();
+ assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), 0.0, 0.01);
+ startPosition.set(Position.KEY_FUEL_LEVEL, 0.7);
+ endPosition.set(Position.KEY_FUEL_LEVEL, 0.5);
+ assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), 0.2, 0.01);
+ }
+
+ @Test
+ public void testDetectTripsSimple() throws ParseException {
+
+ List<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 0, 3000),
+ position("2016-01-01 00:06:00.000", 0, 3000),
+ position("2016-01-01 00:07:00.000", 0, 3000));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ Iterator<StopReport> iterator = stops.iterator();
+
+ StopReport itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectTripsSimpleWithIgnition() throws ParseException {
+
+ List<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 0, 3000),
+ position("2016-01-01 00:06:00.000", 0, 3000),
+ position("2016-01-01 00:07:00.000", 0, 3000));
+
+ data.get(5).set(Position.KEY_IGNITION, false);
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, true, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemTrip.getEndTime());
+ assertEquals(180000, itemTrip.getDuration());
+ assertEquals(10, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(3000, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ Iterator<StopReport> iterator = stops.iterator();
+
+ StopReport itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectTripsWithFluctuation() throws ParseException {
+
+ List<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 10, 3000),
+ position("2016-01-01 00:06:00.000", 10, 4000),
+ position("2016-01-01 00:07:00.000", 0, 5000),
+ position("2016-01-01 00:08:00.000", 10, 6000),
+ position("2016-01-01 00:09:00.000", 0, 7000),
+ position("2016-01-01 00:10:00.000", 0, 7000),
+ position("2016-01-01 00:11:00.000", 0, 7000));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:09:00.000"), itemTrip.getEndTime());
+ assertEquals(420000, itemTrip.getDuration());
+ assertEquals(8.57, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(10, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(7000, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ Iterator<StopReport> iterator = stops.iterator();
+
+ StopReport itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ itemStop = iterator.next();
+
+ assertEquals(date("2016-01-01 00:09:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:11:00.000"), itemStop.getEndTime());
+ assertEquals(120000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsOnly() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 1, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 1, 0),
+ position("2016-01-01 00:05:00.000", 0, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(300000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsWithTripCut() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 0, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 1, 0),
+ position("2016-01-01 00:05:00.000", 2, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getEndTime());
+ assertEquals(240000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsStartedFromTrip() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 2, 0),
+ position("2016-01-01 00:01:00.000", 1, 0),
+ position("2016-01-01 00:02:00.000", 0, 0),
+ position("2016-01-01 00:03:00.000", 0, 0),
+ position("2016-01-01 00:04:00.000", 0, 0),
+ position("2016-01-01 00:05:00.000", 0, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ StopReport itemStop = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime());
+ assertEquals(180000, itemStop.getDuration());
+
+ }
+
+ @Test
+ public void testDetectStopsMoving() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 5, 0),
+ position("2016-01-01 00:01:00.000", 5, 0),
+ position("2016-01-01 00:02:00.000", 3, 0),
+ position("2016-01-01 00:03:00.000", 5, 0),
+ position("2016-01-01 00:04:00.000", 5, 0),
+ position("2016-01-01 00:05:00.000", 5, 0));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01);
+
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(result);
+ assertTrue(result.isEmpty());
+
+ }
+
+ @Test
+ public void testDetectTripAndStopByGap() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 7, 100),
+ position("2016-01-01 00:01:00.000", 7, 300),
+ position("2016-01-01 00:02:00.000", 5, 500),
+ position("2016-01-01 00:03:00.000", 5, 600),
+ position("2016-01-01 00:04:00.000", 3, 700),
+ position("2016-01-01 00:23:00.000", 2, 700),
+ position("2016-01-01 00:24:00.000", 5, 800),
+ position("2016-01-01 00:25:00.000", 5, 900));
+
+ TripsConfig tripsConfig = new TripsConfig(500, 200000, 200000, 900000, false, false, 0.01);
+
+ Collection<TripReport> trips = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, TripReport.class);
+
+ assertNotNull(trips);
+ assertFalse(trips.isEmpty());
+
+ TripReport itemTrip = trips.iterator().next();
+
+ assertEquals(date("2016-01-01 00:00:00.000"), itemTrip.getStartTime());
+ assertEquals(date("2016-01-01 00:04:00.000"), itemTrip.getEndTime());
+ assertEquals(240000, itemTrip.getDuration());
+ assertEquals(6.75, itemTrip.getAverageSpeed(), 0.01);
+ assertEquals(7, itemTrip.getMaxSpeed(), 0.01);
+ assertEquals(600, itemTrip.getDistance(), 0.01);
+
+ Collection<StopReport> stops = ReportUtils.detectTripsAndStops(
+ new TestIdentityManager(), null, data, tripsConfig, false, StopReport.class);
+
+ assertNotNull(stops);
+ assertFalse(stops.isEmpty());
+
+ StopReport itemStop = stops.iterator().next();
+
+ assertEquals(date("2016-01-01 00:04:00.000"), itemStop.getStartTime());
+ assertEquals(date("2016-01-01 00:24:00.000"), itemStop.getEndTime());
+ assertEquals(1200000, itemStop.getDuration());
+ }
+
+}
diff --git a/src/test/java/org/traccar/web/WebServerTest.java b/src/test/java/org/traccar/web/WebServerTest.java
new file mode 100644
index 000000000..5a79fbac2
--- /dev/null
+++ b/src/test/java/org/traccar/web/WebServerTest.java
@@ -0,0 +1,29 @@
+package org.traccar.web;
+
+import org.junit.Test;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class WebServerTest {
+
+ @Test
+ public void contextTest() throws NamingException {
+ DataSource mockDataSource = (DataSource) Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[] { DataSource.class }, new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+
+ Context context = new InitialContext();
+ context.bind("java:/DefaultDS", mockDataSource);
+ }
+
+}