aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--build.gradle43
-rw-r--r--gradle/checkstyle.xml15
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--pom.xml70
-rw-r--r--schema/changelog-4.7.xml28
-rw-r--r--schema/changelog-master.xml1
-rw-r--r--setup/README.txt3
-rw-r--r--setup/default.xml14
-rw-r--r--setup/environment.sh26
-rwxr-xr-xsetup/package.sh4
-rw-r--r--setup/traccar.iss2
-rw-r--r--setup/traccar.service3
-rw-r--r--src/main/java/org/traccar/Main.java58
-rw-r--r--src/main/java/org/traccar/MainModule.java4
-rw-r--r--src/main/java/org/traccar/StringProtocolEncoder.java30
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java2
-rw-r--r--src/main/java/org/traccar/api/HealthCheckService.java91
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java2
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java60
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java29
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java118
-rw-r--r--src/main/java/org/traccar/database/DriversManager.java51
-rw-r--r--src/main/java/org/traccar/database/ExtendedObjectManager.java78
-rw-r--r--src/main/java/org/traccar/database/GroupsManager.java5
-rw-r--r--src/main/java/org/traccar/database/SimpleObjectManager.java30
-rw-r--r--src/main/java/org/traccar/database/UsersManager.java6
-rw-r--r--src/main/java/org/traccar/geocoder/FactualGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/HereGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/MapQuestGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/OpenCageGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java7
-rw-r--r--src/main/java/org/traccar/helper/Checksum.java8
-rw-r--r--src/main/java/org/traccar/helper/LogAction.java12
-rw-r--r--src/main/java/org/traccar/helper/ServletHelper.java45
-rw-r--r--src/main/java/org/traccar/model/Command.java1
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java179
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java82
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocol.java9
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java80
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java108
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java104
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocolDecoder.java170
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocolEncoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolDecoder.java27
-rw-r--r--src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java18
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java87
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java29
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java80
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/Jt600FrameDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java22
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java17
-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/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/OutsafeProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java69
-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/PluginProtocolDecoder.java62
-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/Pt502ProtocolEncoder.java8
-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/RstProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java132
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java40
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java13
-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/SigfoxProtocolDecoder.java188
-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/StarcomProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/SuntechFrameDecoder.java23
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java168
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java30
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java34
-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/TotemProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java59
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocolDecoder.java23
-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/WatchProtocolDecoder.java15
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolEncoder.java22
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolEncoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java2
-rw-r--r--src/main/proto/OmnicommMessage.proto464
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java4
-rw-r--r--src/test/java/org/traccar/helper/ChecksumTest.java10
-rw-r--r--src/test/java/org/traccar/helper/ServletHelperTest.java65
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java38
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java (renamed from src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java)22
-rw-r--r--src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java34
-rw-r--r--src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java18
-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/OutsafeProtocolDecoderTest.java22
-rw-r--r--src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java32
-rw-r--r--src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java8
-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/RaceDynamicsProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java29
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java11
-rw-r--r--src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java120
-rw-r--r--src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java38
-rw-r--r--src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java3
-rw-r--r--swagger.json5
m---------traccar-web0
182 files changed, 5685 insertions, 690 deletions
diff --git a/.gitignore b/.gitignore
index c005ef9e4..f2672aada 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ nbactions.xml
.gradle
out
build
+src/main/java/org/traccar/protobuf
diff --git a/build.gradle b/build.gradle
index 0842a6a0f..6aa6af8a7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,36 +1,46 @@
plugins {
id "java"
id "checkstyle"
+ id "com.google.protobuf" version "0.8.11"
}
repositories {
mavenCentral()
}
+ext {
+ guiceVersion = "4.2.2"
+ jettyVersion = "9.4.26.v20200117"
+ jerseyVersion = "2.30"
+ jacksonVersion = "2.9.9" // same version as jersey-media-json-jackson dependency
+ protobufVersion = "3.11.1"
+}
+
sourceCompatibility = "1.8"
compileJava.options.encoding = "UTF-8"
checkstyle {
+ toolVersion = "8.26"
configFile = "gradle/checkstyle.xml" as File
checkstyleTest.enabled = false
}
-ext {
- guiceVersion = "4.2.2"
- jettyVersion = "9.4.20.v20190813"
- jerseyVersion = "2.29"
- jacksonVersion = "2.9.9" // same version as jersey-media-json-jackson dependency
+protobuf {
+ generatedFilesBaseDir = "$projectDir/src"
+ protoc {
+ artifact = "com.google.protobuf:protoc:$protobufVersion"
+ }
}
dependencies {
implementation "commons-codec:commons-codec:1.13"
- implementation "com.h2database:h2:1.4.199"
- implementation "mysql:mysql-connector-java:8.0.17"
- implementation "org.postgresql:postgresql:42.2.6"
+ implementation "com.h2database:h2:1.4.200"
+ implementation "mysql:mysql-connector-java:8.0.18"
+ implementation "org.postgresql:postgresql:42.2.9"
implementation "com.microsoft.sqlserver:mssql-jdbc:7.4.1.jre8"
- implementation "com.zaxxer:HikariCP:3.3.1"
- implementation "io.netty:netty-all:4.1.39.Final"
- implementation "org.slf4j:slf4j-jdk14:1.7.28"
+ implementation "com.zaxxer:HikariCP:3.4.2"
+ implementation "io.netty:netty-all:4.1.44.Final"
+ implementation "org.slf4j:slf4j-jdk14:1.7.30"
implementation "com.google.inject:guice:$guiceVersion"
implementation "com.google.inject.extensions:guice-assistedinject:$guiceVersion"
implementation "org.owasp.encoder:encoder:1.2.2"
@@ -46,7 +56,7 @@ dependencies {
implementation "org.glassfish.jersey.inject:jersey-hk2:$jerseyVersion"
implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jacksonVersion"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr353:$jacksonVersion"
- implementation "org.liquibase:liquibase-core:3.8.0", {
+ implementation "org.liquibase:liquibase-core:3.8.5", {
exclude group: "ch.qos.logback", module: "logback-classic"
}
implementation "com.sun.mail:javax.mail:1.6.2"
@@ -57,13 +67,14 @@ dependencies {
implementation "org.apache.commons:commons-collections4:4.4"
implementation "org.mnode.ical4j:ical4j:2.0.5" // needs upgrade
implementation "com.fizzed:ch-smpp:6.0.0-netty4-beta-3"
- implementation "net.java.dev.jna:jna-platform:5.4.0"
- implementation "com.github.jnr:jnr-posix:3.0.50"
+ implementation "net.java.dev.jna:jna-platform:5.5.0"
+ implementation "com.github.jnr:jnr-posix:3.0.51"
+ implementation "com.google.protobuf:protobuf-java:$protobufVersion"
implementation "javax.xml.bind:jaxb-api:2.3.1"
implementation "com.sun.xml.bind:jaxb-core:2.3.0.1"
implementation "com.sun.xml.bind:jaxb-impl:2.3.2"
implementation "javax.activation:activation:1.1.1"
- testImplementation "junit:junit:4.12"
+ testImplementation "junit:junit:4.13"
}
task copyDependencies(type: Copy) {
@@ -80,7 +91,7 @@ jar {
manifest {
attributes(
"Main-Class": "org.traccar.Main",
- "Implementation-Version": "4.6",
+ "Implementation-Version": "4.8",
"Class-Path": configurations.runtimeClasspath.files.collect { "lib/$it.name" }.join(" "))
}
}
diff --git a/gradle/checkstyle.xml b/gradle/checkstyle.xml
index d85100471..e4df54d8c 100644
--- a/gradle/checkstyle.xml
+++ b/gradle/checkstyle.xml
@@ -5,6 +5,13 @@
<module name="Checker">
+ <module name="SuppressWarningsFilter"/>
+
+ <module name="SuppressionSingleFilter">
+ <property name="files" value="[/\\]protobuf[/\\]"/>
+ <property name="checks" value=".*"/>
+ </module>
+
<!--<property name="fileExtensions" value="java, properties, xml"/>-->
<!-- Checks whether files end with a new line. -->
@@ -20,6 +27,9 @@
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="FileLength"/>
+ <module name="LineLength">
+ <property name="max" value="120"/>
+ </module>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
@@ -43,6 +53,8 @@
<module name="TreeWalker">
+ <module name="SuppressWarningsHolder"/>
+
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
@@ -64,9 +76,6 @@
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
- <module name="LineLength">
- <property name="max" value="120"/>
- </module>
<module name="MethodLength">
<property name="max" value="200"/>
</module>
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b8a51fe20..31a0802f3 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/pom.xml b/pom.xml
index 3562ef149..535a20e6d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.traccar</groupId>
<artifactId>traccar</artifactId>
- <version>4.6-SNAPSHOT</version>
+ <version>4.8-SNAPSHOT</version>
<name>traccar</name>
<url>https://www.traccar.org</url>
@@ -12,16 +12,17 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<guice.version>4.2.2</guice.version>
- <jetty.version>9.4.20.v20190813</jetty.version>
- <jersey.version>2.29</jersey.version>
+ <jetty.version>9.4.26.v20200117</jetty.version>
+ <jersey.version>2.30</jersey.version>
<jackson.version>2.9.9</jackson.version> <!-- same version as jersey-media-json-jackson dependency -->
+ <protobuf.version>3.11.1</protobuf.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.12</version>
+ <version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -32,17 +33,17 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
- <version>1.4.199</version>
+ <version>1.4.200</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
- <version>8.0.17</version>
+ <version>8.0.18</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
- <version>42.2.6</version>
+ <version>42.2.9</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
@@ -52,17 +53,17 @@
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
- <version>3.3.1</version>
+ <version>3.4.2</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
- <version>4.1.39.Final</version>
+ <version>4.1.44.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
- <version>1.7.28</version>
+ <version>1.7.30</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
@@ -152,7 +153,7 @@
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
- <version>3.8.0</version>
+ <version>3.8.5</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
@@ -203,12 +204,17 @@
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
- <version>5.4.0</version>
+ <version>5.5.0</version>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-posix</artifactId>
- <version>3.0.50</version>
+ <version>3.0.51</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.protobuf</groupId>
+ <artifactId>protobuf-java</artifactId>
+ <version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
@@ -240,7 +246,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
- <version>1.7.28</version>
+ <version>1.7.30</version>
</dependency>
</dependencies>
@@ -250,22 +256,6 @@
<plugins>
<plugin>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>3.0.0</version>
- <configuration>
- <configLocation>gradle/checkstyle.xml</configLocation>
- </configuration>
- <executions>
- <execution>
- <id>checkstyle</id>
- <phase>validate</phase>
- <goals>
- <goal>check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
@@ -344,6 +334,26 @@
</dependencies>
</plugin>
<plugin>
+ <groupId>com.github.os72</groupId>
+ <artifactId>protoc-jar-maven-plugin</artifactId>
+ <version>3.8.0</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <protocArtifact>com.google.protobuf:protoc:${protobuf.version}</protocArtifact>
+ <inputDirectories>
+ <include>src/main/proto</include>
+ </inputDirectories>
+ <outputDirectory>src/main/java</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
diff --git a/schema/changelog-4.7.xml b/schema/changelog-4.7.xml
new file mode 100644
index 000000000..ca42aa6f9
--- /dev/null
+++ b/schema/changelog-4.7.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-4.7">
+
+ <changeSet author="author" id="changelog-4.7">
+
+ <preConditions onFail="MARK_RAN">
+ <not>
+ <indexExists indexName="user_device_user_id" />
+ </not>
+ </preConditions>
+
+ <createIndex tableName="tc_user_device" indexName="user_device_user_id">
+ <column name="userid" />
+ </createIndex>
+
+ <createIndex tableName="tc_positions" indexName="position_deviceid_fixtime">
+ <column name="deviceid" />
+ <column name="fixtime" />
+ </createIndex>
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index f564c3d68..5d770c4aa 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -23,5 +23,6 @@
<include file="changelog-4.0.xml" relativeToChangelogFile="true" />
<include file="changelog-4.1.xml" relativeToChangelogFile="true" />
+ <include file="changelog-4.7.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/README.txt b/setup/README.txt
index c50b392e2..ea8ea7a84 100644
--- a/setup/README.txt
+++ b/setup/README.txt
@@ -5,9 +5,6 @@ Installation instructions are available on the official website:
Windows - https://www.traccar.org/windows/
Linux - https://www.traccar.org/linux/
Docker - https://www.traccar.org/docker/
-OS X - https://www.traccar.org/mac-os/
-OpenBSD - https://www.traccar.org/openbsd/
-FreeBSD - https://www.traccar.org/freebsd/
Other - https://www.traccar.org/manual-installation/
If you have any questions or problems visit support page:
diff --git a/setup/default.xml b/setup/default.xml
index c5046ae83..561b57a57 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -14,6 +14,7 @@
<entry key='web.port'>8082</entry>
<entry key='web.path'>./web</entry>
<entry key='web.cacheControl'>max-age=3600,public</entry>
+ <entry key='web.healthCheck'>true</entry>
<entry key='geocoder.enable'>false</entry>
<entry key='geocoder.type'>google</entry>
@@ -271,5 +272,18 @@
<entry key='plugin.port'>5192</entry>
<entry key='leafspy.port'>5193</entry>
<entry key='naviset.port'>5194</entry>
+ <entry key='racedynamics.port'>5195</entry>
+ <entry key='rst.port'>5196</entry>
+ <entry key='pt215.port'>5197</entry>
+ <entry key='pacifictrack.port'>5198</entry>
+ <entry key='topin.port'>5199</entry>
+ <entry key='outsafe.port'>5200</entry>
+ <entry key='solarpowered.port'>5201</entry>
+ <entry key='motor.port'>5202</entry>
+ <entry key='omnicomm.port'>5203</entry>
+ <entry key='s168.port'>5204</entry>
+ <entry key='vnet.port'>5205</entry>
+ <entry key='blue.port'>5206</entry>
+ <entry key='pst.port'>5207</entry>
</properties>
diff --git a/setup/environment.sh b/setup/environment.sh
new file mode 100644
index 000000000..5c2c16507
--- /dev/null
+++ b/setup/environment.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+add-apt-repository ppa:openjdk-r/ppa
+apt update
+apt install openjdk-11-jdk zip unzip innoextract wine makeself
+
+# /usr/bin/printf '\xfe\xed\xfe\xed\x00\x00\x00\x02\x00\x00\x00\x00\xe2\x68\x6e\x45\xfb\x43\xdf\xa4\xd9\x92\xdd\x41\xce\xb6\xb2\x1c\x63\x30\xd7\x92' > /etc/ssl/certs/java/cacerts
+# /var/lib/dpkg/info/ca-certificates-java.postinst configure
+
+git clone --recurse-submodules https://github.com/traccar/traccar.git
+(cd traccar/traccar-web && git checkout master)
+(cd traccar && ./gradlew assemble)
+
+wget http://cdn.sencha.com/ext/gpl/ext-6.2.0-gpl.zip
+unzip ext-*-gpl.zip ; rm ext-*-gpl.zip
+
+wget http://cdn.sencha.com/cmd/7.1.0.15/no-jre/SenchaCmd-7.1.0.15-linux-i386.sh.zip
+unzip SenchaCmd-*.zip ; rm SenchaCmd-*.zip
+./SenchaCmd-*.sh -q ; rm SenchaCmd-*
+export PATH=$PATH:~/bin/Sencha/Cmd/
+
+cd traccar/setup
+wget http://files.jrsoftware.org/is/5/isetup-5.5.6.exe
+wget https://github.com/ojdkbuild/ojdkbuild/releases/download/java-11-openjdk-debug-11.0.6.10-1/java-11-openjdk-debug-11.0.6.10-1.windows.ojdkbuild.x86_64.zip
+wget https://github.com/ojdkbuild/contrib_jdk11u-ci/releases/download/jdk-11.0.5%2B10/jdk-11.0.5-ojdkbuild-linux-x64.zip
+wget https://github.com/ojdkbuild/contrib_jdk11u-arm32-ci/releases/download/jdk-11.0.5%2B10/jdk-11.0.5-ojdkbuild-linux-armhf.zip
diff --git a/setup/package.sh b/setup/package.sh
index 861b9a51d..7f23069c5 100755
--- a/setup/package.sh
+++ b/setup/package.sh
@@ -64,7 +64,7 @@ if [ $PLATFORM != "other" ]; then
fi
if [ $PLATFORM = "all" -o $PLATFORM = "windows-64" ]; then
check_requirement "Inno Extractor" "which innoextract" "Missing innoextract binary"
- check_requirement "Inno Setup" "ls innosetup-*.exe" "Missing Inno Setup (http://www.jrsoftware.org/isdl.php)"
+ check_requirement "Inno Setup" "ls i*setup-*.exe" "Missing Inno Setup (http://www.jrsoftware.org/isdl.php)"
check_requirement "Windows 64 Java" "ls java-*.windows.ojdkbuild.x86_64.zip" "Missing Windows 64 Java (https://github.com/ojdkbuild/ojdkbuild)"
check_requirement "Wine" "which wine" "Missing wine binary"
fi
@@ -100,7 +100,7 @@ prepare () {
cp traccar.xml out/conf
if [ $PLATFORM = "all" -o $PLATFORM = "windows-64" ]; then
- innoextract innosetup-*.exe >/dev/null
+ innoextract i*setup-*.exe >/dev/null
info "If you got any errors here try Inno Setup version 5.5.5 (or check supported versions using 'innoextract -v')"
fi
}
diff --git a/setup/traccar.iss b/setup/traccar.iss
index 9a4fc65b9..6dcad8e4d 100644
--- a/setup/traccar.iss
+++ b/setup/traccar.iss
@@ -1,6 +1,6 @@
[Setup]
AppName=Traccar
-AppVersion=4.6
+AppVersion=4.8
DefaultDirName={pf}\Traccar
OutputBaseFilename=traccar-setup
ArchitecturesInstallIn64BitMode=x64
diff --git a/setup/traccar.service b/setup/traccar.service
index fe746dda8..6ae24ab8a 100644
--- a/setup/traccar.service
+++ b/setup/traccar.service
@@ -8,6 +8,9 @@ WorkingDirectory=/opt/traccar
ExecStart=/opt/traccar/jre/bin/java -jar tracker-server.jar conf/traccar.xml
SyslogIdentifier=traccar
SuccessExitStatus=143
+WatchdogSec=100
+Restart=on-failure
+RestartSec=10
[Install]
WantedBy=multi-user.target
diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java
index 6ebd1d399..47d6e91df 100644
--- a/src/main/java/org/traccar/Main.java
+++ b/src/main/java/org/traccar/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 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.
@@ -19,6 +19,7 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.api.HealthCheckService;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
@@ -105,6 +106,27 @@ public final class Main {
}
}
+ private static void scheduleHealthCheck() {
+ HealthCheckService service = new HealthCheckService();
+ if (service.isEnabled()) {
+ new Timer().scheduleAtFixedRate(
+ service.createTask(), service.getPeriod(), service.getPeriod());
+ }
+ }
+
+ private static void scheduleDatabaseCleanup() {
+ 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);
+ }
+
public static void run(String configFile) {
try {
Context.init(configFile);
@@ -118,35 +140,19 @@ public final class Main {
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);
+ scheduleHealthCheck();
+ scheduleDatabaseCleanup();
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- LOGGER.error("Thread exception", e);
- }
- });
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e));
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- LOGGER.info("Shutting down server...");
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ LOGGER.info("Shutting down server...");
- if (Context.getWebServer() != null) {
- Context.getWebServer().stop();
- }
- Context.getServerManager().stop();
+ 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/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 1d5508f5a..c3c0182d7 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -171,7 +171,7 @@ public class MainModule extends AbstractModule {
case "ban":
return new BanGeocoder(cacheSize, addressFormat);
case "here":
- return new HereGeocoder(id, key, language, cacheSize, addressFormat);
+ return new HereGeocoder(url, id, key, language, cacheSize, addressFormat);
case "mapmyindia":
return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat);
default:
@@ -192,7 +192,7 @@ public class MainModule extends AbstractModule {
case "google":
return new GoogleGeolocationProvider(key);
case "opencellid":
- return new OpenCellIdGeolocationProvider(key);
+ return new OpenCellIdGeolocationProvider(url, key);
case "unwired":
return new UnwiredGeolocationProvider(url, key);
default:
diff --git a/src/main/java/org/traccar/StringProtocolEncoder.java b/src/main/java/org/traccar/StringProtocolEncoder.java
index d9acce7f0..e9fb65500 100644
--- a/src/main/java/org/traccar/StringProtocolEncoder.java
+++ b/src/main/java/org/traccar/StringProtocolEncoder.java
@@ -17,8 +17,6 @@ package org.traccar;
import org.traccar.model.Command;
-import java.util.Map;
-
public abstract class StringProtocolEncoder extends BaseProtocolEncoder {
public StringProtocolEncoder(Protocol protocol) {
@@ -31,21 +29,27 @@ public abstract class StringProtocolEncoder extends BaseProtocolEncoder {
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/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java
index 007a7b1bd..9e554217e 100644
--- a/src/main/java/org/traccar/api/ExtendedObjectResource.java
+++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java
@@ -55,8 +55,8 @@ public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResou
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
result.retainAll(manager.getDeviceItems(deviceId));
}
- return manager.getItems(result);
+ return manager.getItems(result);
}
}
diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/api/HealthCheckService.java
new file mode 100644
index 000000000..1e8f0d731
--- /dev/null
+++ b/src/main/java/org/traccar/api/HealthCheckService.java
@@ -0,0 +1,91 @@
+/*
+ * 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.api;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+
+import java.util.TimerTask;
+
+public class HealthCheckService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckService.class);
+
+ private SystemD systemD;
+
+ private boolean enabled;
+ private long period;
+
+ public HealthCheckService() {
+ if (Context.getConfig().getBoolean("web.healthCheck")
+ && System.getProperty("os.name").toLowerCase().startsWith("linux")) {
+ try {
+ systemD = Native.load("systemd", SystemD.class);
+ String watchdogTimer = System.getenv("WATCHDOG_USEC");
+ if (watchdogTimer != null && !watchdogTimer.isEmpty()) {
+ period = Long.parseLong(watchdogTimer) / 1000 * 4 / 5;
+ }
+ if (period > 0) {
+ LOGGER.info("Health check enabled with period {}", period);
+ enabled = true;
+ }
+ } catch (UnsatisfiedLinkError e) {
+ LOGGER.warn("No systemd support", e);
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public long getPeriod() {
+ return period;
+ }
+
+ private String getUrl() {
+ String address = Context.getConfig().getString("web.address", "localhost");
+ int port = Context.getConfig().getInteger("web.port", 8082);
+ return "http://" + address + ":" + port + "/api/server";
+ }
+
+ public TimerTask createTask() {
+ return new TimerTask() {
+ @Override
+ public void run() {
+ LOGGER.debug("Health check running");
+ int status = Context.getClient().target(getUrl()).request().get().getStatus();
+ if (status == 200) {
+ int result = systemD.sd_notify(0, "WATCHDOG=1");
+ if (result < 0) {
+ LOGGER.warn("Health check notify error {}", result);
+ }
+ } else {
+ LOGGER.warn("Health check failed with status {}", status);
+ }
+ }
+ };
+ }
+
+ interface SystemD extends Library {
+ @SuppressWarnings("checkstyle:MethodName")
+ int sd_notify(@SuppressWarnings("checkstyle:ParameterName") int unset_environment, String state);
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java
index fd331c766..e3c5d457f 100644
--- a/src/main/java/org/traccar/api/resource/SessionResource.java
+++ b/src/main/java/org/traccar/api/resource/SessionResource.java
@@ -18,6 +18,7 @@ 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;
@@ -106,6 +107,7 @@ public class SessionResource extends BaseResource {
LogAction.login(user.getId());
return user;
} else {
+ LogAction.failedLogin(ServletHelper.retrieveRemoteAddress(request));
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
index 8bf9ef860..e274e5aba 100644
--- a/src/main/java/org/traccar/database/BaseObjectManager.java
+++ b/src/main/java/org/traccar/database/BaseObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -23,6 +23,8 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +34,8 @@ public class BaseObjectManager<T extends BaseModel> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseObjectManager.class);
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
private final DataManager dataManager;
private Map<Long, T> items;
@@ -43,6 +47,22 @@ public class BaseObjectManager<T extends BaseModel> {
refreshItems();
}
+ protected final void readLock() {
+ lock.readLock().lock();
+ }
+
+ protected final void readUnlock() {
+ lock.readLock().unlock();
+ }
+
+ protected final void writeLock() {
+ lock.writeLock().lock();
+ }
+
+ protected final void writeUnlock() {
+ lock.writeLock().unlock();
+ }
+
protected final DataManager getDataManager() {
return dataManager;
}
@@ -52,12 +72,18 @@ public class BaseObjectManager<T extends BaseModel> {
}
public T getById(long itemId) {
- return items.get(itemId);
+ try {
+ readLock();
+ return items.get(itemId);
+ } finally {
+ readUnlock();
+ }
}
public void refreshItems() {
if (dataManager != null) {
try {
+ writeLock();
Collection<T> databaseItems = dataManager.getObjects(baseClass);
if (items == null) {
items = new ConcurrentHashMap<>(databaseItems.size());
@@ -78,12 +104,19 @@ public class BaseObjectManager<T extends BaseModel> {
}
} catch (SQLException error) {
LOGGER.warn("Error refreshing items", error);
+ } finally {
+ writeUnlock();
}
}
}
protected void addNewItem(T item) {
- items.put(item.getId(), item);
+ try {
+ writeLock();
+ items.put(item.getId(), item);
+ } finally {
+ writeUnlock();
+ }
}
public void addItem(T item) throws SQLException {
@@ -92,7 +125,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
protected void updateCachedItem(T item) {
- items.put(item.getId(), item);
+ try {
+ writeLock();
+ items.put(item.getId(), item);
+ } finally {
+ writeUnlock();
+ }
}
public void updateItem(T item) throws SQLException {
@@ -101,7 +139,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
protected void removeCachedItem(long itemId) {
- items.remove(itemId);
+ try {
+ writeLock();
+ items.remove(itemId);
+ } finally {
+ writeUnlock();
+ }
}
public void removeItem(long itemId) throws SQLException {
@@ -121,7 +164,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
public Set<Long> getAllItems() {
- return items.keySet();
+ try {
+ readLock();
+ return items.keySet();
+ } finally {
+ readUnlock();
+ }
}
}
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index dc9512d9e..de6eeeba8 100644
--- a/src/main/java/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -142,14 +142,33 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
}
private Queue<Command> getDeviceQueue(long deviceId) {
- if (!deviceQueues.containsKey(deviceId)) {
- deviceQueues.put(deviceId, new ConcurrentLinkedQueue<Command>());
+ Queue<Command> deviceQueue;
+ try {
+ readLock();
+ deviceQueue = deviceQueues.get(deviceId);
+ } finally {
+ readUnlock();
+ }
+ if (deviceQueue != null) {
+ return deviceQueue;
+ } else {
+ try {
+ writeLock();
+ return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>());
+ } finally {
+ writeUnlock();
+ }
}
- return deviceQueues.get(deviceId);
}
public void sendQueuedCommands(ActiveDevice activeDevice) {
- Queue<Command> deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
+ Queue<Command> deviceQueue;
+ try {
+ readLock();
+ deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
+ } finally {
+ readUnlock();
+ }
if (deviceQueue != null) {
Command command = deviceQueue.poll();
while (command != null) {
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
index fa95adeb2..fe17f7ced 100644
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ b/src/main/java/org/traccar/database/DeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 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.
@@ -58,11 +58,16 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
public DeviceManager(DataManager dataManager) {
super(dataManager, Device.class);
this.config = Context.getConfig();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
+ try {
+ writeLock();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
+ } finally {
+ writeUnlock();
}
dataRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000;
lookupGroupsAttribute = config.getBoolean("deviceManager.lookupGroupsAttribute");
@@ -108,11 +113,20 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
@Override
public Device getByUniqueId(String uniqueId) throws SQLException {
- boolean forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
-
+ boolean forceUpdate;
+ try {
+ readLock();
+ forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
+ } finally {
+ readUnlock();
+ }
updateDeviceCache(forceUpdate);
-
- return devicesByUniqueId.get(uniqueId);
+ try {
+ readLock();
+ return devicesByUniqueId.get(uniqueId);
+ } finally {
+ readUnlock();
+ }
}
@Override
@@ -134,7 +148,12 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public Device getDeviceByPhone(String phone) {
- return devicesByPhone.get(phone);
+ try {
+ readLock();
+ return devicesByPhone.get(phone);
+ } finally {
+ readUnlock();
+ }
}
@Override
@@ -176,8 +195,7 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public Set<Long> getAllManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getAllUserItems(userId));
+ Set<Long> result = new HashSet<>(getAllUserItems(userId));
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getAllUserItems(managedUserId));
}
@@ -186,34 +204,68 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = new HashSet<>(getUserItems(userId));
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
return result;
}
- private void putUniqueDeviceId(Device device) {
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ private void addByUniqueId(Device device) {
+ try {
+ writeLock();
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
+ devicesByUniqueId.put(device.getUniqueId(), device);
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void removeByUniqueId(String deviceUniqueId) {
+ try {
+ writeLock();
+ if (devicesByUniqueId != null) {
+ devicesByUniqueId.remove(deviceUniqueId);
+ }
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void addByPhone(Device device) {
+ try {
+ writeLock();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ devicesByPhone.put(device.getPhone(), device);
+ } finally {
+ writeUnlock();
}
- devicesByUniqueId.put(device.getUniqueId(), device);
}
- private void putPhone(Device device) {
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>(getAllItems().size());
+ private void removeByPhone(String phone) {
+ if (phone == null || phone.isEmpty()) {
+ return;
+ }
+ try {
+ writeLock();
+ if (devicesByPhone != null) {
+ devicesByPhone.remove(phone);
+ }
+ } finally {
+ writeUnlock();
}
- devicesByPhone.put(device.getPhone(), device);
}
@Override
protected void addNewItem(Device device) {
super.addNewItem(device);
- putUniqueDeviceId(device);
+ addByUniqueId(device);
if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- putPhone(device);
+ addByPhone(device);
}
if (Context.getGeofenceManager() != null) {
Position lastPosition = getLastPosition(device.getId());
@@ -234,18 +286,16 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
cachedDevice.setDisabled(device.getDisabled());
cachedDevice.setAttributes(device.getAttributes());
if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
- devicesByUniqueId.remove(cachedDevice.getUniqueId());
+ removeByUniqueId(cachedDevice.getUniqueId());
cachedDevice.setUniqueId(device.getUniqueId());
- putUniqueDeviceId(cachedDevice);
+ addByUniqueId(cachedDevice);
}
if (device.getPhone() != null && !device.getPhone().isEmpty()
&& !device.getPhone().equals(cachedDevice.getPhone())) {
String phone = cachedDevice.getPhone();
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
- }
+ removeByPhone(phone);
cachedDevice.setPhone(device.getPhone());
- putPhone(cachedDevice);
+ addByPhone(cachedDevice);
}
}
@@ -256,10 +306,8 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
String deviceUniqueId = cachedDevice.getUniqueId();
String phone = cachedDevice.getPhone();
super.removeCachedItem(deviceId);
- devicesByUniqueId.remove(deviceUniqueId);
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
- }
+ removeByUniqueId(deviceUniqueId);
+ removeByPhone(phone);
}
positions.remove(deviceId);
}
diff --git a/src/main/java/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java
index 930951460..d111cd643 100644
--- a/src/main/java/org/traccar/database/DriversManager.java
+++ b/src/main/java/org/traccar/database/DriversManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -27,22 +27,44 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
public DriversManager(DataManager dataManager) {
super(dataManager, Driver.class);
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ } finally {
+ writeUnlock();
}
}
- private void putUniqueDriverId(Driver driver) {
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ private void addByUniqueId(Driver driver) {
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ driversByUniqueId.put(driver.getUniqueId(), driver);
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void removeByUniqueId(String driverUniqueId) {
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ driversByUniqueId.remove(driverUniqueId);
+ } finally {
+ writeUnlock();
}
- driversByUniqueId.put(driver.getUniqueId(), driver);
}
@Override
protected void addNewItem(Driver driver) {
super.addNewItem(driver);
- putUniqueDriverId(driver);
+ addByUniqueId(driver);
}
@Override
@@ -50,9 +72,9 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
Driver cachedDriver = getById(driver.getId());
cachedDriver.setName(driver.getName());
if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) {
- driversByUniqueId.remove(cachedDriver.getUniqueId());
+ removeByUniqueId(cachedDriver.getUniqueId());
cachedDriver.setUniqueId(driver.getUniqueId());
- putUniqueDriverId(cachedDriver);
+ addByUniqueId(cachedDriver);
}
cachedDriver.setAttributes(driver.getAttributes());
}
@@ -63,11 +85,16 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
if (cachedDriver != null) {
String driverUniqueId = cachedDriver.getUniqueId();
super.removeCachedItem(driverId);
- driversByUniqueId.remove(driverUniqueId);
+ removeByUniqueId(driverUniqueId);
}
}
public Driver getDriverByUniqueId(String uniqueId) {
- return driversByUniqueId.get(uniqueId);
+ try {
+ readLock();
+ return driversByUniqueId.get(uniqueId);
+ } finally {
+ readUnlock();
+ }
}
}
diff --git a/src/main/java/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java
index ceb85b537..93e5820fb 100644
--- a/src/main/java/org/traccar/database/ExtendedObjectManager.java
+++ b/src/main/java/org/traccar/database/ExtendedObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -45,24 +45,45 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
}
public final Set<Long> getGroupItems(long groupId) {
- if (!groupItems.containsKey(groupId)) {
- groupItems.put(groupId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = groupItems.get(groupId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return groupItems.get(groupId);
}
public final Set<Long> getDeviceItems(long deviceId) {
- if (!deviceItems.containsKey(deviceId)) {
- deviceItems.put(deviceId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = deviceItems.get(deviceId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return deviceItems.get(deviceId);
}
public Set<Long> getAllDeviceItems(long deviceId) {
- if (!deviceItemsWithGroups.containsKey(deviceId)) {
- deviceItemsWithGroups.put(deviceId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = deviceItemsWithGroups.get(deviceId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return deviceItemsWithGroups.get(deviceId);
}
@Override
@@ -74,41 +95,48 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
public void refreshExtendedPermissions() {
if (getDataManager() != null) {
try {
-
Collection<Permission> databaseGroupPermissions =
getDataManager().getPermissions(Group.class, getBaseClass());
- groupItems.clear();
- for (Permission groupPermission : databaseGroupPermissions) {
- getGroupItems(groupPermission.getOwnerId()).add(groupPermission.getPropertyId());
- }
-
Collection<Permission> databaseDevicePermissions =
getDataManager().getPermissions(Device.class, getBaseClass());
+ writeLock();
+
+ groupItems.clear();
deviceItems.clear();
deviceItemsWithGroups.clear();
+ for (Permission groupPermission : databaseGroupPermissions) {
+ groupItems
+ .computeIfAbsent(groupPermission.getOwnerId(), key -> new HashSet<>())
+ .add(groupPermission.getPropertyId());
+ }
+
for (Permission devicePermission : databaseDevicePermissions) {
- getDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
- getAllDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
+ deviceItems
+ .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
+ .add(devicePermission.getPropertyId());
+ deviceItemsWithGroups
+ .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
+ .add(devicePermission.getPropertyId());
}
for (Device device : Context.getDeviceManager().getAllDevices()) {
long groupId = device.getGroupId();
- while (groupId != 0) {
- getAllDeviceItems(device.getId()).addAll(getGroupItems(groupId));
+ while (groupId > 0) {
+ deviceItemsWithGroups
+ .computeIfAbsent(device.getId(), key -> new HashSet<>())
+ .addAll(groupItems.getOrDefault(groupId, new HashSet<>()));
Group group = Context.getGroupsManager().getById(groupId);
- if (group != null) {
- groupId = group.getGroupId();
- } else {
- groupId = 0;
- }
+ groupId = group != null ? group.getGroupId() : 0;
}
}
} catch (SQLException | ClassNotFoundException error) {
LOGGER.warn("Refresh permissions error", error);
+ } finally {
+ writeUnlock();
}
}
}
diff --git a/src/main/java/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java
index d8404c614..81f1968aa 100644
--- a/src/main/java/org/traccar/database/GroupsManager.java
+++ b/src/main/java/org/traccar/database/GroupsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -95,8 +95,7 @@ public class GroupsManager extends BaseObjectManager<Group> implements Managable
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
diff --git a/src/main/java/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java
index 15dda4520..eb8284d4e 100644
--- a/src/main/java/org/traccar/database/SimpleObjectManager.java
+++ b/src/main/java/org/traccar/database/SimpleObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -42,16 +42,22 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
@Override
public final Set<Long> getUserItems(long userId) {
- if (!userItems.containsKey(userId)) {
- userItems.put(userId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = userItems.get(userId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return userItems.get(userId);
}
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
@@ -71,16 +77,16 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
public final void refreshUserItems() {
if (getDataManager() != null) {
try {
- if (userItems != null) {
- userItems.clear();
- } else {
- userItems = new ConcurrentHashMap<>();
- }
+ writeLock();
+ userItems = new ConcurrentHashMap<>();
for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) {
- getUserItems(permission.getOwnerId()).add(permission.getPropertyId());
+ Set<Long> items = userItems.computeIfAbsent(permission.getOwnerId(), key -> new HashSet<>());
+ items.add(permission.getPropertyId());
}
} catch (SQLException | ClassNotFoundException error) {
LOGGER.warn("Error getting permissions", error);
+ } finally {
+ writeUnlock();
}
}
}
diff --git a/src/main/java/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java
index 576a9e6c7..b741a85b6 100644
--- a/src/main/java/org/traccar/database/UsersManager.java
+++ b/src/main/java/org/traccar/database/UsersManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -16,7 +16,6 @@
*/
package org.traccar.database;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -73,8 +72,7 @@ public class UsersManager extends SimpleObjectManager<User> {
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
result.add(userId);
return result;
}
diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
index c7a68c293..f540eb8fe 100644
--- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -20,8 +20,16 @@ import javax.json.JsonObject;
public class FactualGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "https://api.factual.com/geotag";
+ }
+ url += "?latitude=%f&longitude=%f&KEY=" + key;
+ return url;
+ }
+
public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
index 3a173f985..b4881a006 100644
--- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ import javax.json.JsonObject;
public class GisgraphyGeocoder extends JsonGeocoder {
- public GisgraphyGeocoder(AddressFormat addressFormat) {
- this("http://services.gisgraphy.com/reversegeocoding/search", 0, addressFormat);
+ private static String formatUrl(String url) {
+ if (url == null) {
+ url = "http://services.gisgraphy.com/reversegeocoding/search";
+ }
+ url += "?format=json&lat=%f&lng=%f&from=1&to=1";
+ return url;
}
public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) {
- super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize, addressFormat);
+ super(formatUrl(url), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java
index 756260b52..aaf11d74d 100644
--- a/src/main/java/org/traccar/geocoder/HereGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java
@@ -19,20 +19,24 @@ import javax.json.JsonObject;
public class HereGeocoder extends JsonGeocoder {
- private static String formatUrl(String id, String key, String language) {
- String url = "https://reverse.geocoder.api.here.com/6.2/reversegeocode.json";
+ 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 id, String key, String language, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(id, key, language), cacheSize, addressFormat);
+ public HereGeocoder(
+ String url, String id, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(url, id, key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
index 4029e3f07..8dc3f76f0 100644
--- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -21,8 +21,16 @@ import javax.json.JsonObject;
public class MapQuestGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "http://www.mapquestapi.com/geocoding/v1/reverse";
+ }
+ url += "?key=" + key + "&location=%f,%f";
+ return url;
+ }
+
public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "?key=" + key + "&location=%f,%f", cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
index 822b6e91e..56161e52c 100644
--- a/src/main/java/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 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -21,8 +21,16 @@ import javax.json.JsonObject;
public class OpenCageGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "https://api.opencagedata.com/geocode/v1";
+ }
+ url += "/json?q=%f,%f&no_annotations=1&key=" + key;
+ return url;
+ }
+
public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "/json?q=%f,%f&no_annotations=1&key=" + key, cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
index 768aaf6a2..cb3094e16 100644
--- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
@@ -26,11 +26,10 @@ 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;
}
diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java
index adfa697c5..d41dc2992 100644
--- a/src/main/java/org/traccar/helper/Checksum.java
+++ b/src/main/java/org/traccar/helper/Checksum.java
@@ -168,6 +168,14 @@ public final class Checksum {
return checksum;
}
+ public static int modulo256(ByteBuffer buf) {
+ int checksum = 0;
+ while (buf.hasRemaining()) {
+ checksum = (checksum + buf.get()) & 0xFF;
+ }
+ return checksum;
+ }
+
public static String sum(String msg) {
byte checksum = 0;
for (byte b : msg.getBytes(StandardCharsets.US_ASCII)) {
diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java
index db13337b8..16d55ec60 100644
--- a/src/main/java/org/traccar/helper/LogAction.java
+++ b/src/main/java/org/traccar/helper/LogAction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * 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");
@@ -44,6 +44,7 @@ public final class LogAction {
private static final String PATTERN_OBJECT = "user: %d, action: %s, object: %s, id: %d";
private static final String PATTERN_LINK = "user: %d, action: %s, owner: %s, id: %d, property: %s, id: %d";
private static final String PATTERN_LOGIN = "user: %d, action: %s";
+ private static final String PATTERN_LOGIN_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) {
@@ -74,6 +75,13 @@ public final class LogAction {
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));
@@ -85,7 +93,7 @@ public final class LogAction {
}
private static void logLinkAction(String action, long userId,
- Class<?> owner, long ownerId, Class<?> property, long propertyId) {
+ Class<?> owner, long ownerId, Class<?> property, long propertyId) {
LOGGER.info(String.format(
PATTERN_LINK, userId, action,
Introspector.decapitalize(owner.getSimpleName()), ownerId,
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/main/java/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java
index 336fc61f4..abe538a10 100644
--- a/src/main/java/org/traccar/model/Command.java
+++ b/src/main/java/org/traccar/model/Command.java
@@ -68,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/main/java/org/traccar/protocol/AdmProtocolEncoder.java b/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
index 1c3dfc156..c02fa4112 100644
--- a/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
@@ -34,7 +34,7 @@ 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:
return null;
diff --git a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
new file mode 100644
index 000000000..e957a6911
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Ivan Muratov (binakot@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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ArnaviBinaryProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final byte HEADER_START_SIGN = (byte) 0xff;
+ private static final byte HEADER_VERSION_1 = 0x22;
+ private static final byte HEADER_VERSION_2 = 0x23;
+
+ private static final byte RECORD_PING = 0x00;
+ private static final byte RECORD_DATA = 0x01;
+ private static final byte RECORD_TEXT = 0x03;
+ private static final byte RECORD_FILE = 0x04;
+ private static final byte RECORD_BINARY = 0x06;
+
+ private static final byte TAG_LATITUDE = 3;
+ private static final byte TAG_LONGITUDE = 4;
+ private static final byte TAG_COORD_PARAMS = 5;
+
+ public ArnaviBinaryProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, byte version, int index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x7b);
+ if (version == HEADER_VERSION_1) {
+ response.writeByte(0x00);
+ response.writeByte((byte) index);
+ } else if (version == HEADER_VERSION_2) {
+ response.writeByte(0x04);
+ response.writeByte(0x00);
+ ByteBuffer time = ByteBuffer.allocate(4).putInt((int) (System.currentTimeMillis() / 1000));
+ ((Buffer) time).position(0);
+ response.writeByte(Checksum.modulo256(time.slice()));
+ response.writeBytes(time);
+ }
+ response.writeByte(0x7d);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private Position decodePosition(DeviceSession deviceSession, ByteBuf buf, int length, Date time) {
+
+ final Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(time);
+
+ int readBytes = 0;
+ while (readBytes < length) {
+ short tag = buf.readUnsignedByte();
+ switch (tag) {
+ case TAG_LATITUDE:
+ position.setLatitude(buf.readFloatLE());
+ position.setValid(true);
+ break;
+
+ case TAG_LONGITUDE:
+ position.setLongitude(buf.readFloatLE());
+ position.setValid(true);
+ break;
+
+ case TAG_COORD_PARAMS:
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readUnsignedByte() * 10);
+ byte satellites = buf.readByte();
+ position.set(Position.KEY_SATELLITES, satellites & 0x0F + (satellites >> 4) & 0x0F);
+ position.setSpeed(buf.readUnsignedByte());
+ break;
+
+ default:
+ buf.skipBytes(4);
+ break;
+ }
+
+ readBytes += 1 + 4;
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ byte startSign = buf.readByte();
+
+ if (startSign == HEADER_START_SIGN) {
+
+ byte version = buf.readByte();
+
+ String imei = String.valueOf(buf.readLongLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+
+ if (deviceSession != null) {
+ sendResponse(channel, version, 0);
+ }
+
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ int index = buf.readUnsignedByte();
+
+ byte recordType = buf.readByte();
+ while (buf.readableBytes() > 0) {
+ switch (recordType) {
+ case RECORD_PING:
+ case RECORD_DATA:
+ case RECORD_TEXT:
+ case RECORD_FILE:
+ case RECORD_BINARY:
+ int length = buf.readUnsignedShortLE();
+ Date time = new Date(buf.readUnsignedIntLE() * 1000);
+
+ if (recordType == RECORD_DATA) {
+ positions.add(decodePosition(deviceSession, buf, length, time));
+ } else {
+ buf.readBytes(length);
+ }
+
+ buf.readUnsignedByte(); // checksum
+ break;
+
+ default:
+ return null;
+ }
+
+ recordType = buf.readByte();
+ }
+
+ sendResponse(channel, HEADER_VERSION_1, index);
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java b/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java
new file mode 100644
index 000000000..473e8b2c7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Ivan Muratov (binakot@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.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
+
+public class ArnaviFrameDecoder extends BaseFrameDecoder {
+
+ private static final int HEADER_LENGTH = 10;
+ private static final int PACKET_WRAPPER_LENGTH = 8;
+ private static final int RESULT_TYPE = 0xfd;
+ private static final byte PACKAGE_END_SIGN = 0x5d;
+
+ private boolean firstPacket = true;
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 4) {
+ return null;
+ }
+
+ if (buf.getByte(buf.readerIndex()) == '$') {
+
+ int index = BufferUtil.indexOf("\r\n", buf);
+ if (index > 0) {
+ ByteBuf frame = buf.readRetainedSlice(index - buf.readerIndex());
+ buf.skipBytes(2);
+ return frame;
+ }
+
+ } else {
+
+ int length;
+ if (firstPacket) {
+ firstPacket = false;
+ length = HEADER_LENGTH;
+ } else {
+ int type = buf.getUnsignedByte(1);
+ if (type == RESULT_TYPE) {
+ length = 4;
+ } else {
+ int index = 2;
+ while (index + PACKET_WRAPPER_LENGTH < buf.readableBytes()
+ && buf.getByte(index) != PACKAGE_END_SIGN) {
+ index += PACKET_WRAPPER_LENGTH + buf.getUnsignedShortLE(index + 1);
+ }
+ if (buf.getByte(index) != PACKAGE_END_SIGN) {
+ return null;
+ }
+ length = index + 1;
+ }
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
index afe491865..aecb42c8c 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
@@ -28,9 +25,7 @@ public class ArnaviProtocol extends BaseProtocol {
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 ArnaviFrameDecoder());
pipeline.addLast(new ArnaviProtocolDecoder(ArnaviProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
index 7996cf429..68a70c944 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,91 +15,35 @@
*/
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.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 ArnaviProtocolDecoder extends BaseProtocolDecoder {
+ private final ArnaviTextProtocolDecoder textProtocolDecoder;
+ private final ArnaviBinaryProtocolDecoder binaryProtocolDecoder;
+
public ArnaviProtocolDecoder(Protocol protocol) {
super(protocol);
+ textProtocolDecoder = new ArnaviTextProtocolDecoder(protocol);
+ binaryProtocolDecoder = new ArnaviBinaryProtocolDecoder(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
- .text("$AV,")
- .number("Vd,") // type
- .number("(d+),") // device id
- .number("(d+),") // index
- .number("(d+),") // power
- .number("(d+),") // battery
- .number("-?d+,")
- .expression("[01],") // movement
- .expression("([01]),") // ignition
- .number("(d+),") // input
- .number("d+,d+,") // input 1
- .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
- .number("(d+.d+),") // speed
- .number("(d+.d+),") // course
- .number("(dd)(dd)(dd)") // date (ddmmyy)
- .any()
- .compile();
-
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- Parser parser = new Parser(PATTERN, (String) msg);
- if (!parser.matches()) {
- return null;
- }
+ ByteBuf buf = (ByteBuf) msg;
- Position position = new Position(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
+ if (buf.getByte(buf.readerIndex()) == '$') {
+ return textProtocolDecoder.decode(channel, remoteAddress, msg);
+ } else {
+ return binaryProtocolDecoder.decode(channel, remoteAddress, msg);
}
- position.setDeviceId(deviceSession.getDeviceId());
-
- 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(), parser.nextInt(), parser.nextInt());
-
- position.setValid(true);
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble());
- position.setCourse(parser.nextDouble());
-
- dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setTime(dateBuilder.getDate());
-
- return position;
}
}
diff --git a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
new file mode 100644
index 000000000..b99869e6e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.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.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 ArnaviTextProtocolDecoder extends BaseProtocolDecoder {
+
+ public ArnaviTextProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$AV,")
+ .number("Vd,") // type
+ .number("(d+),") // device id
+ .number("(d+),") // index
+ .number("(d+),") // power
+ .number("(d+),") // battery
+ .number("-?d+,")
+ .expression("[01],") // movement
+ .expression("([01]),") // ignition
+ .number("(d+),") // input
+ .number("d+,d+,") // input 1
+ .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
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ Parser parser = new Parser(PATTERN, buf.toString(StandardCharsets.US_ASCII));
+ 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.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(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(true);
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
index f07338937..54136382c 100644
--- a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ 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;
@@ -82,10 +83,10 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(4);
}
if (BitUtil.check(mask, 12)) {
- buf.skipBytes(2);
+ position.set("fuel1", buf.readUnsignedShort());
}
if (BitUtil.check(mask, 13)) {
- buf.skipBytes(2);
+ position.set("fuel2", buf.readUnsignedShort());
}
if (BitUtil.check(mask, 14)) {
@@ -97,7 +98,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeMask2(ByteBuf buf, int mask) {
+ private void decodeMask2(ByteBuf buf, int mask, Position position) {
if (BitUtil.check(mask, 0)) {
buf.readUnsignedShortLE(); // wheel speed
@@ -106,31 +107,31 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // acceleration pedal
}
if (BitUtil.check(mask, 2)) {
- buf.readUnsignedIntLE(); // total fuel used
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 3)) {
- buf.readUnsignedByte(); // fuel level
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
}
if (BitUtil.check(mask, 4)) {
- buf.readUnsignedShortLE(); // engine speed
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE() * 0.0125);
}
if (BitUtil.check(mask, 5)) {
- buf.readUnsignedIntLE(); // total hours
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 6)) {
- buf.readUnsignedIntLE(); // total distance
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 7)) {
- buf.readUnsignedByte(); // engine coolant
+ position.set(Position.KEY_COOLANT_TEMP, buf.readByte() - 40);
}
if (BitUtil.check(mask, 8)) {
- buf.readUnsignedByte(); // fuel level 2
+ position.set("fuel2", buf.readUnsignedByte());
}
if (BitUtil.check(mask, 9)) {
- buf.readUnsignedByte(); // engine load
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte());
}
if (BitUtil.check(mask, 10)) {
- buf.readUnsignedShortLE(); // service distance
+ position.set(Position.KEY_ODOMETER_SERVICE, buf.readUnsignedShortLE());
}
if (BitUtil.check(mask, 11)) {
buf.skipBytes(8); // sensors
@@ -142,7 +143,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(8); // trailer id
}
if (BitUtil.check(mask, 14)) {
- buf.readUnsignedShortLE(); // fuel rate
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShortLE());
}
}
@@ -152,10 +153,10 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShortLE(); // fuel economy
}
if (BitUtil.check(mask, 1)) {
- buf.readUnsignedIntLE(); // fuel consumption
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 2)) {
- buf.readUnsignedMediumLE(); // axle weight
+ position.set(Position.KEY_AXLE_WEIGHT, buf.readUnsignedMediumLE());
}
if (BitUtil.check(mask, 3)) {
buf.readUnsignedByte(); // mil status
@@ -169,6 +170,70 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
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)) {
+ position.set("maxAcceleration", buf.readUnsignedByte());
+ position.set("maxBraking", buf.readUnsignedByte());
+ position.set("maxCornering", buf.readUnsignedByte());
+ }
+ 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
@@ -223,13 +288,18 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
if (masks.size() >= 2) {
mask = masks.get(1);
- decodeMask2(buf, mask);
+ 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);
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..f35ac6fbe
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
@@ -0,0 +1,170 @@
+/*
+ * 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()));
+ }
+ }
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
+ case 8:
+ return Position.ALARM_OVERSPEED;
+ case 19:
+ return Position.ALARM_LOW_POWER;
+ default:
+ return null;
+ }
+ }
+
+ @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
+
+ status = buf.readUnsignedByte(); // status 4
+ int ignition = BitUtil.between(status, 2, 4);
+ if (ignition == 0b01) {
+ position.set(Position.KEY_IGNITION, false);
+ }
+ if (ignition == 0b10) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+
+ buf.readUnsignedByte(); // status 5
+ buf.readUnsignedByte(); // status 6
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+
+ } else if (type == 0x81) {
+
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+
+ } 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/CarcellProtocolEncoder.java b/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
index 083fe9a9e..78dbe7e91 100644
--- a/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
@@ -30,9 +30,9 @@ public class CarcellProtocolEncoder extends StringProtocolEncoder {
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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
index 03e4b25fd..23401b5ee 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
@@ -23,6 +23,7 @@ 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;
@@ -185,7 +186,14 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedIntLE());
position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE());
buf.readUnsignedShortLE(); // current fuel consumption
- position.set(Position.KEY_STATUS, buf.readUnsignedIntLE());
+
+ 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);
}
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
index ea4975d3e..aa13a0aa2 100644
--- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
@@ -88,7 +88,7 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder {
content.writeByte(0); // ack
content.writeShortLE(0); // reserved
- ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, packetNumber, content);
+ ByteBuf reply = encodeContent(MSG_CLIENT_MODULAR_EXT, (int) deviceId, packetNumber, content);
channel.writeAndFlush(new NetworkMessage(reply, remoteAddress));
}
}
diff --git a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
index c04e90f1d..58a5a88e3 100644
--- a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 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.
@@ -208,10 +208,21 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder {
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);
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index a6fd94b83..41d76f37f 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -412,7 +412,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
return decodeNew(deviceSession, buf, type, index);
- } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) {
+ } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2
+ || type == MSG_OBD && buf.readableBytes() == 4) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
diff --git a/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java b/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
index 6ee305ed8..74f9e22ab 100644
--- a/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
@@ -31,13 +31,13 @@ public class EsealProtocolEncoder extends StringProtocolEncoder {
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);
+ 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);
+ 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);
+ command, "##S,eSeal,%s,256,3.0.8,RC-Unlock,E##", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java
index aaa92da58..fb047c207 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocol.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -33,6 +33,14 @@ public class EskyProtocol extends BaseProtocol {
pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
}
});
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
+ }
+ });
}
}
diff --git a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
index 641b2e28f..d9de110f4 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 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.
@@ -16,8 +16,10 @@
package org.traccar.protocol;
import io.netty.channel.Channel;
+import io.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -35,7 +37,7 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.expression("..;") // header
- .number("d+;") // index
+ .number("(d+);") // index
.number("(d+);") // imei
.text("R;") // data type
.number("(d+)[+;]") // satellites
@@ -63,6 +65,11 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ int index = parser.nextInt();
+ if (channel instanceof DatagramChannel) {
+ channel.writeAndFlush(new NetworkMessage("ACK," + index + "#", remoteAddress));
+ }
+
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 458e92065..40e146e0b 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -60,7 +60,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.number("(-?d+),") // altitude
.number("(d+),") // odometer
.number("d+,") // runtime
- .number("(xxxx),") // status
+ .number("(x{4,8}),") // status
.number("(x+)?,") // input
.number("(x+)?,") // output
.number("(d+)|") // mcc
@@ -151,7 +151,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextInt());
position.set(Position.KEY_ODOMETER, parser.nextLong());
- position.set(Position.KEY_STATUS, parser.nextHexInt());
+ position.set(Position.KEY_STATUS, parser.nextHexLong());
position.set(Position.KEY_INPUT, parser.nextHexInt());
position.set(Position.KEY_OUTPUT, parser.nextHexInt());
diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
index 5b843324c..dfaedd695 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -318,7 +318,9 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // part number
photo.writeBytes(buf, length - 1);
- } else {
+ sendResponse(channel, 0x07, buf.readUnsignedShortLE());
+
+ } else if (photo != null) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
@@ -334,8 +336,6 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
- sendResponse(channel, 0x07, buf.readUnsignedShortLE());
-
return position;
}
diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
index 31500bae6..087861635 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -23,6 +23,7 @@ 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;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -58,16 +59,16 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
return String.format("%02d%02d%02d%02d%02d", d1, d2, d3, d4, d5);
}
- private void sendResponse(Channel channel, SocketAddress remoteAddress, byte calibration) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int type, int checksum) {
if (channel != null) {
ByteBuf response = Unpooled.buffer();
- response.writeByte(0x24); response.writeByte(0x24); // header
- response.writeByte(MSG_HEARTBEAT); // size
- response.writeShort(5);
- response.writeByte(calibration);
- response.writeByte(0); // main order
- response.writeByte(0); // slave order
- response.writeByte(1); // calibration
+ response.writeShort(0x2424); // header
+ response.writeByte(MSG_HEARTBEAT);
+ response.writeShort(5); // length
+ response.writeByte(checksum);
+ response.writeByte(type);
+ response.writeByte(0); // subtype
+ response.writeByte(Checksum.xor(response.nioBuffer(2, response.writerIndex())));
response.writeByte(0x0D);
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
@@ -87,7 +88,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(), buf.readUnsignedByte(),
buf.readUnsignedByte(), buf.readUnsignedByte());
- sendResponse(channel, remoteAddress, buf.getByte(buf.writerIndex() - 2));
+ sendResponse(channel, remoteAddress, type, buf.getByte(buf.writerIndex() - 2));
if (type == MSG_POSITION_DATA || type == MSG_ROLLCALL_RESPONSE
|| type == MSG_ALARM_DATA || type == MSG_BLIND_AREA) {
@@ -120,8 +121,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/Gl200ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
index 32307446b..dd0672c23 100644
--- a/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
@@ -32,17 +32,17 @@ 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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index 5739a1224..382509793 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -17,28 +17,41 @@ 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.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
import org.traccar.BaseHttpProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.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;
@@ -67,6 +80,40 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
+ 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)));
+
+ if (channel != null) {
+ FullHttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
+ response.headers()
+ .add(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes())
+ .add(HttpHeaderNames.CONTENT_TYPE, "text/xml");
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -112,7 +159,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
- sendResponse(channel, HttpResponseStatus.OK);
+ sendResponse(channel, document.getFirstChild().getAttributes().getNamedItem("messageID").getNodeValue());
return positions;
}
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index aa02e8ad4..e00d83061 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -77,7 +77,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW])?,").optional()
.number("(d+.?d*)?").optional() // speed
.number(",(d+.?d*)?").optional() // course
- .number(",(d+.?d*)?").optional() // altitude
+ .number(",(-?d+.?d*)?").optional() // altitude
.number(",([01])?").optional() // ignition
.number(",([01])?").optional() // door
.groupBegin()
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
index 7128823ed..e662e9b04 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
@@ -47,24 +47,24 @@ 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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java b/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
index 7dd4b2d77..be0ab5130 100644
--- a/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
@@ -32,7 +32,7 @@ public class GranitProtocolSmsEncoder extends StringProtocolEncoder {
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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index dc7a95955..946652b03 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -103,6 +103,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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);
@@ -208,7 +209,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
sendResponse(channel, false, MSG_X1_PHOTO_DATA, 0, content);
}
- private boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) {
+ public static boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) {
DateBuilder dateBuilder = new DateBuilder(timezone)
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
@@ -547,6 +548,15 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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) {
@@ -720,6 +730,14 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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));
@@ -750,6 +768,39 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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) {
@@ -958,7 +1009,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_GPS_MODULAR) {
- return decodeExtendedModular(channel, buf, deviceSession, type);
+ return decodeExtendedModular(buf, deviceSession);
} else {
@@ -969,7 +1020,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodeExtendedModular(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
+ private Object decodeExtendedModular(ByteBuf buf, DeviceSession deviceSession) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -977,7 +1028,28 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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;
@@ -988,7 +1060,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_INDEX, buf.readUnsignedInt());
break;
case 0x2a:
- position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ 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();
@@ -1036,6 +1110,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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;
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index 137689a67..320fe991d 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -185,7 +186,7 @@ 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)
@@ -223,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()
@@ -287,9 +288,15 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
private void sendResponse(Channel channel, SocketAddress remoteAddress, String id, String type) {
if (channel != null && id != null) {
- DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ String response;
+ DateFormat dateFormat = new SimpleDateFormat(type.equals("R12") ? "HHmmss" : "yyyyMMddHHmmss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- String response = String.format("*HQ,%s,V4,%s,%s#", id, type, dateFormat.format(new Date()));
+ 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));
}
}
@@ -316,6 +323,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
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();
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index 9449e2d5c..eac06bbfe 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 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.
@@ -25,7 +25,10 @@ import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
@@ -137,6 +140,8 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedShort() * 1000);
+ Network network = new Network();
+
while (buf.readableBytes() > 4) {
int subtype = buf.readUnsignedShort();
int length = buf.readUnsignedShort() - 4;
@@ -161,12 +166,34 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(
Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
break;
+ case 0x0020:
+ 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)));
+ }
+ break;
+ case 0x0021:
+ String[] points = buf.readCharSequence(
+ length, StandardCharsets.US_ASCII).toString().split("\\+");
+ for (String point : points) {
+ String[] values = point.split("@");
+ network.addWifiAccessPoint(WifiAccessPoint.from(values[0], Integer.parseInt(values[1])));
+ }
+ break;
default:
buf.skipBytes(length);
break;
}
}
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
sendResponse(channel, MSG_POSITION_RSP, index, null);
return position;
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 6e2e1377b..bf5b6d89a 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -58,7 +58,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
buf.writeShort(type);
buf.writeShort(data.readableBytes());
buf.writeBytes(id);
- buf.writeShort(1); // index
+ buf.writeShort(0); // index
buf.writeBytes(data);
data.release();
buf.writeByte(Checksum.xor(buf.nioBuffer(1, buf.readableBytes() - 1)));
@@ -98,6 +98,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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;
}
@@ -131,7 +134,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
ByteBuf response = Unpooled.buffer();
response.writeShort(index);
response.writeByte(RESULT_SUCCESS);
- response.writeBytes("authentication".getBytes(StandardCharsets.US_ASCII));
+ response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII));
channel.writeAndFlush(new NetworkMessage(
formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress));
}
@@ -160,22 +163,23 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
- int flags = buf.readInt();
+ int status = buf.readInt();
- position.set(Position.KEY_IGNITION, BitUtil.check(flags, 0));
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10));
- position.setValid(BitUtil.check(flags, 1));
+ position.setValid(BitUtil.check(status, 1));
double lat = buf.readUnsignedInt() * 0.000001;
double lon = buf.readUnsignedInt() * 0.000001;
- if (BitUtil.check(flags, 2)) {
+ if (BitUtil.check(status, 2)) {
position.setLatitude(-lat);
} else {
position.setLatitude(lat);
}
- if (BitUtil.check(flags, 3)) {
+ if (BitUtil.check(status, 3)) {
position.setLongitude(-lon);
} else {
position.setLongitude(lon);
@@ -197,20 +201,80 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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:
- buf.skipBytes(length);
break;
}
+ buf.readerIndex(endIndex);
}
return position;
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index d76d9c92e..e8d77f1a8 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -41,7 +41,7 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.text("$")
.expression(",?[^,]+,") // event
.groupBegin()
- .expression("[^,]+,") // vendor
+ .expression("[^,]*,") // vendor
.expression("[^,]+,") // firmware version
.expression("(..),") // status
.number("(d+),").optional() // event
@@ -81,8 +81,8 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.number("([01]{2}),") // outputs
.groupBegin()
.number("d+,") // index
- .number("(d+.d+),") // adc1
- .number("(d+.d+),") // adc2
+ .number("(d+.?d*),") // adc1
+ .number("(d+.?d*),") // adc2
.groupEnd("?")
.groupEnd("?")
.or()
@@ -195,22 +195,25 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
position.set("emergency", parser.nextInt() > 0);
- String[] cells = parser.next().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));
+ 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);
}
- position.setNetwork(network);
String input = parser.next();
if (input.charAt(input.length() - 1) == '2') {
diff --git a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
index b5d060ecc..bfefb94a7 100644
--- a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
@@ -35,7 +35,7 @@ public class Jt600FrameDecoder extends BaseFrameDecoder {
char type = (char) buf.getByte(buf.readerIndex());
if (type == '$') {
- boolean longFormat = 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.readRetainedSlice(length);
diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index da055dff3..f456cd1ef 100644
--- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -86,13 +86,17 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
}
+ 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(ByteBufUtil.hexDump(buf.readSlice(5))));
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
index 343ac9431..0c9f8ebb8 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
@@ -40,13 +40,13 @@ public class LaipacProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}",
+ return formatCommand(command, "%s",
Command.KEY_DATA);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "AVREQ,{%s},1",
+ return formatCommand(command, "AVREQ,%s,1",
Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "AVRESET,{%s},{%s}",
+ 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/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
index cbfc3660a..bd66cdc4b 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 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.
@@ -47,7 +47,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
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]),")
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 55260ef0c..529496928 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -70,7 +70,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)|") // mnc
.number("(x+)|") // lac
.number("(x+),") // cid
- .number("(x+),") // state
+ .number("(xx)") // input
+ .number("(xx),") // output
.number("(x+)?|") // adc1
.number("(x+)?|") // adc2
.number("(x+)?|") // adc3
@@ -149,39 +150,38 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
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));
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
position.setTime(parser.nextDateTime());
position.setValid(parser.next().equals("A"));
position.set(Position.KEY_SATELLITES, parser.nextInt());
- int rssi = parser.nextInt(0);
+ int rssi = parser.nextInt();
- 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.KEY_HDOP, parser.nextDouble());
- position.setAltitude(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble());
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set("runtime", parser.next());
position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0), rssi)));
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
- position.set(Position.KEY_STATUS, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
for (int i = 1; i <= 3; i++) {
- if (parser.hasNext()) {
- position.set(Position.PREFIX_ADC + i, parser.nextHexInt(0));
- }
+ position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
}
String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
index 36fb9fc2f..059f688f7 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
@@ -57,28 +57,28 @@ public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements
switch (command.getType()) {
case Command.TYPE_SET_TIMEZONE:
- return formatCommand(command, "{%s}L{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_TIMEZONE);
+ return formatCommand(command, "%sL%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);
+ return formatCommand(command, "%sP%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);
+ return formatCommand(command, "%sJ1%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);
+ return formatCommand(command, "%sR1%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);
+ return formatCommand(command, "%sW1,%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);
+ return formatCommand(command, "%sAGPS%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);
+ return formatCommand(command, "%sF%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);
+ return formatCommand(command, "%sSP%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);
+ return formatCommand(command, "%sDS%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
case Command.TYPE_SOS_NUMBER:
- return formatCommand(command, "{%s}{%s}1,{%s}", this,
+ 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, "{%s}LED{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ return formatCommand(command, "%sLED%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index 75c8b11d3..b8ab134c5 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -117,6 +117,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
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);
@@ -150,9 +154,20 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
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));
- position.set(Position.KEY_STATUS, buf.readUnsignedIntLE());
+ 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
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/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/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/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/main/java/org/traccar/protocol/PluginProtocolDecoder.java b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
index 106889ee0..949c994ee 100644
--- a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
@@ -19,6 +19,7 @@ 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;
@@ -40,17 +41,30 @@ public class PluginProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)(dd)(dd),") // time (hhmmss)
.number("(-?d+.d+),") // longitude
.number("(-?d+.d+),") // latitude
- .number("(d+),") // speed
+ .number("(d+.?d*),") // speed
.number("(d+),") // course
.number("(-?d+),") // altitude
.number("(-?d+),") // satellites
.number("d+,") // type
- .number("(d+),") // odometer
+ .number("(d+.?d*),") // odometer
.number("(d+),") // status
- .expression("[^,]*,")
+ .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("?")
@@ -74,18 +88,50 @@ public class PluginProtocolDecoder extends BaseProtocolDecoder {
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.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, parser.nextInt());
- position.set(Position.KEY_STATUS, parser.nextInt());
- position.set(Position.KEY_EVENT, 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/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/Pt502ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
index ba08b16ae..364ecae86 100644
--- a/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
@@ -46,13 +46,13 @@ 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:
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/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/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 2d476427d..227a9ac91 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -34,6 +35,8 @@ import java.util.List;
public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
+ private ByteBuf photo;
+
public RuptelaProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -247,6 +250,43 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
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/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
index 51967403d..fb0dcf690 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
import org.traccar.Protocol;
@@ -30,7 +31,7 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
super(protocol);
}
- private ByteBuf encodeContent(int type, ByteBuf content) {
+ public static ByteBuf encodeContent(int type, ByteBuf content) {
ByteBuf buf = Unpooled.buffer();
@@ -49,8 +50,14 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
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
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/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
index 7e0e3e110..304f61836 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
@@ -24,6 +24,7 @@ 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;
@@ -31,7 +32,10 @@ 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;
@@ -44,6 +48,30 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
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 {
@@ -64,71 +92,113 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(new Date(json.getInt("time") * 1000L));
+ 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")) {
- String data = json.getString(json.containsKey("data") ? "data" : "payload");
- ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data));
- try {
- int event = buf.readUnsignedByte();
- if (event >> 4 == 0) {
+ JsonObject location;
+ if (json.containsKey("location")) {
+ location = json.getJsonObject("location");
+ } else {
+ location = json;
+ }
- 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.setValid(true);
+ position.setLatitude(getJsonDouble(location, "lat"));
+ position.setLongitude(getJsonDouble(location, "lng"));
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.025);
+ } else {
- } 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.set(Position.KEY_EVENT, event);
-
- 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;
+ 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();
}
- } finally {
- buf.release();
}
if (position.getLatitude() == 0 && position.getLongitude() == 0) {
@@ -136,10 +206,10 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
}
if (json.containsKey("rssi")) {
- position.set(Position.KEY_RSSI, json.getJsonNumber("rssi").doubleValue());
+ position.set(Position.KEY_RSSI, getJsonDouble(json, "rssi"));
}
if (json.containsKey("seqNumber")) {
- position.set(Position.KEY_INDEX, json.getInt("seqNumber"));
+ position.set(Position.KEY_INDEX, getJsonInt(json, "seqNumber"));
}
sendResponse(channel, HttpResponseStatus.OK);
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/StarcomProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
index 90151d061..5ffddb318 100644
--- a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
@@ -18,6 +18,7 @@ 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;
@@ -66,7 +67,7 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(Double.parseDouble(value));
break;
case "velocity":
- position.setSpeed(Integer.parseInt(value));
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(value)));
break;
case "heading":
position.setCourse(Integer.parseInt(value));
@@ -74,8 +75,8 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder {
case "eventid":
position.set(Position.KEY_EVENT, Integer.parseInt(value));
break;
- case "odometer":
- position.set(Position.KEY_ODOMETER, Long.parseLong(value));
+ case "mileage":
+ position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(value) * 1000));
break;
case "satellites":
position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
diff --git a/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
index de8de17f0..8748ecc61 100644
--- a/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
@@ -32,13 +32,24 @@ public class SuntechFrameDecoder extends BaseFrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- 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);
+ 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/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java
index 7e2c20e6f..199885537 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java
@@ -15,7 +15,6 @@
*/
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;
@@ -38,7 +37,6 @@ public class SuntechProtocol extends BaseProtocol {
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new SuntechFrameDecoder());
pipeline.addLast(new StringEncoder());
- pipeline.addLast(new StringDecoder());
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
index e40096a77..978d768be 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * 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.
@@ -15,18 +15,22 @@
*/
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;
@@ -34,6 +38,8 @@ import java.util.TimeZone;
public class SuntechProtocolDecoder extends BaseProtocolDecoder {
+ private String prefix;
+
private int protocolType;
private boolean hbm;
private boolean includeAdc;
@@ -44,6 +50,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public String getPrefix() {
+ return prefix;
+ }
+
public void setProtocolType(int protocolType) {
this.protocolType = protocolType;
}
@@ -232,6 +242,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
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++]));
@@ -268,7 +282,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_TYPE, type);
- if (protocol.equals("ST300") || protocol.equals("ST500") || protocol.equals("ST600")) {
+ if (protocol.startsWith("ST3") || protocol.equals("ST500") || protocol.equals("ST600")) {
index += 1; // model
}
@@ -300,7 +314,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
String io = values[index++];
- if (io.length() == 6) {
+ 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');
@@ -361,6 +375,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
remaining -= attribute.length() + 1;
}
+ index += 1; // checksum
break;
default:
break;
@@ -382,7 +397,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (isIncludeAdc(deviceSession.getDeviceId())) {
for (int i = 1; i <= 3; i++) {
- if (!values[index++].isEmpty()) {
+ if (index < values.length && !values[index++].isEmpty()) {
position.set(Position.PREFIX_ADC + i, Double.parseDouble(values[index - 1]));
}
}
@@ -501,20 +516,149 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
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());
+ }
+
+ int alertId = 0;
+ if (BitUtil.check(mask, 19)) {
+ alertId = buf.readUnsignedByte();
+ if (type == 0x82) {
+ position.set(Position.KEY_ALARM, decodeAlert(alertId));
+ }
+ }
+
+ if (BitUtil.check(mask, 20)) {
+ buf.readUnsignedShort(); // alert mod
+ }
+
+ if (BitUtil.check(mask, 21)) {
+ if (alertId == 59) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(8)));
+ }
+ }
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String[] values = ((String) msg).split(";");
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (buf.getByte(buf.readerIndex() + 1) == 0) {
+
+ return decodeBinary(channel, remoteAddress, buf);
- if (values[0].length() < 5) {
- return decodeUniversal(channel, remoteAddress, values);
- } else if (values[0].startsWith("ST9")) {
- return decode9(channel, remoteAddress, values);
- } else if (values[0].startsWith("ST4")) {
- return decode4(channel, remoteAddress, values);
} else {
- return decode2356(channel, remoteAddress, values[0].substring(0, 5), values);
+
+ 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
index 6dae42ad5..3b4995110 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
@@ -15,6 +15,8 @@
*/
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;
@@ -25,32 +27,49 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder {
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(Command command) {
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ String prefix = getPrefix(channel);
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "SA200CMD;{%s};02;Reboot\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;Reboot\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "SA200GTR;{%s};02;\r", Command.KEY_UNIQUE_ID);
+ 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, "SA200CMD;{%s};02;Enable{%s}\r",
+ return formatCommand(command, prefix + ";%s;02;Enable%s\r",
Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
} else {
- return formatCommand(command, "SA200CMD;{%s};02;Disable{%s}\r",
+ return formatCommand(command, prefix + ";%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);
+ return formatCommand(command, prefix + ";%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);
+ return formatCommand(command, prefix + ";%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);
+ return formatCommand(command, prefix + ";%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);
+ return formatCommand(command, prefix + ";%s;02;Disable2\r", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java b/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
index 2607d7bd1..d218f63ce 100644
--- a/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
@@ -30,11 +30,11 @@ public class SviasProtocolEncoder extends StringProtocolEncoder {
protected Object encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}", Command.KEY_DATA);
+ 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);
+ 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:
diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index e25f9e11a..b75addfae 100644
--- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
@@ -96,6 +96,19 @@ 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(
@@ -225,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 {
@@ -275,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/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 485df833a..cc2868a20 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -157,7 +157,19 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (readable) {
- position.set(Position.KEY_RESULT, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ 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)));
}
@@ -188,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;
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
index a8aa84105..5d7e63920 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
@@ -50,7 +50,7 @@ public class Tk103ProtocolEncoder extends StringProtocolEncoder {
if (alternative) {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatAlt(command, "{%s}", Command.KEY_DATA);
+ return formatAlt(command, "%s", Command.KEY_DATA);
case Command.TYPE_GET_VERSION:
return formatAlt(command, "*about*");
case Command.TYPE_POWER_OFF:
@@ -74,36 +74,36 @@ public class Tk103ProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ALARM_SOS:
return formatAlt(command, command.getBoolean(Command.KEY_ENABLE) ? "*soson*" : "*sosoff*");
case Command.TYPE_SET_CONNECTION:
- return formatAlt(command, "*setip*%s*{%s}*",
- command.getString(Command.KEY_SERVER).replace(".", "*"), Command.KEY_PORT);
+ 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);
+ 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);
+ return formatCommand(command, "(%s%s)", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
case Command.TYPE_GET_VERSION:
- return formatCommand(command, "({%s}AP07)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAP07)", Command.KEY_UNIQUE_ID);
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "({%s}AT00)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAT00)", Command.KEY_UNIQUE_ID);
case Command.TYPE_SET_ODOMETER:
- return formatCommand(command, "({%s}AX01)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAX01)", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "({%s}AP00)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAP00)", 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)));
+ 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, "({%s}AR0000000000)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAR0000000000)", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "({%s}AV010)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAV010)", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "({%s}AV011)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAV011)", Command.KEY_UNIQUE_ID);
case Command.TYPE_OUTPUT_CONTROL:
- return formatCommand(command, "({%s}AV00{%s})", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ return formatCommand(command, "(%sAV00%s)", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index 2a06d35e5..3ce6438f8 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -36,10 +36,18 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN_HEADER = new PatternBuilder()
- .number("#(d+)#") // imei
- .any()
- .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()
@@ -114,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");
@@ -140,6 +161,11 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
position.setTime(dateBuilder.getDate());
+ 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/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/main/java/org/traccar/protocol/TotemProtocolEncoder.java b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
index 3bbb92031..a96dd1ee3 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
@@ -34,9 +34,9 @@ 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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
index 87b44a4b2..4f6854098 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 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.
@@ -23,6 +23,7 @@ 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;
@@ -67,7 +68,17 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
return false;
}
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ 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;
@@ -80,25 +91,39 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
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());
+ if (hardware == 0x413) {
- position.setSpeed(buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
- position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+ 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));
- 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);
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
index 0a2a59e23..7fec0bf8b 100644
--- a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -237,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:
diff --git a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
index 873b22006..a17003fc8 100644
--- a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
@@ -171,12 +171,31 @@ public class UproProtocolDecoder extends BaseProtocolDecoder {
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':
- position.set(Position.KEY_BATTERY_LEVEL,
- Integer.parseInt(data.readSlice(3).toString(StandardCharsets.US_ASCII)) * 0.1);
+ 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,
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/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 70b207e9b..0647afdee 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -249,8 +249,7 @@ 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")) {
Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII));
@@ -267,7 +266,10 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
sendResponse(channel, id, index, "TKQ");
- } else if (type.equals("PULSE") || type.equals("heart") || type.equals("bphrt")) {
+ } else if (type.equalsIgnoreCase("PULSE")
+ || type.equalsIgnoreCase("HEART")
+ || type.equalsIgnoreCase("BLOOD")
+ || type.equalsIgnoreCase("BPHRT")) {
if (buf.isReadable()) {
@@ -279,11 +281,14 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
String[] values = buf.toString(StandardCharsets.US_ASCII).split(",");
int valueIndex = 0;
- if (type.equals("bphrt")) {
+ if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) {
position.set("pressureHigh", values[valueIndex++]);
position.set("pressureLow", values[valueIndex++]);
}
- position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+
+ if (valueIndex <= values.length - 1) {
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+ }
return position;
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
index b433dfd2a..f285267ba 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
@@ -137,33 +137,33 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
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);
+ 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);
+ return formatTextCommand(channel, command, "SOSSMS,%s", Command.KEY_ENABLE);
case Command.TYPE_ALARM_BATTERY:
- return formatTextCommand(channel, command, "LOWBAT,{%s}", Command.KEY_ENABLE);
+ 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);
+ return formatTextCommand(channel, command, "REMOVE,%s", Command.KEY_ENABLE);
case Command.TYPE_SILENCE_TIME:
- return formatTextCommand(channel, command, "SILENCETIME,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "SILENCETIME,%s", Command.KEY_DATA);
case Command.TYPE_ALARM_CLOCK:
- return formatTextCommand(channel, command, "REMIND,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "REMIND,%s", Command.KEY_DATA);
case Command.TYPE_SET_PHONEBOOK:
- return formatTextCommand(channel, command, "PHB,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "PHB,%s", Command.KEY_DATA);
case Command.TYPE_MESSAGE:
- return formatTextCommand(channel, command, "MESSAGE,{%s}", Command.KEY_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);
+ return formatTextCommand(channel, command, "UPLOAD,%s", Command.KEY_FREQUENCY);
case Command.TYPE_SET_TIMEZONE:
- return formatTextCommand(channel, command, "LZ,,{%s}", Command.KEY_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);
+ return formatTextCommand(channel, command, "FLOWER,%s", Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java b/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
index c45edf00d..93086bf8a 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
@@ -32,11 +32,11 @@ public class WialonProtocolEncoder extends StringProtocolEncoder {
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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
index e9bb23d15..21f1ee321 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
@@ -32,17 +32,17 @@ 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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java b/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
index fc849fe15..4f2707c2a 100644
--- a/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
@@ -32,9 +32,9 @@ 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:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
index 938394d6b..69e5b7372 100644
--- a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
@@ -47,7 +47,7 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
.expression("..,") // vendor
.number("d{15},") // imei
.expression("..,") // type
- .number("0,") // reserved
+ .number("[01],") // reserved
.number("(dd)(dd)(dd).d+,") // time (hhmmss)
.expression("([AV]),") // validity
.number("(dd)(dd.d+),") // latitude
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/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java
index 85d9bf62f..e70719b89 100644
--- a/src/test/java/org/traccar/geocoder/GeocoderTest.java
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -32,7 +32,7 @@ public class GeocoderTest {
@Ignore
@Test
public void testGisgraphy() {
- Geocoder geocoder = new GisgraphyGeocoder(new AddressFormat());
+ Geocoder geocoder = new GisgraphyGeocoder(null, 0, new AddressFormat());
String address = geocoder.getAddress(48.8530000, 2.3400000, null);
assertEquals("Rue du Jardinet, Paris, Île-de-France, FR", address);
}
@@ -73,7 +73,7 @@ public class GeocoderTest {
@Ignore
@Test
public void testHere() {
- Geocoder geocoder = new HereGeocoder("", "", null, 0, new AddressFormat());
+ 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);
}
diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java
index 5737b9ff5..ff48527bc 100644
--- a/src/test/java/org/traccar/helper/ChecksumTest.java
+++ b/src/test/java/org/traccar/helper/ChecksumTest.java
@@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
@@ -36,4 +37,13 @@ public class ChecksumTest {
assertEquals(0, Checksum.luhn(63070019470771L));
}
+ @Test
+ public void testModulo256() {
+ assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00})));
+ assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x00})));
+ assertEquals(0xca, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x77, 0x77, 0x77, 0x77, 0x77, 0x77})));
+
+
+ }
+
}
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/protocol/ArnaviBinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java
new file mode 100644
index 000000000..f2940de59
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java
@@ -0,0 +1,38 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArnaviBinaryProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testHeader1Decode() throws Exception {
+
+ ArnaviBinaryProtocolDecoder decoder;
+
+ decoder = new ArnaviBinaryProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "ff22f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+ }
+
+ @Test
+ public void testHeader2Decode() throws Exception {
+
+ ArnaviBinaryProtocolDecoder decoder;
+
+ decoder = new ArnaviBinaryProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "ff23f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java
new file mode 100644
index 000000000..90eb20296
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArnaviFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeValidPackets() throws Exception {
+
+ ArnaviFrameDecoder decoder = new ArnaviFrameDecoder();
+
+ verifyFrame(
+ binary("2441562c563344492c38353136342c3231342c2d312c31392c30303030344634462c30303030303935452c30433030303030322c3836333037313031333034313631382c38393939373031353630333832353236363232462c2a3039"),
+ decoder.decode(null, null, binary("2441562c563344492c38353136342c3231342c2d312c31392c30303030344634462c30303030303935452c30433030303030322c3836333037313031333034313631382c38393939373031353630333832353236363232462c2a30390d0a")));
+
+ verifyFrame(
+ binary("ff22f30c45f5c90f0300"),
+ decoder.decode(null, null, binary("ff22f30c45f5c90f0300")));
+
+ verifyFrame(
+ binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ verifyFrame(
+ binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ verifyFrame(
+ binary("5b01030700e3f16b50747261636361721b5d"),
+ decoder.decode(null, null, binary("5b01030700e3f16b50747261636361721b5d")));
+
+ verifyFrame(
+ binary("5b01030700e3f16b50747261636361721b030700e3f16b50747261636361721b5d"),
+ decoder.decode(null, null, binary("5b01030700e3f16b50747261636361721b030700e3f16b50747261636361721b5d")));
+
+ verifyFrame(
+ binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d"),
+ decoder.decode(null, null, binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d")));
+
+ verifyFrame(
+ binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d"),
+ decoder.decode(null, null, binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d")));
+
+ verifyFrame(
+ binary("5bfd005d"),
+ decoder.decode(null, null, binary("5bfd005d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java
index 6b075facc..79179d4f3 100644
--- a/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java
@@ -3,38 +3,38 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
-public class ArnaviProtocolDecoderTest extends ProtocolTest {
+public class ArnaviTextProtocolDecoderTest extends ProtocolTest {
@Test
public void testDecode() throws Exception {
- ArnaviProtocolDecoder decoder = new ArnaviProtocolDecoder(null);
+ ArnaviTextProtocolDecoder decoder = new ArnaviTextProtocolDecoder(null);
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$AV,V4,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,80.0,56.1,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$AV,V3DI,85164,20707,-1,19,0008C56A,000879AC,0C000002,863071013041618,89997077111301204297,*0B"));
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$AV,V6SD,85164,20708,-1,3,6,37,33,*52"));
- verifyAttributes(decoder, text(
+ verifyAttributes(decoder, buffer(
"$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(
+ verifyNull(decoder, buffer(
"$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(
+ verifyAttributes(decoder, buffer(
"$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(
+ verifyNull(decoder, buffer(
"$AV,V6SD,85164,20452,-1,3,3,5,6769,,SF*5D"));
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$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(
+ verifyPosition(decoder, buffer(
"$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/BceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
index 71211ccfc..9801b56cc 100644
--- a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java
@@ -14,6 +14,9 @@ public class BceProtocolDecoderTest extends ProtocolTest {
"3ab90b71bc1503000300c10bff11"));
verifyPositions(decoder, binary(
+ "cdc3440cf31403001902a58c0a06e0ceb0009f4e4452419417e0ceb08bc0ffcf428014463627b24018104b425b1c508b00d16a9743d188da6e0110ce001455069262002e4c5adabb810200418728157501004229460377000bb4d04b10c000ffff335aa800000000000000000000000000a912963d0042313130303030313236313432303031202020202020202020202020202020203f2946030301f70100007b0400009f130000762700000a26e0ceb06f074e4452419427e0ceb08bc0ffcf42801446ee28b240a40f4b425c1c518a00df414c42d188936eff0fce001455069262002e4c5adabb810200417b28157501004c294603770008b4d04b10c000ffff4c5a89000000000000000000000000009a2c8c3b004231313030303031323631343230303120202020202020202020202020202020492946030301f80100007c040000a9130000802700009477e0ceb08bc0ffcf42801446eb2fb2405c0d4b425d1c53830089e07e43d188a56eff0fce001455069262002eb35700bb810200415227167501007e294603780000b4d04b10c000ffff995700000000000000000000000000008bd9f43b004231313030303031323631343230303120202020202020202020202020202020802946030301fd01000081040000e0130000b72700000a86e0ceb064464e4452410a96e0ceb000a54e4452410aa6e0ceb000914e4452410ab6e0ceb068334e4452410ac6e0ceb0009f4e4452410ac6e0ceb06f074e445241f4"));
+
+ verifyPositions(decoder, binary(
"cc2c5792c6160300b000a5520aa6c813ae64465343513840a7c813ae0bc0fd800080040036093f427884ea41001c900e00000000009088c562a301024156d12a004c00006df80c0000000086fb0200562a08005a000000000ac6c813ae0091534351380af6c813ae009f534351380af6c813ae6f075343513840f7c813ae0bc0fd800080040036093f427884ea41001c900e00000000009088f162a301024156d12a004c00006df80c0000000086fb0200562a08005a000000003f"));
verifyPositions(decoder, false, binary(
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..4aa50e56b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
@@ -0,0 +1,34 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class BlueProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ BlueProtocolDecoder decoder = new BlueProtocolDecoder(null);
+
+ verifyAttribute(decoder, binary(
+ "AA0056860080E3E79E0C811F80000114020207170520011F00407F8005EE1938113B270000000000000000140202071705005AC7A621121F0002000100B7000080110000000000001A3A0000000001F400000000000078"),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyAttribute(decoder, binary(
+ "AA004A860080E3E79E20015FBE40148005EE193B113B263700000000000000140202080C09005AC7A621125F0002000000BB000000000000000000001A3A0007000001F400000000000008"),
+ Position.KEY_IGNITION, true);
+
+ verifyAttribute(decoder, binary(
+ "AA004A860080E3E79E200160BE40148005EE193B113B263700000000000000140202080C13005AC7A62112600002000000B7000000110000000000001A3A0007000001F400000000000012"),
+ Position.KEY_STATUS, 0x11);
+
+ verifyPosition(decoder, binary(
+ "aa00550000813f6f840b840380001032000000002001030040008005ee1938113b26f300000000000000140114082833044d27602112030002000000b70000020000000000000000650000001601f4000000000000e4"));
+
+ verifyPosition(decoder, binary(
+ "aa0055860080e3e79e0b840f800010320000000020010f0040008005ee197f113b26e800000000000000130c11091a2b005ac7a621120f0002000000b7000002000000000000001a3a0000000001f40000000000003f"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
index ae5e9353c..8977cf194 100644
--- a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java
@@ -14,6 +14,9 @@ public class DmtProtocolDecoderTest extends ProtocolTest {
"0255003300001b00003335333232393032373533393235310038393931353030303030303030313330343539340000000403041910780603"));
verifyPositions(decoder, false, binary(
+ "025504ab013d00c21a00004829900c0300154929900cbd163617b08a94c7fa003c07032c131a0302080300000000000300060f019b14037e0e0463000558140607213d00c31a0000ca29900c030015ca29900ca3033817bbb895c71401be0603b310190302080300000000000300060f019b14036d0e0463000558140607213d00c41a0000472a900c030015472a900c8d453817423e96c7fa000200040013270302080300000000000300060f019b1403840e0463000546140606213d00c51a0000c52a900c030015c52a900c184c3817c35296c724010400050016180302080300000000000300060f019b1403750e0463000547140606213d00c61a0000462b900c030015462b900cbd8a361703b495c710018c07085a10210302080300000000000300060f019b1403630e0463000546140606213d00c71a0000c52b900c030015c52b900cf6d63517455a94c7e9004c05035a10240302080300000000000300060f019b14036e0e0463000545140606213d00c81a00004b2c900c0300154b2c900c766d3517ddf093c7320107000d00102e0302080300000000000300060f019b1403750e046300054314060521"));
+
+ verifyPositions(decoder, false, binary(
"02551040000eaca40d00d2b8e562c51f9912f39a6bee00007e420091090903070100000000008b1065360000000000007fd401c4fcf2feffffffffffffffffee0000003f1b"));
verifyPositions(decoder, false, binary(
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
index e3cff9525..e24da3173 100644
--- a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
@@ -14,6 +14,10 @@ public class EelinkProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"454C0027E753035254407167747167670100180002035254407167747100200205020500010432000086BD"));
+ verifyAttribute(decoder, binary(
+ "6767070006000e0077035d"),
+ Position.KEY_IGNITION, true);
+
verifyAttributes(decoder, binary(
"676707006502df5c89fde800bc3fa8030302005555045b555555057a5555550b225555550c105c55550d115555550e7e5555550f4555555510017b5555112b5555551f01ed5555208005b0012100005555407ad000004237f5555589000000498a0000aef78b00000000"));
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
index 2e75a180f..88a460854 100644
--- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest {
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);
diff --git a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
index e2be99cb9..7e1ccb79d 100644
--- a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java
@@ -20,6 +20,9 @@ public class GatorProtocolDecoderTest extends ProtocolTest {
GatorProtocolDecoder decoder = new GatorProtocolDecoder(null);
verifyAttributes(decoder, binary(
+ "242480002600341cad190917022021812497260280594200000000c047010000135400009bb600ff00b90d"));
+
+ verifyAttributes(decoder, binary(
"2424800026364101b31608041108380273453415301532000000008000010000122800000124000000c40d"));
verifyNull(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
index da8d8ff5a..63a6e06d0 100644
--- a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class Gps103ProtocolDecoderTest extends ProtocolTest {
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);
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index caa354e54..672711f22 100644
--- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -17,6 +17,24 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
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"));
@@ -296,6 +314,17 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
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/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
index 31e4f2dee..fe4988b5a 100644
--- a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -11,9 +11,18 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
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"));
@@ -100,6 +109,9 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
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#"));
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
index 6033bc744..b624f69ab 100644
--- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
@@ -16,6 +16,9 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0"));
+ verifyNotNull(decoder, binary(
+ "c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0"));
+
verifyPosition(decoder, binary(
"c000000060aa000000000000fa8000000031393037303431363434323700e9900affd61c1b00000000003a000000010015ffffff0000000000000004c2ffffffffff0005000a0d080000ca6a000900155741555a5a5a344730454e313133373233c0"));
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
index d4ae3b50c..c5e8d5b2a 100644
--- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class HuabaoProtocolDecoderTest extends ProtocolTest {
@@ -13,6 +14,17 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest {
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"));
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
index 4126c4e4f..8733e9034 100644
--- a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
@@ -11,6 +11,18 @@ public class ItsProtocolDecoderTest extends ProtocolTest {
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,*"));
diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
index ae0948987..e695624a9 100644
--- a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
@@ -11,6 +11,10 @@ public class Jt600FrameDecoderTest extends ProtocolTest {
Jt600FrameDecoder decoder = new Jt600FrameDecoder();
verifyFrame(
+ binary("2478905197081711003405101917164812492365028134847d0a1c000002640c0000000020c032759600731000000f0f0f0f0f0f0f0f0f0f000702850274"),
+ decoder.decode(null, null, binary("2478905197081711003405101917164812492365028134847d0a1c000002640c0000000020c032759600731000000f0f0f0f0f0f0f0f0f0f000702850274")));
+
+ verifyFrame(
binary("24315011626912001b21111718095900000000000000000e0000005c000000000000000000"),
decoder.decode(null, null, binary("24315011626912001b21111718095900000000000000000e0000005c00000000000000000024315011626912001b22111708130400000000000000000e0000005a00000000000000000024315011626912001b22111708140400000000000000000e0000005a000000723e18a61b01")));
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
index 718000a44..4a3c752cd 100644
--- a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
@@ -12,6 +12,12 @@ public class Jt600ProtocolDecoderTest extends ProtocolTest {
Jt600ProtocolDecoder decoder = new Jt600ProtocolDecoder(null);
verifyPositions(decoder, binary(
+ "2478807035371711003419081920061851380856003256223b000000000000070000000020c0ff965d54de1800000f0f0f0f0f0f0f0f0f0f02d600ea0a21"));
+
+ verifyPositions(decoder, binary(
+ "2475201509261611002313101503464722331560113555309F00000000002D0500CB206800F064109326381A03"));
+
+ verifyPositions(decoder, binary(
"2475810297431713003401010000030100000000000000000e000000000001000000000020e0641aba1b6f1b00000f0f0f0f0f0f0f0f0f0f000001942803"));
verifyPositions(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
index da5a81144..94f4c8202 100644
--- a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
@@ -10,6 +10,12 @@ public class MeiligaoProtocolDecoderTest extends ProtocolTest {
MeiligaoProtocolDecoder decoder = new MeiligaoProtocolDecoder(null);
+ verifyPosition(decoder, binary(
+ "2424011e143190975469ff99993130343634382e3030302c562c303735332e353338332c4e2c30393832322e313737382c452c302e30302c302c3230303132302c2c2a31417c302e307c307c363430307c303030302c303030302c303130312c303238467c30323038303030353137444630304633363838467c30387c30303030314242367c30307c2520205e59454e53414241494348414924534f4e474b52414e244d522e5e5e3f3b363030373634333130303530303337333835333d3135303531393637303631343d3f2b2020202020202020202020202032342020202020202020202020203120202020202020202020202030303034313131202030303130302020202020202020202020202020202020202020203f7b850d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424008d143190975469ff99993130343634312e3030302c562c303735332e353338332c4e2c30393832322e313737382c452c302e30302c302c3230303132302c2c2a31337c302e307c307c323430307c303030302c303030302c303130302c303238397c30323038303030353137444630304633363838467c30387c30303030314242367c3030be980d0a"));
+
verifyNull(decoder, binary(
"24240012254748594772ff080002ffff0d0a"));
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
index 3e05d5243..9f9da26ca 100644
--- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest {
MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(null);
+ verifyPosition(decoder, buffer(
+ "$$O160,863835028611502,AAA,35,7.887840,98.375193,200202020238,A,12,4,0,279,0.6,45,32121,442492,520|3|12DF|015273E2,0000,0000|0000|0000|018D|04F0,00000001,,1,0000*F3"));
+
verifyNull(decoder, binary(
"242441313038362c3836343530373033313231393937342c4430302c3138303232343037323631345f4331453130395f4e31553144312e6a70672c31342c302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00cca69ac8d06e3348569884db4845021b498a60371494008692980119a8ca7a5342101a5cd5221a0ab312ed1ee68b943e80dce2a467ffd0c806a48e592270f13b230e841a0096eeea7bb09e6c85b667033552800069c2980e14f15422418a916ad099228a95455089505584140993a2d5fb598a7cae72bd8fa536ae892e8e69e2b9d971168a459fffd1ece8a0028a006b534f4a68ce5b9130a89ab444919a61a6c634d34d21894952310d25002514084a4a00ffd2d2349564086929082929805250025140094940c4a4a04251400949408292819fffd3cca31591a098a5c62801a45464531098a69a6210d371400629a6980628eb400c64cd3791c1aa16c491479393563343105424fcc4d007ffd4c3463c0a94500381a5e3b8cd000c99e57f2a8f3835402834e0d4d08914d4aa6ac4c954d4aad4c4c955aa647a6496236ab51b552132e412e383d3f955b0722b09ab32a0c5a2a0d0ffd5ece8a0028a0061a6d5193dc8daa36ab422334c34c634"));
diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
index c0ce67cb6..5898b74a8 100644
--- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
@@ -13,6 +13,9 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"ab10150076f1320003100133353534363530373130323933303602105a"));
+ verifyNotNull(decoder, binary(
+ "ab1024009b3f9742011001383635323039303336333430303235113154cfc95d0a00000080d0c95d0a000000"));
+
verifyPosition(decoder, binary(
"ab103f007e2533000110013335353436353037313032393330360930e09d245d210100000924b49e245d01025b201620e6c03b1ef367420400000000aa026d00c90e0000100110"));
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/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/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/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/PluginProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java
index 582ed9a5a..0fe3a3a01 100644
--- a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class PluginProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,13 @@ public class PluginProtocolDecoderTest extends ProtocolTest {
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"));
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/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/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
index 12f63ef7d..4c7b09bcc 100644
--- a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class RuptelaProtocolDecoderTest extends ProtocolTest {
RuptelaProtocolDecoder decoder = new RuptelaProtocolDecoder(null);
verifyNull(decoder, binary(
+ "03fc0003142b0c152acd2502003544444131464144000a0000ffd8ffe000104a46494600010100000100010000ffdb00c50006040506050406060506070706080a100a0a09090a140e0f0c1017141818171416161a1d251f1a1b231c1616202c20232627292a29191f2d302d283025282928010707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828020707070a080a130a0a13281a161a2828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828ffc000110800f0014003012200021101031102ffc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffdd00040000ffda000c03010002110311003f00e27534fde484fa66950079add40e153754d73892794f552e00a6da0c4b282794f947d2aa92b2b1d7887795fb9b5a4200eee7e957e6bdfb2b0f2cb6eff669cba4dee99656d25f5acb02dd209622e38753d08fc315177cf151562e337196e8e46f5352cb5d6603cd52ebeb8c1adcb4be82e07eedc67d0f06b8f071daa64618c8383508573ae991251891430f7a836cd07fa97de9fdc7fe86b0adf51b8830377989e8d5a96daa413603131bfa350061fc49d41adbc39e5edc34ee14f7c639af1b91f2719aef7e2c5d4bf6cb780b7ee7607500f19e735e7a06e3c9e2b4a4b4b88b51b2aaf14bbc1351226796a79e3815b81283c500d460d381a405988d7a6785ad8c3a241bc9f9f2e47d7ffad5e73a5db35dddc30a757603e83bd7aba011c6a8bc2a8000a89e8807b1551815a5636fba38ce3af359248cd7516298862ff7456713fc58"));
+
+ verifyNull(decoder, binary(
"002e000315bc70d3e2ff0f4f42443130302e30312e30382e30300000c2b30ea77e430000601b000001f40000003c00144aa0"));
verifyAttributes(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
index 5868b07df..613a89b96 100644
--- a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java
@@ -14,10 +14,13 @@ public class RuptelaProtocolEncoderTest extends ProtocolTest {
Command command = new Command();
command.setDeviceId(1);
command.setType(Command.TYPE_CUSTOM);
- command.set(Command.KEY_DATA, " Setio 2,1");
+ 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/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
index 7e7fc24bc..4ab343876 100644
--- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
@@ -3,6 +3,7 @@ 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 {
@@ -11,6 +12,16 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest {
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 }")));
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/SuntechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
index ccd9139f4..1c84b5c89 100644
--- a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java
@@ -11,6 +11,10 @@ public class SuntechFrameDecoderTest extends ProtocolTest {
SuntechFrameDecoder decoder = new SuntechFrameDecoder();
verifyFrame(
+ binary("81004e05200013383fffff3401000301130a0512080400000000000000000000000047f9d5846a06810072225214010100020300a8002604c1000004b000000470000025a100000000000025c4000000a6"),
+ decoder.decode(null, null, binary("81004e05200013383fffff3401000301130a0512080400000000000000000000000047f9d5846a06810072225214010100020300a8002604c1000004b000000470000025a100000000000025c4000000a6")));
+
+ verifyFrame(
binary("5354363030414c563b303038373238333237"),
decoder.decode(null, null, binary("5354363030414c563b3030383732383332370d")));
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index 304bc0fed..692a13131 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -13,15 +13,24 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
decoder.setHbm(true);
decoder.setIncludeAdc(true);
+
+ verifyAttribute(decoder, buffer(
+ "ST600STT;008594432;20;492;20200212;18:58:30;060bb0e1;334;20;36bb;45;+19.337897;-099.064489;000.398;000.00;12;1;5049883;13.61;100100;2;1198;013762;4.2;1;4.68"),
+ Position.PREFIX_ADC + 1, 4.68);
+
decoder.setIncludeTemp(true);
- verifyPosition(decoder, text(
+ 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, text(
+ 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, text(
+ 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;:;:"));
}
@@ -34,7 +43,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
decoder.setHbm(true);
decoder.setIncludeRpm(true);
- verifyAttribute(decoder, text(
+ 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);
@@ -47,7 +56,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
decoder.setHbm(true);
- verifyAttribute(decoder, text(
+ 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);
@@ -58,135 +67,152 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
- verifyPosition(decoder, text(
+ verifyAttribute(decoder, binary(
+ "82004d05800000553fffff360100100114020410293902ccccf102dc007b00053c00476fa18469e87f000000000b0100003b00081d00000113f3f8010000049e00000000000000001d00000113f3f801"),
+ Position.KEY_DRIVER_UNIQUE_ID, "1d00000113f3f801");
+
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ verifyPosition(decoder, buffer(
"ST910;Emergency;205576803;500;20170319;12:15:22;-22.846014;-046.322176;000.000;000.00;0;2"));
- verifyPosition(decoder, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ verifyNull(decoder, buffer(
"SA200ALV;317652"));
- verifyPosition(decoder, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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, text(
+ 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/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
index f21acdee7..c0511f2a1 100644
--- a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class T55ProtocolDecoderTest extends ProtocolTest {
@@ -13,6 +14,10 @@ public class T55ProtocolDecoderTest extends ProtocolTest {
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"));
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
index b515fbdc6..827e12a96 100644
--- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -16,6 +16,9 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
"000F313233343536373839303132333435"));
verifyPositions(decoder, false, binary(
+ "000000000000004f0c01060000004755555555777730362e343b30342e323b30302e303b30302e303b30302e303b30302e303b30302e303b30302e303b30312e333b30302e303b31302e373b30302e303b5353530d0a010000e371"));
+
+ verifyPositions(decoder, false, binary(
"00000000000000100C010600000008010300010015D5C5010000D988"));
verifyPositions(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
index d1dad8c92..82e0e0d88 100644
--- a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java
@@ -23,7 +23,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeEngineStop() throws Exception {
+ public void testEncodeEngineStop() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -36,7 +36,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodePositionSingle() throws Exception {
+ public void testEncodePositionSingle() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -49,7 +49,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodePositionPeriodic() throws Exception {
+ public void testEncodePositionPeriodic() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -63,7 +63,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodePositionStop() throws Exception {
+ public void testEncodePositionStop() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -76,7 +76,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeGetVersion() throws Exception {
+ public void testEncodeGetVersion() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -89,7 +89,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeRebootDevice() throws Exception {
+ public void testEncodeRebootDevice() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -102,7 +102,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeSetOdometer() throws Exception {
+ public void testEncodeSetOdometer() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -115,7 +115,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodePositionSingleAlternative() throws Exception {
+ public void testEncodePositionSingleAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -128,7 +128,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodePositionPeriodicAlternative() throws Exception {
+ public void testEncodePositionPeriodicAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -141,7 +141,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodePositionStopAlternative() throws Exception {
+ public void testEncodePositionStopAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -154,7 +154,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeGetVersionAlternative() throws Exception {
+ public void testEncodeGetVersionAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -167,7 +167,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeRebootDeviceAlternative() throws Exception {
+ public void testEncodeRebootDeviceAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -180,7 +180,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeIdentificationAlternative() throws Exception {
+ public void testEncodeIdentificationAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -193,7 +193,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeSosOnAlternative() throws Exception {
+ public void testEncodeSosOnAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -207,7 +207,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeSosOffAlternative() throws Exception {
+ public void testEncodeSosOffAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -221,7 +221,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeCustom() throws Exception {
+ public void testEncodeCustom() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null);
@@ -235,7 +235,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeCustomAlternative() throws Exception {
+ public void testEncodeCustomAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -249,7 +249,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeSetConnectionAlternative() throws Exception {
+ public void testEncodeSetConnectionAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
@@ -264,7 +264,7 @@ public class Tk103ProtocolEncoderTest extends ProtocolTest {
}
@Test
- public void testEncodeSosNumberAlternative() throws Exception {
+ public void testEncodeSosNumberAlternative() {
Tk103ProtocolEncoder encoder = new Tk103ProtocolEncoder(null, true);
diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
index 185c3c368..216e65a5d 100644
--- a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java
@@ -11,6 +11,10 @@ public class Tlt2hProtocolDecoderTest extends ProtocolTest {
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"),
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/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
index bd1fc71f4..f90497292 100644
--- a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class TzoneProtocolDecoderTest extends ProtocolTest {
TzoneProtocolDecoder decoder = new TzoneProtocolDecoder(null);
verifyAttributes(decoder, binary(
+ "545A004B2424041302000000086706003324776413030C0A1A2900180513030C0A1A25080F7E1028CAC830000A000F0000000005000AA53201633D05046000010009AA201737019408973B0032B0260D0A"));
+
+ verifyAttributes(decoder, binary(
"545a005b24240406010800000866050033819630120911071824000472bd8e5b0008aac01b07019b04bb002f00040b06161154000e100132ff2006161152000e080096ff4606161151000e1e0101ff1406161156000db6405bff490024469e0d0a"));
verifyAttributes(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
index d3d0429d6..01d63bfa3 100644
--- a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
@@ -12,15 +12,15 @@ public class UlbotechFrameDecoderTest extends ProtocolTest {
UlbotechFrameDecoder decoder = new UlbotechFrameDecoder();
- assertEquals(
+ verifyFrame(
binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"),
decoder.decode(null, null, binary("f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8")));
- assertEquals(
+ verifyFrame(
binary("2a545330312c33353430343330353133383934363023"),
decoder.decode(null, null, binary("2a545330312c33353430343330353133383934363023")));
- assertEquals(
+ 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
index 1c29ccd4a..8c820183b 100644
--- a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
@@ -17,6 +17,10 @@ public class UlbotechProtocolDecoderTest extends ProtocolTest {
"*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(
diff --git a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
index e43ff322e..e758725f2 100644
--- a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class UproProtocolDecoderTest extends ProtocolTest {
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);
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/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
index 539e63253..88acd8222 100644
--- a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
@@ -16,6 +16,12 @@ public class WatchProtocolDecoderTest extends ProtocolTest {
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(
@@ -24,6 +30,9 @@ public class WatchProtocolDecoderTest extends ProtocolTest {
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]"));
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
index 798213c7b..a4a795050 100644
--- a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
+++ b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java
@@ -78,6 +78,10 @@ public class WatchProtocolEncoderTest extends ProtocolTest {
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/Xrb28ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
index 0f15f31b4..20adacd6b 100644
--- a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java
@@ -27,6 +27,9 @@ public class Xrb28ProtocolDecoderTest extends ProtocolTest {
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/swagger.json b/swagger.json
index 917892e65..e252d36c4 100644
--- a/swagger.json
+++ b/swagger.json
@@ -1,7 +1,7 @@
{
"swagger": "2.0",
"info": {
- "version": "4.6",
+ "version": "4.8",
"title": "traccar"
},
"host": "demo.traccar.org",
@@ -1503,7 +1503,8 @@
"type": "number"
},
"network": {
- "type": "string"
+ "type": "object",
+ "additionalProperties": true
},
"attributes": {
"type": "object",
diff --git a/traccar-web b/traccar-web
-Subproject 3dcc3e9186467dd5c0be50d989161cee4e26fd2
+Subproject e8479d77f13acc3b3738a180a7990b06c495f1b