diff options
990 files changed, 13402 insertions, 4389 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 03a3831b9..ba524068f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -36,7 +36,7 @@ Provide as much details as possible, including log fragments, operating system a Before creating a feature request make sure that the feature or modification that you are requesting is not yet implemented. -Search reposiroty to ensure that there is no existing issues for your request. If there is, add a new comment on that issue. +Search repository to ensure that there is no existing issues for your request. If there is, add a new comment on that issue. Provide as much details as possible, including use case for your feature and any benefits that you can think of. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ceb88a7c..a8e4c5369 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 @@ -28,13 +28,13 @@ jobs: - run: ./gradlew build - uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 18 cache: npm cache-dependency-path: | traccar-web/package-lock.json traccar-web/modern/package-lock.json - run: | - wget -q http://cdn.sencha.com/cmd/7.1.0.15/no-jre/SenchaCmd-7.1.0.15-linux-i386.sh.zip + wget -q https://trials.sencha.com/cmd/7.6.0/SenchaCmd-7.6.0.87-linux-amd64.sh.zip unzip SenchaCmd-*.zip ./SenchaCmd-*.sh -q echo "$HOME/bin/Sencha/Cmd/" >> $GITHUB_PATH @@ -42,14 +42,16 @@ jobs: - run: | sudo dpkg --add-architecture i386 sudo apt-get update + sudo apt-get install libgcc-s1:i386 libstdc++6:i386 sudo apt-get install innoextract makeself wine32 s3cmd - name: Build installers working-directory: ./setup run: | wget -q http://files.jrsoftware.org/is/5/isetup-5.5.6.exe - wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4+8/OpenJDK17U-jdk_x64_windows_hotspot_17.0.4_8.zip - wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4+8/OpenJDK17U-jdk_x64_linux_hotspot_17.0.4_8.tar.gz - wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.4+8/OpenJDK17U-jdk_arm_linux_hotspot_17.0.4_8.tar.gz + wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_x64_windows_hotspot_17.0.6_10.zip + wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_x64_linux_hotspot_17.0.6_10.tar.gz + wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_arm_linux_hotspot_17.0.6_10.tar.gz + wget -q https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6+10/OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.6_10.tar.gz ./package.sh ${{ github.event.inputs.version }} - name: Upload installers working-directory: ./setup diff --git a/.gitignore b/.gitignore index c005ef9e4..2d03a9625 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ nbactions.xml .gradle out build +.vscode +bin diff --git a/build.gradle b/build.gradle index 91ba2cd5c..3677f8f94 100644 --- a/build.gradle +++ b/build.gradle @@ -1,57 +1,61 @@ plugins { id "java" id "checkstyle" - id "com.google.protobuf" version "0.8.19" - id "org.kordamp.gradle.project-enforcer" version "0.10.0" + id "com.google.protobuf" version "0.9.4" + id "org.kordamp.gradle.project-enforcer" version "0.13.0" } repositories { mavenCentral() } -ext { - guiceVersion = "5.1.0" - jettyVersion = "10.0.12" // jetty 11 javax to jakarta - jerseyVersion = "2.37" // jersey 3 javax to jakarta - jacksonVersion = "2.13.4" // same version as jersey-media-json-jackson dependency - protobufVersion = "3.21.9" -} - sourceCompatibility = "11" compileJava.options.encoding = "UTF-8" jar.destinationDirectory = file("$projectDir/target") checkstyle { - toolVersion = "10.3.4" + toolVersion = "10.12.5" configFile = "gradle/checkstyle.xml" as File checkstyleTest.enabled = false } -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:$protobufVersion" - } -} - enforce { rule(enforcer.rules.EnforceBytecodeVersion) { r -> r.maxJdkVersion = "11" } } +ext { + guiceVersion = "7.0.0" + jettyVersion = "11.0.19" + jerseyVersion = "3.1.5" + jacksonVersion = "2.15.3" // same version as jersey-media-json-jackson dependency + protobufVersion = "3.25.2" + jxlsVersion = "2.14.0" + junitVersion = "5.10.1" +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:$protobufVersion" + } +} + dependencies { - implementation "commons-codec:commons-codec:1.15" - implementation "com.h2database:h2:2.1.214" - implementation "com.mysql:mysql-connector-j:8.0.31" - implementation "org.postgresql:postgresql:42.5.1" - implementation "com.microsoft.sqlserver:mssql-jdbc:11.2.1.jre11" - implementation "com.zaxxer:HikariCP:5.0.1" - implementation "io.netty:netty-all:4.1.85.Final" - implementation "org.slf4j:slf4j-jdk14:2.0.5" + implementation "commons-codec:commons-codec:1.16.0" + implementation "com.h2database:h2:2.2.224" + implementation "com.mysql:mysql-connector-j:8.2.0" + implementation "org.mariadb.jdbc:mariadb-java-client:3.3.2" + implementation "org.postgresql:postgresql:42.7.1" + implementation "com.microsoft.sqlserver:mssql-jdbc:12.4.2.jre11" + implementation "com.zaxxer:HikariCP:5.1.0" + implementation "io.netty:netty-all:4.1.104.Final" + implementation "org.slf4j:slf4j-jdk14:2.0.11" implementation "com.google.inject:guice:$guiceVersion" implementation "com.google.inject.extensions:guice-servlet:$guiceVersion" implementation "org.owasp.encoder:encoder:1.2.3" - implementation "org.glassfish:jakarta.json:1.1.6" + implementation "org.glassfish:jakarta.json:2.0.1" + implementation "com.sun.mail:jakarta.mail:2.0.1" implementation "org.eclipse.jetty:jetty-server:$jettyVersion" implementation "org.eclipse.jetty:jetty-servlet:$jettyVersion" implementation "org.eclipse.jetty:jetty-servlets:$jettyVersion" @@ -62,35 +66,37 @@ dependencies { implementation "org.glassfish.jersey.containers:jersey-container-servlet:$jerseyVersion" implementation "org.glassfish.jersey.media:jersey-media-json-jackson:$jerseyVersion" implementation "org.glassfish.jersey.inject:jersey-hk2:$jerseyVersion" - implementation "org.glassfish.hk2:guice-bridge:2.6.1" // same version as jersey-hk2 + implementation "org.glassfish.hk2:guice-bridge:3.0.5" // same version as jersey-hk2 implementation "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:$jacksonVersion" - implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr353:$jacksonVersion" - implementation "org.liquibase:liquibase-core:4.17.2" - implementation "com.sun.mail:jakarta.mail:1.6.7" - implementation "org.jxls:jxls:2.4.7" // needs upgrade (wait for jexl 4) - implementation "org.jxls:jxls-poi:1.0.16" // needs upgrade (wait for jexl 4) - implementation "org.apache.velocity:velocity:1.7" // needs upgrade - implementation "org.apache.velocity:velocity-tools:2.0" // needs upgrade + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jakarta-jsonp:$jacksonVersion" + implementation "org.liquibase:liquibase-core:4.23.2" // upgrade has issues + implementation "org.apache.commons:commons-jexl3:3.3" + implementation "org.jxls:jxls:$jxlsVersion" + implementation "org.jxls:jxls-poi:$jxlsVersion" + implementation "org.apache.velocity:velocity-engine-core:2.3" + implementation "org.apache.velocity.tools:velocity-tools-generic:3.1" implementation "org.apache.commons:commons-collections4:4.4" - implementation "org.mnode.ical4j:ical4j:3.2.7" + implementation "org.mnode.ical4j:ical4j:3.2.14" implementation "org.locationtech.spatial4j:spatial4j:0.8" implementation "org.locationtech.jts:jts-core:1.19.0" - implementation "net.java.dev.jna:jna-platform:5.12.1" - implementation "com.github.jnr:jnr-posix:3.1.16" + implementation "net.java.dev.jna:jna-platform:5.14.0" + implementation "com.github.jnr:jnr-posix:3.1.18" implementation "com.google.protobuf:protobuf-java:$protobufVersion" - implementation "javax.xml.bind:jaxb-api:2.3.1" - implementation "com.sun.xml.bind:jaxb-core:3.0.2" // needs upgrade - implementation "com.sun.xml.bind:jaxb-impl:3.0.2" // needs upgrade - implementation "javax.activation:activation:1.1.1" - implementation "com.amazonaws:aws-java-sdk-sns:1.12.349" - implementation "org.apache.kafka:kafka-clients:3.3.1" - implementation 'com.hivemq:hivemq-mqtt-client:1.3.0' - implementation("com.google.firebase:firebase-admin:9.1.1") { - exclude group: 'com.google.cloud', module: 'google-cloud-firestore' - exclude group: 'com.google.cloud', module: 'google-cloud-storage' - } - testImplementation "junit:junit:4.13.2" - testImplementation "org.mockito:mockito-core:4.+" + implementation "com.amazonaws:aws-java-sdk-sns:1.12.636" + implementation "org.apache.kafka:kafka-clients:3.6.1" + implementation "com.hivemq:hivemq-mqtt-client:1.3.3" + implementation "redis.clients:jedis:5.1.0" + implementation "com.google.firebase:firebase-admin:9.2.0" + implementation "com.nimbusds:oauth2-oidc-sdk:11.9.1" + implementation "com.rabbitmq:amqp-client:5.20.0" + implementation "com.warrenstrange:googleauth:1.5.0" + testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + testImplementation "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + testImplementation "org.mockito:mockito-core:5.8.0" +} + +test { + useJUnitPlatform() } task copyDependencies(type: Copy) { @@ -103,7 +109,7 @@ jar { manifest { attributes( "Main-Class": "org.traccar.Main", - "Implementation-Version": "5.5", + "Implementation-Version": "5.12", "Class-Path": configurations.runtimeClasspath.files.collect { "lib/$it.name" }.join(" ")) } } diff --git a/gradle/checkstyle.xml b/gradle/checkstyle.xml index a6a6f0ff7..bb89450d6 100644 --- a/gradle/checkstyle.xml +++ b/gradle/checkstyle.xml @@ -114,7 +114,7 @@ <module name="EmptyStatement"/> <module name="EqualsHashCode"/> <module name="IllegalInstantiation"/> - <module name="InnerAssignment"/> + <!--<module name="InnerAssignment"/>--> <module name="MissingSwitchDefault"/> <module name="SimplifyBooleanExpression"/> <module name="SimplifyBooleanReturn"/> diff --git a/schema/changelog-5.10.xml b/schema/changelog-5.10.xml new file mode 100644 index 000000000..63988b14a --- /dev/null +++ b/schema/changelog-5.10.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" + logicalFilePath="changelog-5.10"> + + <changeSet author="author" id="changelog-5.10"> + + <addColumn tableName="tc_users"> + <column name="totpkey" type="VARCHAR(64)" /> + </addColumn> + + </changeSet> + +</databaseChangeLog> diff --git a/schema/changelog-5.11.xml b/schema/changelog-5.11.xml new file mode 100644 index 000000000..e59df9249 --- /dev/null +++ b/schema/changelog-5.11.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" + logicalFilePath="changelog-5.11"> + + <changeSet author="author" id="changelog-5.11"> + + <addColumn tableName="tc_users"> + <column name="temporary" type="BOOLEAN" defaultValueBoolean="false" /> + </addColumn> + + </changeSet> + +</databaseChangeLog> diff --git a/schema/changelog-5.7.xml b/schema/changelog-5.7.xml new file mode 100644 index 000000000..ad15ac48c --- /dev/null +++ b/schema/changelog-5.7.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" + logicalFilePath="changelog-5.7"> + + <changeSet author="author" id="changelog-5.7"> + + <addColumn tableName="tc_notifications"> + <column name="commandid" type="INT" /> + </addColumn> + + <addForeignKeyConstraint + baseTableName="tc_notifications" + baseColumnNames="commandid" + constraintName="fk_notifications_commandid" + referencedTableName="tc_commands" + referencedColumnNames="id" + onDelete="CASCADE" /> + + </changeSet> + +</databaseChangeLog> diff --git a/schema/changelog-5.8.xml b/schema/changelog-5.8.xml new file mode 100644 index 000000000..ec54bf17f --- /dev/null +++ b/schema/changelog-5.8.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" + logicalFilePath="changelog-5.8"> + + <changeSet author="author" id="changelog-5.8"> + + <dropColumn tableName="tc_devices" columnName="geofenceids" /> + <addColumn tableName="tc_positions"> + <column name="geofenceids" type="VARCHAR(128)" /> + </addColumn> + + </changeSet> + +</databaseChangeLog> diff --git a/schema/changelog-5.9.xml b/schema/changelog-5.9.xml new file mode 100644 index 000000000..50a3d3aaa --- /dev/null +++ b/schema/changelog-5.9.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<databaseChangeLog + xmlns="http://www.liquibase.org/xml/ns/dbchangelog" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog + http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" + logicalFilePath="changelog-5.9"> + + <changeSet author="author" id="changelog-5.9"> + + <addColumn tableName="tc_devices"> + <column name="calendarid" type="INT" /> + </addColumn> + + <addForeignKeyConstraint + baseTableName="tc_devices" + baseColumnNames="calendarid" + constraintName="fk_devices_calendarid" + referencedTableName="tc_calendars" + referencedColumnNames="id" + onDelete="SET NULL" + onUpdate="RESTRICT" + /> + + </changeSet> + +</databaseChangeLog> diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml index 0835918c9..559d90923 100644 --- a/schema/changelog-master.xml +++ b/schema/changelog-master.xml @@ -37,5 +37,10 @@ <include file="changelog-5.4.xml" relativeToChangelogFile="true" /> <include file="changelog-5.5.xml" relativeToChangelogFile="true" /> <include file="changelog-5.6.xml" relativeToChangelogFile="true" /> + <include file="changelog-5.7.xml" relativeToChangelogFile="true" /> + <include file="changelog-5.8.xml" relativeToChangelogFile="true" /> + <include file="changelog-5.9.xml" relativeToChangelogFile="true" /> + <include file="changelog-5.10.xml" relativeToChangelogFile="true" /> + <include file="changelog-5.11.xml" relativeToChangelogFile="true" /> </databaseChangeLog> diff --git a/setup/cloud-init.yaml b/setup/cloud-init.yaml new file mode 100644 index 000000000..1246fb1b7 --- /dev/null +++ b/setup/cloud-init.yaml @@ -0,0 +1,29 @@ +#cloud-config + +write_files: + - content: | + <?xml version='1.0' encoding='UTF-8'?> + <!DOCTYPE properties SYSTEM 'http://java.sun.com/dtd/properties.dtd'> + <properties> + + <entry key="config.default">./conf/default.xml</entry> + + <entry key='database.driver'>com.mysql.jdbc.Driver</entry> + <entry key='database.url'>jdbc:mysql://localhost/traccar?zeroDateTimeBehavior=round&serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&autoReconnect=true&useUnicode=yes&characterEncoding=UTF-8&sessionVariables=sql_mode=''</entry> + <entry key='database.user'>root</entry> + <entry key='database.password'>root</entry> + + </properties> + path: /root/traccar.xml + +package_update: true +packages: + - unzip + - mysql-server + +runcmd: + - mysql -u root --execute="ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES; CREATE DATABASE traccar;" + - wget https://www.traccar.org/download/traccar-linux-64-latest.zip + - unzip traccar-linux-*.zip && ./traccar.run + - cp /root/traccar.xml /opt/traccar/conf/ + - service traccar start diff --git a/setup/default.xml b/setup/default.xml index c00d29384..95bb1f04b 100644 --- a/setup/default.xml +++ b/setup/default.xml @@ -14,6 +14,7 @@ <entry key='web.path'>./modern</entry> <entry key='web.sanitize'>false</entry> <entry key='web.persistSession'>false</entry> + <entry key='web.showUnknownDevices'>true</entry> <entry key='geocoder.enable'>true</entry> <entry key='geocoder.type'>locationiq</entry> @@ -32,7 +33,7 @@ <entry key='media.path'>./media</entry> - <entry key='notificator.types'>web,mail</entry> + <entry key='notificator.types'>web,mail,command</entry> <entry key='server.statistics'>https://www.traccar.org/analytics/</entry> @@ -287,5 +288,15 @@ <entry key='ndtpv6.port'>5243</entry> <entry key='g1rus.port'>5244</entry> <entry key='rftrack.port'>5245</entry> + <entry key='vlt.port'>5246</entry> + <entry key='transync.port'>5247</entry> + <entry key='t622iridium.port'>5248</entry> + <entry key='pui.port'>5249</entry> + <entry key='nto.port'>5250</entry> + <entry key='ramac.port'>5251</entry> + <entry key='positrex.port'>5252</entry> + <entry key='dragino.port'>5253</entry> + <entry key='fleetguide.port'>5254</entry> + <entry key='valtrack.port'>5255</entry> </properties> diff --git a/setup/package.sh b/setup/package.sh index 71b86014f..f8ec927eb 100755 --- a/setup/package.sh +++ b/setup/package.sh @@ -15,6 +15,7 @@ usage () { echo "Available platforms:" echo " * linux-64" echo " * linux-arm" + echo " * linux-arm64" echo " * windows-64" echo " * other" exit 1 @@ -64,15 +65,18 @@ if [ $PLATFORM = "all" -o $PLATFORM = "windows-64" ]; then check_requirement "Windows 64 Java" "ls OpenJDK*64_windows*.zip" "Missing Windows 64 JDK (https://adoptium.net/)" check_requirement "Wine" "which wine" "Missing wine binary" fi -if [ $PLATFORM = "all" -o $PLATFORM = "linux-64" -o $PLATFORM = "linux-arm" ]; then +if [ $PLATFORM = "all" -o $PLATFORM = "linux-64" -o $PLATFORM = "linux-arm" -o $PLATFORM = "linux-arm64" ]; then check_requirement "Makeself" "which makeself" "Missing makeself binary" fi if [ $PLATFORM = "all" -o $PLATFORM = "linux-64" ]; then - check_requirement "Linux 64 Java" "ls OpenJDK*64_linux*.tar.gz" "Missing Linux 64 JDK (https://adoptium.net/)" + check_requirement "Linux 64 Java" "ls OpenJDK*x64_linux*.tar.gz" "Missing Linux 64 JDK (https://adoptium.net/)" fi if [ $PLATFORM = "all" -o $PLATFORM = "linux-arm" ]; then check_requirement "Linux ARM Java" "ls OpenJDK*arm_linux*.tar.gz" "Missing Linux ARM JDK (https://adoptium.net/)" fi +if [ $PLATFORM = "all" -o $PLATFORM = "linux-arm64" ]; then + check_requirement "Linux ARM 64 Java" "ls OpenJDK*aarch64_linux*.tar.gz" "Missing Linux ARM 64 JDK (https://adoptium.net/)" +fi if [ $PREREQ = false ]; then info "Missing build requirements, aborting..." exit 1 @@ -120,7 +124,7 @@ package_other () { package_windows () { info "Building Windows 64 installer" unzip -q OpenJDK*64_windows*.zip - jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec --output out/jre + jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec,jdk.unsupported --output out/jre rm -rf jdk-* wine app/ISCC.exe traccar.iss >/dev/null rm -rf out/jre @@ -133,8 +137,8 @@ package_linux () { cp setup.sh out cp traccar.service out - tar -xf OpenJDK*$1_linux*.tar.gz - jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec --output out/jre + tar -xf OpenJDK*$2_linux*.tar.gz + jlink --module-path jdk-*/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec,jdk.unsupported --output out/jre rm -rf jdk-* makeself --needroot --quiet --notemp out traccar.run "traccar" ./setup.sh rm -rf out/jre @@ -148,22 +152,29 @@ package_linux () { package_linux_64 () { info "Building Linux 64 installer" - package_linux 64 + package_linux 64 x64 ok "Created Linux 64 installer" } package_linux_arm () { info "Building Linux ARM installer" - package_linux arm + package_linux arm arm ok "Created Linux ARM installer" } +package_linux_arm64 () { + info "Building Linux ARM 64 installer" + package_linux arm64 aarch64 + ok "Created Linux ARM 64 installer" +} + prepare case $PLATFORM in all) package_linux_64 package_linux_arm + package_linux_arm64 package_windows package_other ;; @@ -176,6 +187,10 @@ case $PLATFORM in package_linux_arm ;; + linux-arm64) + package_linux_arm64 + ;; + windows-64) package_windows ;; diff --git a/setup/traccar.iss b/setup/traccar.iss index 28b33adfb..2ccee1c3e 100644 --- a/setup/traccar.iss +++ b/setup/traccar.iss @@ -1,6 +1,6 @@ [Setup] AppName=Traccar -AppVersion=5.5 +AppVersion=5.12 DefaultDirName={pf}\Traccar OutputBaseFilename=traccar-setup ArchitecturesInstallIn64BitMode=x64 diff --git a/src/main/java/org/traccar/BaseMqttProtocolDecoder.java b/src/main/java/org/traccar/BaseMqttProtocolDecoder.java new file mode 100644 index 000000000..0388563f5 --- /dev/null +++ b/src/main/java/org/traccar/BaseMqttProtocolDecoder.java @@ -0,0 +1,96 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar; + +import io.netty.channel.Channel; +import io.netty.handler.codec.mqtt.MqttConnectMessage; +import io.netty.handler.codec.mqtt.MqttConnectReturnCode; +import io.netty.handler.codec.mqtt.MqttMessage; +import io.netty.handler.codec.mqtt.MqttMessageBuilders; +import io.netty.handler.codec.mqtt.MqttPublishMessage; +import io.netty.handler.codec.mqtt.MqttSubscribeMessage; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; + +public abstract class BaseMqttProtocolDecoder extends BaseProtocolDecoder { + + public BaseMqttProtocolDecoder(Protocol protocol) { + super(protocol); + } + + protected abstract Object decode(DeviceSession deviceSession, MqttPublishMessage message) throws Exception; + + @Override + protected final Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + if (msg instanceof MqttConnectMessage) { + + MqttConnectMessage message = (MqttConnectMessage) msg; + + DeviceSession deviceSession = getDeviceSession( + channel, remoteAddress, message.payload().clientIdentifier()); + + MqttConnectReturnCode returnCode = deviceSession != null + ? MqttConnectReturnCode.CONNECTION_ACCEPTED + : MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; + + MqttMessage response = MqttMessageBuilders.connAck().returnCode(returnCode).build(); + + if (channel != null) { + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + + } else if (msg instanceof MqttSubscribeMessage) { + + MqttSubscribeMessage message = (MqttSubscribeMessage) msg; + + MqttMessage response = MqttMessageBuilders.subAck() + .packetId(message.variableHeader().messageId()) + .build(); + + if (channel != null) { + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + + } else if (msg instanceof MqttPublishMessage) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + MqttPublishMessage message = (MqttPublishMessage) msg; + + Object result = decode(deviceSession, message); + + MqttMessage response = MqttMessageBuilders.pubAck() + .packetId(message.variableHeader().packetId()) + .build(); + + if (channel != null) { + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + + return result; + + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java index b184da45c..ca4a4ae63 100644 --- a/src/main/java/org/traccar/BasePipelineFactory.java +++ b/src/main/java/org/traccar/BasePipelineFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import io.netty.channel.ChannelPipeline; import io.netty.handler.timeout.IdleStateHandler; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.handler.AcknowledgementHandler; import org.traccar.handler.ComputedAttributesHandler; import org.traccar.handler.CopyAttributesHandler; import org.traccar.handler.DefaultDataHandler; @@ -32,9 +33,11 @@ import org.traccar.handler.DistanceHandler; import org.traccar.handler.EngineHoursHandler; import org.traccar.handler.FilterHandler; import org.traccar.handler.GeocoderHandler; +import org.traccar.handler.GeofenceHandler; import org.traccar.handler.GeolocationHandler; import org.traccar.handler.HemisphereHandler; import org.traccar.handler.MotionHandler; +import org.traccar.handler.NetworkForwarderHandler; import org.traccar.handler.NetworkMessageHandler; import org.traccar.handler.OpenChannelHandler; import org.traccar.handler.RemoteAddressHandler; @@ -59,16 +62,20 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { private final Injector injector; private final TrackerConnector connector; + private final Config config; private final String protocol; - private int timeout; + private final int timeout; public BasePipelineFactory(TrackerConnector connector, Config config, String protocol) { this.injector = Main.getInjector(); this.connector = connector; + this.config = config; this.protocol = protocol; - timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol)); + int timeout = config.getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol)); if (timeout == 0) { - timeout = config.getInteger(Keys.SERVER_TIMEOUT); + this.timeout = config.getInteger(Keys.SERVER_TIMEOUT); + } else { + this.timeout = timeout; } } @@ -110,8 +117,21 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { pipeline.addLast(new IdleStateHandler(timeout, 0, 0)); } pipeline.addLast(new OpenChannelHandler(connector)); + if (config.hasKey(Keys.SERVER_FORWARD)) { + int port = config.getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol)); + var handler = new NetworkForwarderHandler(port); + injector.injectMembers(handler); + pipeline.addLast(handler); + } pipeline.addLast(new NetworkMessageHandler()); - pipeline.addLast(new StandardLoggingHandler(protocol)); + + var loggingHandler = new StandardLoggingHandler(protocol); + injector.injectMembers(loggingHandler); + pipeline.addLast(loggingHandler); + + if (!connector.isDatagram() && !config.getBoolean(Keys.SERVER_INSTANT_ACKNOWLEDGEMENT)) { + pipeline.addLast(new AcknowledgementHandler()); + } addProtocolHandlers(handler -> { if (handler instanceof BaseProtocolDecoder || handler instanceof BaseProtocolEncoder) { @@ -134,6 +154,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> { DistanceHandler.class, RemoteAddressHandler.class, FilterHandler.class, + GeofenceHandler.class, GeocoderHandler.class, SpeedLimitHandler.class, MotionHandler.class, diff --git a/src/main/java/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java index d19fc307e..ea302997c 100644 --- a/src/main/java/org/traccar/BaseProtocol.java +++ b/src/main/java/org/traccar/BaseProtocol.java @@ -23,8 +23,8 @@ import org.traccar.helper.DataConverter; import org.traccar.model.Command; import org.traccar.sms.SmsManager; -import javax.annotation.Nullable; -import javax.inject.Inject; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; import java.net.SocketAddress; import java.util.Arrays; import java.util.Collection; @@ -105,7 +105,8 @@ public abstract class BaseProtocol implements Protocol { } else if (command.getType().equals(Command.TYPE_CUSTOM)) { String data = command.getString(Command.KEY_DATA); if (BasePipelineFactory.getHandler(channel.pipeline(), StringEncoder.class) != null) { - channel.writeAndFlush(new NetworkMessage(data, remoteAddress)); + channel.writeAndFlush(new NetworkMessage( + data.replace("\\r", "\r").replace("\\n", "\n"), remoteAddress)); } else { ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data)); channel.writeAndFlush(new NetworkMessage(buf, remoteAddress)); diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java index 382daf92f..495a866c0 100644 --- a/src/main/java/org/traccar/BaseProtocolDecoder.java +++ b/src/main/java/org/traccar/BaseProtocolDecoder.java @@ -29,9 +29,8 @@ import org.traccar.model.Position; import org.traccar.session.ConnectionManager; import org.traccar.session.DeviceSession; import org.traccar.session.cache.CacheManager; -import org.traccar.storage.StorageException; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collection; @@ -52,6 +51,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { private MediaManager mediaManager; private CommandsManager commandsManager; + private String modelOverride; + public BaseProtocolDecoder(Protocol protocol) { this.protocol = protocol; } @@ -125,22 +126,31 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { } protected TimeZone getTimeZone(long deviceId, String defaultTimeZone) { - TimeZone result = TimeZone.getTimeZone(defaultTimeZone); String timeZoneName = AttributeUtil.lookup(cacheManager, Keys.DECODER_TIMEZONE, deviceId); if (timeZoneName != null) { - result = TimeZone.getTimeZone(timeZoneName); + return TimeZone.getTimeZone(timeZoneName); + } else if (defaultTimeZone != null) { + return TimeZone.getTimeZone(defaultTimeZone); } - return result; + return null; } public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) { try { return connectionManager.getDeviceSession(protocol, channel, remoteAddress, uniqueIds); - } catch (StorageException e) { + } catch (Exception e) { throw new RuntimeException(e); } } + public void setModelOverride(String modelOverride) { + this.modelOverride = modelOverride; + } + + public String getDeviceModel(DeviceSession deviceSession) { + return modelOverride != null ? modelOverride : deviceSession.getModel(); + } + public void getLastLocation(Position position, Date deviceTime) { if (position.getDeviceId() != 0) { position.setOutdated(true); diff --git a/src/main/java/org/traccar/BaseProtocolEncoder.java b/src/main/java/org/traccar/BaseProtocolEncoder.java index 9c3934184..e357c27dc 100644 --- a/src/main/java/org/traccar/BaseProtocolEncoder.java +++ b/src/main/java/org/traccar/BaseProtocolEncoder.java @@ -27,7 +27,7 @@ import org.traccar.model.Command; import org.traccar.model.Device; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; +import jakarta.inject.Inject; public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter { @@ -39,6 +39,8 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter private CacheManager cacheManager; + private String modelOverride; + public BaseProtocolEncoder(Protocol protocol) { this.protocol = protocol; } @@ -68,34 +70,42 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter } } - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - - NetworkMessage networkMessage = (NetworkMessage) msg; + public void setModelOverride(String modelOverride) { + this.modelOverride = modelOverride; + } - if (networkMessage.getMessage() instanceof Command) { + public String getDeviceModel(long deviceId) { + String model = getCacheManager().getObject(Device.class, deviceId).getModel(); + return modelOverride != null ? modelOverride : model; + } - Command command = (Command) networkMessage.getMessage(); - Object encodedCommand = encodeCommand(ctx.channel(), command); + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - StringBuilder s = new StringBuilder(); - s.append("[").append(NetworkUtil.session(ctx.channel())).append("] "); - s.append("id: ").append(getUniqueId(command.getDeviceId())).append(", "); - s.append("command type: ").append(command.getType()).append(" "); - if (encodedCommand != null) { - s.append("sent"); - } else { - s.append("not sent"); - } - LOGGER.info(s.toString()); + if (msg instanceof NetworkMessage) { + NetworkMessage networkMessage = (NetworkMessage) msg; + if (networkMessage.getMessage() instanceof Command) { - ctx.write(new NetworkMessage(encodedCommand, networkMessage.getRemoteAddress()), promise); + Command command = (Command) networkMessage.getMessage(); + Object encodedCommand = encodeCommand(ctx.channel(), command); - } else { + StringBuilder s = new StringBuilder(); + s.append("[").append(NetworkUtil.session(ctx.channel())).append("] "); + s.append("id: ").append(getUniqueId(command.getDeviceId())).append(", "); + s.append("command type: ").append(command.getType()).append(" "); + if (encodedCommand != null) { + s.append("sent"); + } else { + s.append("not sent"); + } + LOGGER.info(s.toString()); - super.write(ctx, msg, promise); + ctx.write(new NetworkMessage(encodedCommand, networkMessage.getRemoteAddress()), promise); + return; + } } + super.write(ctx, msg, promise); } protected Object encodeCommand(Channel channel, Command command) { diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java index f79a36c85..cddddcd80 100644 --- a/src/main/java/org/traccar/ExtendedObjectDecoder.java +++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,13 +23,15 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.ReferenceCountUtil; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.handler.AcknowledgementHandler; import org.traccar.helper.DataConverter; import org.traccar.model.Position; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Collection; +import java.util.List; public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter { @@ -68,6 +70,7 @@ public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { NetworkMessage networkMessage = (NetworkMessage) msg; Object originalMessage = networkMessage.getMessage(); + ctx.writeAndFlush(new AcknowledgementHandler.EventReceived()); try { Object decodedMessage = decode(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage); onMessageEvent(ctx.channel(), networkMessage.getRemoteAddress(), originalMessage, decodedMessage); @@ -76,14 +79,19 @@ public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter } if (decodedMessage != null) { if (decodedMessage instanceof Collection) { - for (Object o : (Collection) decodedMessage) { + var collection = (Collection) decodedMessage; + ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(collection)); + for (Object o : collection) { saveOriginal(o, originalMessage); ctx.fireChannelRead(o); } } else { + ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(List.of(decodedMessage))); saveOriginal(decodedMessage, originalMessage); ctx.fireChannelRead(decodedMessage); } + } else { + ctx.writeAndFlush(new AcknowledgementHandler.EventDecoded(List.of())); } } finally { ReferenceCountUtil.release(originalMessage); diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java index e34fbb72a..33fcf654f 100644 --- a/src/main/java/org/traccar/Main.java +++ b/src/main/java/org/traccar/Main.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,8 @@ import com.google.inject.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.broadcast.BroadcastService; -import org.traccar.helper.model.DeviceUtil; import org.traccar.schedule.ScheduleManager; import org.traccar.storage.DatabaseModule; -import org.traccar.storage.Storage; import org.traccar.web.WebModule; import org.traccar.web.WebServer; @@ -33,10 +31,9 @@ import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; public final class Main { @@ -70,8 +67,7 @@ public final class Main { + " heap: " + memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024) + "mb" + " non-heap: " + memoryBean.getNonHeapMemoryUsage().getMax() / (1024 * 1024) + "mb"); - LOGGER.info("Character encoding: " - + System.getProperty("file.encoding") + " charset: " + Charset.defaultCharset()); + LOGGER.info("Character encoding: " + Charset.defaultCharset().displayName()); } catch (Exception error) { LOGGER.warn("Failed to get system info"); @@ -122,18 +118,14 @@ public final class Main { LOGGER.info("Version: " + Main.class.getPackage().getImplementationVersion()); LOGGER.info("Starting server..."); - if (injector.getInstance(BroadcastService.class).singleInstance()) { - DeviceUtil.resetStatus(injector.getInstance(Storage.class)); - } - - var services = Stream.of( - ServerManager.class, WebServer.class, ScheduleManager.class, BroadcastService.class) - .map(injector::getInstance) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - for (var service : services) { - service.start(); + var services = new ArrayList<LifecycleObject>(); + for (var clazz : List.of( + ScheduleManager.class, ServerManager.class, WebServer.class, BroadcastService.class)) { + var service = injector.getInstance(clazz); + if (service != null) { + service.start(); + services.add(service); + } } Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e)); diff --git a/src/main/java/org/traccar/MainEventHandler.java b/src/main/java/org/traccar/MainEventHandler.java index 877f03ae7..fb0171d63 100644 --- a/src/main/java/org/traccar/MainEventHandler.java +++ b/src/main/java/org/traccar/MainEventHandler.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.StatisticsManager; +import org.traccar.handler.AcknowledgementHandler; import org.traccar.helper.DateUtil; import org.traccar.helper.NetworkUtil; import org.traccar.helper.model.PositionUtil; @@ -40,8 +41,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; @@ -145,6 +146,8 @@ public class MainEventHandler extends ChannelInboundHandlerAdapter { LOGGER.info(builder.toString()); statisticsManager.registerMessageStored(position.getDeviceId(), position.getProtocol()); + + ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); } } diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java index 55211d109..3fec4d1e6 100644 --- a/src/main/java/org/traccar/MainModule.java +++ b/src/main/java/org/traccar/MainModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.traccar; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr353.JSR353Module; +import com.fasterxml.jackson.datatype.jsonp.JSONPModule; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Provides; @@ -26,22 +26,25 @@ import com.google.inject.name.Names; import io.netty.util.HashedWheelTimer; import io.netty.util.Timer; import org.apache.velocity.app.VelocityEngine; -import org.apache.velocity.runtime.log.NullLogChute; -import org.eclipse.jetty.util.URIUtil; import org.traccar.broadcast.BroadcastService; import org.traccar.broadcast.MulticastBroadcastService; +import org.traccar.broadcast.RedisBroadcastService; import org.traccar.broadcast.NullBroadcastService; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.LdapProvider; +import org.traccar.database.OpenIdProvider; import org.traccar.database.StatisticsManager; import org.traccar.forward.EventForwarder; import org.traccar.forward.EventForwarderJson; +import org.traccar.forward.EventForwarderAmqp; import org.traccar.forward.EventForwarderKafka; import org.traccar.forward.EventForwarderMqtt; import org.traccar.forward.PositionForwarder; import org.traccar.forward.PositionForwarderJson; +import org.traccar.forward.PositionForwarderAmqp; import org.traccar.forward.PositionForwarderKafka; +import org.traccar.forward.PositionForwarderRedis; import org.traccar.forward.PositionForwarderUrl; import org.traccar.geocoder.AddressFormat; import org.traccar.geocoder.BanGeocoder; @@ -74,6 +77,7 @@ import org.traccar.handler.GeolocationHandler; import org.traccar.handler.SpeedLimitHandler; import org.traccar.helper.ObjectMapperContextResolver; import org.traccar.helper.SanitizerModule; +import org.traccar.helper.WebHelper; import org.traccar.mail.LogMailManager; import org.traccar.mail.MailManager; import org.traccar.mail.SmtpMailManager; @@ -84,16 +88,18 @@ import org.traccar.sms.SnsSmsClient; import org.traccar.speedlimit.OverpassSpeedLimitProvider; import org.traccar.speedlimit.SpeedLimitProvider; import org.traccar.storage.DatabaseStorage; +import org.traccar.storage.MemoryStorage; import org.traccar.storage.Storage; import org.traccar.web.WebServer; +import org.traccar.api.security.LoginService; -import javax.annotation.Nullable; -import javax.inject.Singleton; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; +import jakarta.annotation.Nullable; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; +import java.net.URISyntaxException; +import java.net.http.HttpClient; import java.util.Properties; public class MainModule extends AbstractModule { @@ -108,18 +114,27 @@ public class MainModule extends AbstractModule { protected void configure() { bindConstant().annotatedWith(Names.named("configFile")).to(configFile); bind(Config.class).asEagerSingleton(); - bind(Storage.class).to(DatabaseStorage.class).in(Scopes.SINGLETON); bind(Timer.class).to(HashedWheelTimer.class).in(Scopes.SINGLETON); } @Singleton @Provides + public static Storage provideStorage(Injector injector, Config config) { + if (config.getBoolean(Keys.DATABASE_MEMORY)) { + return injector.getInstance(MemoryStorage.class); + } else { + return injector.getInstance(DatabaseStorage.class); + } + } + + @Singleton + @Provides public static ObjectMapper provideObjectMapper(Config config) { ObjectMapper objectMapper = new ObjectMapper(); if (config.getBoolean(Keys.WEB_SANITIZE)) { objectMapper.registerModule(new SanitizerModule()); } - objectMapper.registerModule(new JSR353Module()); + objectMapper.registerModule(new JSONPModule()); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return objectMapper; } @@ -160,6 +175,17 @@ public class MainModule extends AbstractModule { return null; } + @Singleton + @Provides + public static OpenIdProvider provideOpenIDProvider( + Config config, LoginService loginService, ObjectMapper objectMapper + ) throws InterruptedException, IOException, URISyntaxException { + if (config.hasKey(Keys.OPENID_CLIENT_ID)) { + return new OpenIdProvider(config, loginService, HttpClient.newHttpClient(), objectMapper); + } + return null; + } + @Provides public static WebServer provideWebServer(Injector injector, Config config) { if (config.hasKey(Keys.WEB_PORT)) { @@ -174,7 +200,6 @@ public class MainModule extends AbstractModule { if (config.getBoolean(Keys.GEOCODER_ENABLE)) { String type = config.getString(Keys.GEOCODER_TYPE, "google"); String url = config.getString(Keys.GEOCODER_URL); - String id = config.getString(Keys.GEOCODER_ID); String key = config.getString(Keys.GEOCODER_KEY); String language = config.getString(Keys.GEOCODER_LANGUAGE); String formatString = config.getString(Keys.GEOCODER_FORMAT); @@ -217,7 +242,7 @@ public class MainModule extends AbstractModule { geocoder = new BanGeocoder(client, cacheSize, addressFormat); break; case "here": - geocoder = new HereGeocoder(client, url, id, key, language, cacheSize, addressFormat); + geocoder = new HereGeocoder(client, url, key, language, cacheSize, addressFormat); break; case "mapmyindia": geocoder = new MapmyIndiaGeocoder(client, url, key, cacheSize, addressFormat); @@ -277,7 +302,7 @@ public class MainModule extends AbstractModule { switch (type) { case "overpass": default: - return new OverpassSpeedLimitProvider(client, url); + return new OverpassSpeedLimitProvider(config, client, url); } } return null; @@ -317,8 +342,15 @@ public class MainModule extends AbstractModule { @Provides public static BroadcastService provideBroadcastService( Config config, ObjectMapper objectMapper) throws IOException { - if (config.hasKey(Keys.BROADCAST_ADDRESS)) { - return new MulticastBroadcastService(config, objectMapper); + if (config.hasKey(Keys.BROADCAST_TYPE)) { + switch (config.getString(Keys.BROADCAST_TYPE)) { + case "multicast": + return new MulticastBroadcastService(config, objectMapper); + case "redis": + return new RedisBroadcastService(config, objectMapper); + default: + break; + } } return new NullBroadcastService(); } @@ -329,10 +361,13 @@ public class MainModule extends AbstractModule { if (config.hasKey(Keys.EVENT_FORWARD_URL)) { String forwardType = config.getString(Keys.EVENT_FORWARD_TYPE); switch (forwardType) { + case "amqp": + return new EventForwarderAmqp(config, objectMapper); case "kafka": return new EventForwarderKafka(config, objectMapper); case "mqtt": return new EventForwarderMqtt(config, objectMapper); + case "json": default: return new EventForwarderJson(config, client); } @@ -347,8 +382,13 @@ public class MainModule extends AbstractModule { switch (config.getString(Keys.FORWARD_TYPE)) { case "json": return new PositionForwarderJson(config, client, objectMapper); + case "amqp": + return new PositionForwarderAmqp(config, objectMapper); case "kafka": return new PositionForwarderKafka(config, objectMapper); + case "redis": + return new PositionForwarderRedis(config, objectMapper); + case "url": default: return new PositionForwarderUrl(config, client, objectMapper); } @@ -360,21 +400,8 @@ public class MainModule extends AbstractModule { @Provides public static VelocityEngine provideVelocityEngine(Config config) { Properties properties = new Properties(); - properties.setProperty("file.resource.loader.path", config.getString(Keys.TEMPLATES_ROOT) + "/"); - properties.setProperty("runtime.log.logsystem.class", NullLogChute.class.getName()); - - if (config.hasKey(Keys.WEB_URL)) { - properties.setProperty("web.url", config.getString(Keys.WEB_URL).replaceAll("/$", "")); - } else { - String address; - try { - address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress()); - } catch (UnknownHostException e) { - address = "localhost"; - } - String url = URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", ""); - properties.setProperty("web.url", url); - } + properties.setProperty("resource.loader.file.path", config.getString(Keys.TEMPLATES_ROOT) + "/"); + properties.setProperty("web.url", WebHelper.retrieveWebUrl(config)); VelocityEngine velocityEngine = new VelocityEngine(); velocityEngine.init(properties); diff --git a/src/main/java/org/traccar/PositionForwardingHandler.java b/src/main/java/org/traccar/PositionForwardingHandler.java index 83f91e937..a79b01367 100644 --- a/src/main/java/org/traccar/PositionForwardingHandler.java +++ b/src/main/java/org/traccar/PositionForwardingHandler.java @@ -30,9 +30,9 @@ import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java index 57afb01fd..e91be50a2 100644 --- a/src/main/java/org/traccar/ServerManager.java +++ b/src/main/java/org/traccar/ServerManager.java @@ -22,8 +22,8 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.ClassScanner; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.IOException; import java.net.BindException; import java.net.ConnectException; diff --git a/src/main/java/org/traccar/WindowsService.java b/src/main/java/org/traccar/WindowsService.java index f233337a7..08eba25a6 100644 --- a/src/main/java/org/traccar/WindowsService.java +++ b/src/main/java/org/traccar/WindowsService.java @@ -170,7 +170,7 @@ public abstract class WindowsService { public abstract void run(); - private class ServiceMain implements SERVICE_MAIN_FUNCTION { + private final class ServiceMain implements SERVICE_MAIN_FUNCTION { public void callback(int dwArgc, Pointer lpszArgv) { ServiceControl serviceControl = new ServiceControl(); @@ -203,7 +203,7 @@ public abstract class WindowsService { } - private class ServiceControl implements HandlerEx { + private final class ServiceControl implements HandlerEx { public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) { switch (dwControl) { diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java index 5fc4b4412..f5fbcbf62 100644 --- a/src/main/java/org/traccar/api/AsyncSocket.java +++ b/src/main/java/org/traccar/api/AsyncSocket.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,16 +22,17 @@ import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.helper.model.PositionUtil; -import org.traccar.session.ConnectionManager; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.LogRecord; import org.traccar.model.Position; +import org.traccar.session.ConnectionManager; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.UpdateListener { @@ -41,12 +42,15 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U private static final String KEY_DEVICES = "devices"; private static final String KEY_POSITIONS = "positions"; private static final String KEY_EVENTS = "events"; + private static final String KEY_LOGS = "logs"; private final ObjectMapper objectMapper; private final ConnectionManager connectionManager; private final Storage storage; private final long userId; + private boolean includeLogs; + public AsyncSocket(ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage, long userId) { this.objectMapper = objectMapper; this.connectionManager = connectionManager; @@ -76,29 +80,41 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U } @Override + public void onWebSocketText(String message) { + super.onWebSocketText(message); + + try { + includeLogs = objectMapper.readTree(message).get("logs").asBoolean(); + } catch (JsonProcessingException e) { + LOGGER.warn("Socket JSON parsing error", e); + } + } + + @Override public void onKeepalive() { sendData(new HashMap<>()); } @Override public void onUpdateDevice(Device device) { - Map<String, Collection<?>> data = new HashMap<>(); - data.put(KEY_DEVICES, Collections.singletonList(device)); - sendData(data); + sendData(Map.of(KEY_DEVICES, List.of(device))); } @Override public void onUpdatePosition(Position position) { - Map<String, Collection<?>> data = new HashMap<>(); - data.put(KEY_POSITIONS, Collections.singletonList(position)); - sendData(data); + sendData(Map.of(KEY_POSITIONS, List.of(position))); } @Override public void onUpdateEvent(Event event) { - Map<String, Collection<?>> data = new HashMap<>(); - data.put(KEY_EVENTS, Collections.singletonList(event)); - sendData(data); + sendData(Map.of(KEY_EVENTS, List.of(event))); + } + + @Override + public void onUpdateLog(LogRecord record) { + if (includeLogs) { + sendData(Map.of(KEY_LOGS, List.of(record))); + } } private void sendData(Map<String, Collection<?>> data) { diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java index 91a745eeb..cd2c1639e 100644 --- a/src/main/java/org/traccar/api/AsyncSocketServlet.java +++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java @@ -24,9 +24,9 @@ import org.traccar.config.Keys; import org.traccar.session.ConnectionManager; import org.traccar.storage.Storage; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.servlet.http.HttpSession; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.http.HttpSession; import java.time.Duration; @Singleton diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java index 904781e54..2a801221b 100644 --- a/src/main/java/org/traccar/api/BaseObjectResource.java +++ b/src/main/java/org/traccar/api/BaseObjectResource.java @@ -16,6 +16,8 @@ */ package org.traccar.api; +import org.traccar.api.security.ServiceAccountUser; +import org.traccar.model.ObjectOperation; import org.traccar.helper.LogAction; import org.traccar.model.BaseModel; import org.traccar.model.Group; @@ -28,14 +30,14 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Response; +import jakarta.inject.Inject; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.Response; public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource { @@ -65,22 +67,25 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour } @POST - public Response add(T entity) throws StorageException { + public Response add(T entity) throws Exception { permissionsService.checkEdit(getUserId(), entity, true); entity.setId(storage.addObject(entity, new Request(new Columns.Exclude("id")))); LogAction.create(getUserId(), entity); - storage.addPermission(new Permission(User.class, getUserId(), baseClass, entity.getId())); - cacheManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId()); - connectionManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId()); - LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId()); + + if (getUserId() != ServiceAccountUser.ID) { + storage.addPermission(new Permission(User.class, getUserId(), baseClass, entity.getId())); + cacheManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId(), true); + connectionManager.invalidatePermission(true, User.class, getUserId(), baseClass, entity.getId(), true); + LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId()); + } return Response.ok(entity).build(); } @Path("{id}") @PUT - public Response update(T entity) throws StorageException { + public Response update(T entity) throws Exception { permissionsService.checkEdit(getUserId(), entity, false); permissionsService.checkPermission(baseClass, getUserId(), entity.getId()); @@ -106,7 +111,7 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour new Condition.Equals("id", entity.getId()))); } } - cacheManager.updateOrInvalidate(true, entity); + cacheManager.invalidateObject(true, entity.getClass(), entity.getId(), ObjectOperation.UPDATE); LogAction.edit(getUserId(), entity); return Response.ok(entity).build(); @@ -114,12 +119,12 @@ public abstract class BaseObjectResource<T extends BaseModel> extends BaseResour @Path("{id}") @DELETE - public Response remove(@PathParam("id") long id) throws StorageException { + public Response remove(@PathParam("id") long id) throws Exception { permissionsService.checkEdit(getUserId(), baseClass, false); permissionsService.checkPermission(baseClass, getUserId(), id); storage.removeObject(baseClass, new Request(new Condition.Equals("id", id))); - cacheManager.invalidate(baseClass, id); + cacheManager.invalidateObject(true, baseClass, id, ObjectOperation.DELETE); LogAction.remove(getUserId(), baseClass, id); diff --git a/src/main/java/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java index 33abe73fa..f20b8c4c2 100644 --- a/src/main/java/org/traccar/api/BaseResource.java +++ b/src/main/java/org/traccar/api/BaseResource.java @@ -19,9 +19,9 @@ import org.traccar.api.security.PermissionsService; import org.traccar.api.security.UserPrincipal; import org.traccar.storage.Storage; -import javax.inject.Inject; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.SecurityContext; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.SecurityContext; public class BaseResource { diff --git a/src/main/java/org/traccar/api/CorsResponseFilter.java b/src/main/java/org/traccar/api/CorsResponseFilter.java index 67d0341a1..a380eb41d 100644 --- a/src/main/java/org/traccar/api/CorsResponseFilter.java +++ b/src/main/java/org/traccar/api/CorsResponseFilter.java @@ -19,11 +19,11 @@ import io.netty.handler.codec.http.HttpHeaderNames; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.container.ContainerResponseFilter; import java.io.IOException; @Singleton diff --git a/src/main/java/org/traccar/api/DateParameterConverterProvider.java b/src/main/java/org/traccar/api/DateParameterConverterProvider.java index f07ece984..4858fcbfd 100644 --- a/src/main/java/org/traccar/api/DateParameterConverterProvider.java +++ b/src/main/java/org/traccar/api/DateParameterConverterProvider.java @@ -17,8 +17,8 @@ package org.traccar.api; import org.traccar.helper.DateUtil; -import javax.ws.rs.ext.ParamConverter; -import javax.ws.rs.ext.ParamConverterProvider; +import jakarta.ws.rs.ext.ParamConverter; +import jakarta.ws.rs.ext.ParamConverterProvider; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Date; diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java index 8467b46c6..348ebe38a 100644 --- a/src/main/java/org/traccar/api/ExtendedObjectResource.java +++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java @@ -25,8 +25,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.ws.rs.GET; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.QueryParam; import java.util.Collection; import java.util.LinkedList; diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java index ab75bdc5d..38d13078d 100644 --- a/src/main/java/org/traccar/api/MediaFilter.java +++ b/src/main/java/org/traccar/api/MediaFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,17 +28,16 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; @Singleton @@ -58,10 +57,6 @@ public class MediaFilter implements Filter { } @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -99,8 +94,4 @@ public class MediaFilter implements Filter { } } - @Override - public void destroy() { - } - } diff --git a/src/main/java/org/traccar/api/ResourceErrorHandler.java b/src/main/java/org/traccar/api/ResourceErrorHandler.java index 108a8e8cc..387f3db6a 100644 --- a/src/main/java/org/traccar/api/ResourceErrorHandler.java +++ b/src/main/java/org/traccar/api/ResourceErrorHandler.java @@ -17,9 +17,9 @@ package org.traccar.api; import org.traccar.helper.Log; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; public class ResourceErrorHandler implements ExceptionMapper<Exception> { diff --git a/src/main/java/org/traccar/api/SimpleObjectResource.java b/src/main/java/org/traccar/api/SimpleObjectResource.java index 4a435ca7d..c9d41b063 100644 --- a/src/main/java/org/traccar/api/SimpleObjectResource.java +++ b/src/main/java/org/traccar/api/SimpleObjectResource.java @@ -23,8 +23,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.ws.rs.GET; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.QueryParam; import java.util.Collection; import java.util.LinkedList; diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java index f85e90133..52c4d6324 100644 --- a/src/main/java/org/traccar/api/resource/AttributeResource.java +++ b/src/main/java/org/traccar/api/resource/AttributeResource.java @@ -16,17 +16,17 @@ */ package org.traccar.api.resource; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Attribute; @@ -78,21 +78,21 @@ public class AttributeResource extends ExtendedObjectResource<Attribute> { } @POST - public Response add(Attribute entity) throws StorageException { + public Response add(Attribute entity) throws Exception { permissionsService.checkAdmin(getUserId()); return super.add(entity); } @Path("{id}") @PUT - public Response update(Attribute entity) throws StorageException { + public Response update(Attribute entity) throws Exception { permissionsService.checkAdmin(getUserId()); return super.update(entity); } @Path("{id}") @DELETE - public Response remove(@PathParam("id") long id) throws StorageException { + public Response remove(@PathParam("id") long id) throws Exception { permissionsService.checkAdmin(getUserId()); return super.remove(id); } diff --git a/src/main/java/org/traccar/api/resource/CalendarResource.java b/src/main/java/org/traccar/api/resource/CalendarResource.java index 9399c34a5..f6c1f3c59 100644 --- a/src/main/java/org/traccar/api/resource/CalendarResource.java +++ b/src/main/java/org/traccar/api/resource/CalendarResource.java @@ -16,10 +16,10 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.traccar.api.SimpleObjectResource; import org.traccar.model.Calendar; diff --git a/src/main/java/org/traccar/api/resource/CommandResource.java b/src/main/java/org/traccar/api/resource/CommandResource.java index 3460cf6e0..c23d91e77 100644 --- a/src/main/java/org/traccar/api/resource/CommandResource.java +++ b/src/main/java/org/traccar/api/resource/CommandResource.java @@ -23,9 +23,12 @@ import org.traccar.BaseProtocol; import org.traccar.ServerManager; import org.traccar.api.ExtendedObjectResource; import org.traccar.database.CommandsManager; +import org.traccar.helper.model.DeviceUtil; import org.traccar.model.Command; import org.traccar.model.Device; +import org.traccar.model.Group; import org.traccar.model.Position; +import org.traccar.model.QueuedCommand; import org.traccar.model.Typed; import org.traccar.model.User; import org.traccar.model.UserRestrictions; @@ -34,15 +37,15 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -104,7 +107,7 @@ public class CommandResource extends ExtendedObjectResource<Command> { @POST @Path("send") - public Response send(Command entity) throws Exception { + public Response send(Command entity, @QueryParam("groupId") long groupId) throws Exception { if (entity.getId() > 0) { permissionsService.checkPermission(baseClass, getUserId(), entity.getId()); long deviceId = entity.getDeviceId(); @@ -114,9 +117,28 @@ public class CommandResource extends ExtendedObjectResource<Command> { } else { permissionsService.checkRestriction(getUserId(), UserRestrictions::getLimitCommands); } - permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId()); - if (!commandsManager.sendCommand(entity)) { - return Response.accepted(entity).build(); + + if (groupId > 0) { + permissionsService.checkPermission(Group.class, getUserId(), groupId); + var devices = DeviceUtil.getAccessibleDevices(storage, getUserId(), List.of(), List.of(groupId)); + List<QueuedCommand> queuedCommands = new ArrayList<>(); + for (Device device : devices) { + Command command = QueuedCommand.fromCommand(entity).toCommand(); + command.setDeviceId(device.getId()); + QueuedCommand queuedCommand = commandsManager.sendCommand(command); + if (queuedCommand != null) { + queuedCommands.add(queuedCommand); + } + } + if (!queuedCommands.isEmpty()) { + return Response.accepted(queuedCommands).build(); + } + } else { + permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId()); + QueuedCommand queuedCommand = commandsManager.sendCommand(entity); + if (queuedCommand != null) { + return Response.accepted(queuedCommand).build(); + } } return Response.ok(entity).build(); } diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java index c0b0cea0d..89bba7237 100644 --- a/src/main/java/org/traccar/api/resource/DeviceResource.java +++ b/src/main/java/org/traccar/api/resource/DeviceResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,17 @@ */ package org.traccar.api.resource; +import jakarta.ws.rs.FormParam; import org.traccar.api.BaseObjectResource; +import org.traccar.api.signature.TokenManager; import org.traccar.broadcast.BroadcastService; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.database.MediaManager; import org.traccar.helper.LogAction; import org.traccar.model.Device; import org.traccar.model.DeviceAccumulators; +import org.traccar.model.Permission; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.session.ConnectionManager; @@ -30,23 +35,25 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.security.GeneralSecurityException; import java.util.Collection; +import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -56,6 +63,9 @@ import java.util.List; public class DeviceResource extends BaseObjectResource<Device> { @Inject + private Config config; + + @Inject private CacheManager cacheManager; @Inject @@ -67,6 +77,9 @@ public class DeviceResource extends BaseObjectResource<Device> { @Inject private MediaManager mediaManager; + @Inject + private TokenManager tokenManager; + public DeviceResource() { super(Device.class); } @@ -120,7 +133,7 @@ public class DeviceResource extends BaseObjectResource<Device> { @Path("{id}/accumulators") @PUT - public Response updateAccumulators(DeviceAccumulators entity) throws StorageException { + public Response updateAccumulators(DeviceAccumulators entity) throws Exception { if (permissionsService.notAdmin(getUserId())) { permissionsService.checkManager(getUserId()); permissionsService.checkPermission(Device.class, getUserId(), entity.getDeviceId()); @@ -183,4 +196,50 @@ public class DeviceResource extends BaseObjectResource<Device> { return Response.status(Response.Status.NOT_FOUND).build(); } + @Path("share") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @POST + public String shareDevice( + @FormParam("deviceId") long deviceId, + @FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException { + + User user = permissionsService.getUser(getUserId()); + if (permissionsService.getServer().getBoolean(Keys.DEVICE_SHARE_DISABLE.getKey())) { + throw new SecurityException("Sharing is disabled"); + } + if (user.getTemporary()) { + throw new SecurityException("Temporary user"); + } + if (user.getExpirationTime() != null && user.getExpirationTime().before(expiration)) { + expiration = user.getExpirationTime(); + } + + Device device = storage.getObject(Device.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("id", deviceId), + new Condition.Permission(User.class, user.getId(), Device.class)))); + + String shareEmail = user.getEmail() + ":" + device.getUniqueId(); + User share = storage.getObject(User.class, new Request( + new Columns.All(), new Condition.Equals("email", shareEmail))); + + if (share == null) { + share = new User(); + share.setName(device.getName()); + share.setEmail(shareEmail); + share.setExpirationTime(expiration); + share.setTemporary(true); + share.setReadonly(true); + share.setLimitCommands(user.getLimitCommands() || !config.getBoolean(Keys.WEB_SHARE_DEVICE_COMMANDS)); + share.setDisableReports(user.getDisableReports() || !config.getBoolean(Keys.WEB_SHARE_DEVICE_REPORTS)); + + share.setId(storage.addObject(share, new Request(new Columns.Exclude("id")))); + + storage.addPermission(new Permission(User.class, share.getId(), Device.class, deviceId)); + } + + return tokenManager.generateToken(share.getId(), expiration); + } + } diff --git a/src/main/java/org/traccar/api/resource/DriverResource.java b/src/main/java/org/traccar/api/resource/DriverResource.java index 91aa54c5e..19cf74f39 100644 --- a/src/main/java/org/traccar/api/resource/DriverResource.java +++ b/src/main/java/org/traccar/api/resource/DriverResource.java @@ -16,10 +16,10 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Driver; diff --git a/src/main/java/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java index afdaf52b5..1f20b880d 100644 --- a/src/main/java/org/traccar/api/resource/EventResource.java +++ b/src/main/java/org/traccar/api/resource/EventResource.java @@ -23,14 +23,14 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; @Path("events") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/GeofenceResource.java b/src/main/java/org/traccar/api/resource/GeofenceResource.java index 58f2c188c..030690889 100644 --- a/src/main/java/org/traccar/api/resource/GeofenceResource.java +++ b/src/main/java/org/traccar/api/resource/GeofenceResource.java @@ -18,10 +18,10 @@ package org.traccar.api.resource; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Geofence; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("geofences") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/GroupResource.java b/src/main/java/org/traccar/api/resource/GroupResource.java index fcea15d0a..628f8f655 100644 --- a/src/main/java/org/traccar/api/resource/GroupResource.java +++ b/src/main/java/org/traccar/api/resource/GroupResource.java @@ -18,10 +18,10 @@ package org.traccar.api.resource; import org.traccar.api.SimpleObjectResource; import org.traccar.model.Group; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("groups") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/MaintenanceResource.java b/src/main/java/org/traccar/api/resource/MaintenanceResource.java index fa1b359ce..12841e497 100644 --- a/src/main/java/org/traccar/api/resource/MaintenanceResource.java +++ b/src/main/java/org/traccar/api/resource/MaintenanceResource.java @@ -16,10 +16,10 @@ */ package org.traccar.api.resource; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import org.traccar.api.ExtendedObjectResource; import org.traccar.model.Maintenance; diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java index 2e4ad12f3..43dc1fa98 100644 --- a/src/main/java/org/traccar/api/resource/NotificationResource.java +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,16 @@ */ package org.traccar.api.resource; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.api.ExtendedObjectResource; @@ -23,20 +33,16 @@ import org.traccar.model.Notification; import org.traccar.model.Typed; import org.traccar.model.User; import org.traccar.notification.MessageException; +import org.traccar.notification.NotificationMessage; import org.traccar.notification.NotificatorManager; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -80,10 +86,10 @@ public class NotificationResource extends ExtendedObjectResource<Notification> { @POST @Path("test") - public Response testMessage() throws MessageException, InterruptedException, StorageException { + public Response testMessage() throws MessageException, StorageException { User user = permissionsService.getUser(getUserId()); for (Typed method : notificatorManager.getAllNotificatorTypes()) { - notificatorManager.getNotificator(method.getType()).send(user, new Event("test", 0), null); + notificatorManager.getNotificator(method.getType()).send(null, user, new Event("test", 0), null); } return Response.noContent().build(); } @@ -91,9 +97,31 @@ public class NotificationResource extends ExtendedObjectResource<Notification> { @POST @Path("test/{notificator}") public Response testMessage(@PathParam("notificator") String notificator) - throws MessageException, InterruptedException, StorageException { + throws MessageException, StorageException { User user = permissionsService.getUser(getUserId()); - notificatorManager.getNotificator(notificator).send(user, new Event("test", 0), null); + notificatorManager.getNotificator(notificator).send(null, user, new Event("test", 0), null); + return Response.noContent().build(); + } + + @POST + @Path("send/{notificator}") + public Response sendMessage( + @PathParam("notificator") String notificator, @QueryParam("userId") List<Long> userIds, + NotificationMessage message) throws MessageException, StorageException { + permissionsService.checkAdmin(getUserId()); + List<User> users; + if (userIds.isEmpty()) { + users = storage.getObjects(User.class, new Request(new Columns.All())); + } else { + users = new ArrayList<>(); + for (long userId : userIds) { + users.add(storage.getObject( + User.class, new Request(new Columns.All(), new Condition.Equals("id", userId)))); + } + } + for (User user : users) { + notificatorManager.getNotificator(notificator).send(user, message, null, null); + } return Response.noContent().build(); } diff --git a/src/main/java/org/traccar/api/resource/OrderResource.java b/src/main/java/org/traccar/api/resource/OrderResource.java index 77608a508..3852b975f 100644 --- a/src/main/java/org/traccar/api/resource/OrderResource.java +++ b/src/main/java/org/traccar/api/resource/OrderResource.java @@ -18,10 +18,10 @@ package org.traccar.api.resource; import org.traccar.api.SimpleObjectResource; import org.traccar.model.Order; -import javax.ws.rs.Consumes; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("orders") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/traccar/api/resource/PasswordResource.java b/src/main/java/org/traccar/api/resource/PasswordResource.java index 2d87a8665..22b3f0cd3 100644 --- a/src/main/java/org/traccar/api/resource/PasswordResource.java +++ b/src/main/java/org/traccar/api/resource/PasswordResource.java @@ -25,16 +25,16 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.annotation.security.PermitAll; -import javax.inject.Inject; -import javax.mail.MessagingException; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.IOException; import java.security.GeneralSecurityException; @@ -63,7 +63,7 @@ public class PasswordResource extends BaseResource { if (user != null) { var velocityContext = textTemplateFormatter.prepareContext(permissionsService.getServer(), user); var fullMessage = textTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full"); - mailManager.sendMessage(user, fullMessage.getSubject(), fullMessage.getBody()); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); } return Response.ok().build(); } @@ -75,7 +75,7 @@ public class PasswordResource extends BaseResource { @FormParam("token") String token, @FormParam("password") String password) throws StorageException, GeneralSecurityException, IOException { - long userId = tokenManager.verifyToken(token); + long userId = tokenManager.verifyToken(token).getUserId(); User user = storage.getObject(User.class, new Request( new Columns.All(), new Condition.Equals("id", userId))); if (user != null) { diff --git a/src/main/java/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java index d35cb98bb..9e2d21f2c 100644 --- a/src/main/java/org/traccar/api/resource/PermissionsResource.java +++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java @@ -23,15 +23,15 @@ import org.traccar.model.UserRestrictions; import org.traccar.session.cache.CacheManager; import org.traccar.storage.StorageException; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -48,7 +48,7 @@ public class PermissionsResource extends BaseResource { private void checkPermission(Permission permission) throws StorageException { if (permissionsService.notAdmin(getUserId())) { permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId()); - permissionsService.checkPermission(permission.getOwnerClass(), getUserId(), permission.getOwnerId()); + permissionsService.checkPermission(permission.getPropertyClass(), getUserId(), permission.getPropertyId()); } } @@ -64,7 +64,7 @@ public class PermissionsResource extends BaseResource { @Path("bulk") @POST - public Response add(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException { + public Response add(List<LinkedHashMap<String, Long>> entities) throws Exception { permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly); checkPermissionTypes(entities); for (LinkedHashMap<String, Long> entity: entities) { @@ -74,7 +74,8 @@ public class PermissionsResource extends BaseResource { cacheManager.invalidatePermission( true, permission.getOwnerClass(), permission.getOwnerId(), - permission.getPropertyClass(), permission.getPropertyId()); + permission.getPropertyClass(), permission.getPropertyId(), + true); LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(), permission.getPropertyClass(), permission.getPropertyId()); @@ -83,13 +84,13 @@ public class PermissionsResource extends BaseResource { } @POST - public Response add(LinkedHashMap<String, Long> entity) throws StorageException, ClassNotFoundException { + public Response add(LinkedHashMap<String, Long> entity) throws Exception { return add(Collections.singletonList(entity)); } @DELETE @Path("bulk") - public Response remove(List<LinkedHashMap<String, Long>> entities) throws StorageException, ClassNotFoundException { + public Response remove(List<LinkedHashMap<String, Long>> entities) throws Exception { permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly); checkPermissionTypes(entities); for (LinkedHashMap<String, Long> entity: entities) { @@ -99,7 +100,8 @@ public class PermissionsResource extends BaseResource { cacheManager.invalidatePermission( true, permission.getOwnerClass(), permission.getOwnerId(), - permission.getPropertyClass(), permission.getPropertyId()); + permission.getPropertyClass(), permission.getPropertyId(), + false); LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(), permission.getPropertyClass(), permission.getPropertyId()); @@ -108,7 +110,7 @@ public class PermissionsResource extends BaseResource { } @DELETE - public Response remove(LinkedHashMap<String, Long> entity) throws StorageException, ClassNotFoundException { + public Response remove(LinkedHashMap<String, Long> entity) throws Exception { return remove(Collections.singletonList(entity)); } diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java index 042dd1e23..0d783a0fe 100644 --- a/src/main/java/org/traccar/api/resource/PositionResource.java +++ b/src/main/java/org/traccar/api/resource/PositionResource.java @@ -28,21 +28,23 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.StreamingOutput; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.LinkedList; @Path("positions") @Produces(MediaType.APPLICATION_JSON) @@ -86,6 +88,21 @@ public class PositionResource extends BaseResource { } } + @DELETE + public Response remove( + @QueryParam("deviceId") long deviceId, + @QueryParam("from") Date from, @QueryParam("to") Date to) throws StorageException { + permissionsService.checkPermission(Device.class, getUserId(), deviceId); + permissionsService.checkRestriction(getUserId(), UserRestrictions::getReadonly); + + var conditions = new LinkedList<Condition>(); + conditions.add(new Condition.Equals("deviceId", deviceId)); + conditions.add(new Condition.Between("fixTime", "from", from, "to", to)); + storage.removeObject(Position.class, new Request(Condition.merge(conditions))); + + return Response.status(Response.Status.NO_CONTENT).build(); + } + @Path("kml") @GET @Produces("application/vnd.google-earth.kml+xml") diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java index 6944de9cb..55a96fa90 100644 --- a/src/main/java/org/traccar/api/resource/ReportResource.java +++ b/src/main/java/org/traccar/api/resource/ReportResource.java @@ -16,13 +16,14 @@ */ package org.traccar.api.resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.api.BaseResource; +import org.traccar.api.SimpleObjectResource; import org.traccar.helper.LogAction; import org.traccar.model.Event; import org.traccar.model.Position; +import org.traccar.model.Report; import org.traccar.model.UserRestrictions; +import org.traccar.reports.CombinedReportProvider; +import org.traccar.reports.DevicesReportProvider; import org.traccar.reports.EventsReportProvider; import org.traccar.reports.RouteReportProvider; import org.traccar.reports.StopsReportProvider; @@ -30,23 +31,24 @@ import org.traccar.reports.SummaryReportProvider; import org.traccar.reports.TripsReportProvider; import org.traccar.reports.common.ReportExecutor; import org.traccar.reports.common.ReportMailer; +import org.traccar.reports.model.CombinedReportItem; import org.traccar.reports.model.StopReportItem; import org.traccar.reports.model.SummaryReportItem; import org.traccar.reports.model.TripReportItem; import org.traccar.storage.StorageException; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.StreamingOutput; import java.util.Collection; import java.util.Date; import java.util.List; @@ -54,13 +56,14 @@ import java.util.List; @Path("reports") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class ReportResource extends BaseResource { - - private static final Logger LOGGER = LoggerFactory.getLogger(ReportResource.class); +public class ReportResource extends SimpleObjectResource<Report> { private static final String EXCEL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; @Inject + private CombinedReportProvider combinedReportProvider; + + @Inject private EventsReportProvider eventsReportProvider; @Inject @@ -76,8 +79,15 @@ public class ReportResource extends BaseResource { private TripsReportProvider tripsReportProvider; @Inject + private DevicesReportProvider devicesReportProvider; + + @Inject private ReportMailer reportMailer; + public ReportResource() { + super(Report.class); + } + private Response executeReport(long userId, boolean mail, ReportExecutor executor) { if (mail) { reportMailer.sendAsync(userId, executor); @@ -95,6 +105,18 @@ public class ReportResource extends BaseResource { } } + @Path("combined") + @GET + public Collection<CombinedReportItem> getCombined( + @QueryParam("deviceId") List<Long> deviceIds, + @QueryParam("groupId") List<Long> groupIds, + @QueryParam("from") Date from, + @QueryParam("to") Date to) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); + LogAction.logReport(getUserId(), "combined", from, to, deviceIds, groupIds); + return combinedReportProvider.getObjects(getUserId(), deviceIds, groupIds, from, to); + } + @Path("route") @GET public Collection<Position> getRoute( @@ -301,4 +323,15 @@ public class ReportResource extends BaseResource { return getStopsExcel(deviceIds, groupIds, from, to, type.equals("mail")); } + @Path("devices/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response geDevicesExcel( + @PathParam("type") String type) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); + return executeReport(getUserId(), type.equals("mail"), stream -> { + devicesReportProvider.getExcel(stream, getUserId()); + }); + } + } diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java index 4b7ee9189..2a72d2773 100644 --- a/src/main/java/org/traccar/api/resource/ServerResource.java +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,30 +16,43 @@ package org.traccar.api.resource; import org.traccar.api.BaseResource; -import org.traccar.helper.model.UserUtil; -import org.traccar.mail.MailManager; +import org.traccar.model.ObjectOperation; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.database.OpenIdProvider; import org.traccar.geocoder.Geocoder; import org.traccar.helper.Log; import org.traccar.helper.LogAction; +import org.traccar.helper.model.UserUtil; +import org.traccar.mail.MailManager; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.session.cache.CacheManager; +import org.traccar.sms.SmsManager; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.annotation.Nullable; -import javax.annotation.security.PermitAll; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.annotation.Nullable; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; import java.util.TimeZone; @@ -50,6 +63,9 @@ import java.util.TimeZone; public class ServerResource extends BaseResource { @Inject + private Config config; + + @Inject private CacheManager cacheManager; @Inject @@ -57,6 +73,14 @@ public class ServerResource extends BaseResource { @Inject @Nullable + private SmsManager smsManager; + + @Inject + @Nullable + private OpenIdProvider openIdProvider; + + @Inject + @Nullable private Geocoder geocoder; @PermitAll @@ -64,7 +88,10 @@ public class ServerResource extends BaseResource { public Server get() throws StorageException { Server server = storage.getObject(Server.class, new Request(new Columns.All())); server.setEmailEnabled(mailManager.getEmailEnabled()); + server.setTextEnabled(smsManager != null); server.setGeocoderEnabled(geocoder != null); + server.setOpenIdEnabled(openIdProvider != null); + server.setOpenIdForce(openIdProvider != null && openIdProvider.getForce()); User user = permissionsService.getUser(getUserId()); if (user != null) { if (user.getAdministrator()) { @@ -73,21 +100,18 @@ public class ServerResource extends BaseResource { } else { server.setNewServer(UserUtil.isEmpty(storage)); } - if (user != null && user.getAdministrator()) { - server.setStorageSpace(Log.getStorageSpace()); - } return server; } @PUT - public Response update(Server entity) throws StorageException { + public Response update(Server server) throws Exception { permissionsService.checkAdmin(getUserId()); - storage.updateObject(entity, new Request( + storage.updateObject(server, new Request( new Columns.Exclude("id"), - new Condition.Equals("id", entity.getId()))); - cacheManager.updateOrInvalidate(true, entity); - LogAction.edit(getUserId(), entity); - return Response.ok(entity).build(); + new Condition.Equals("id", server.getId()))); + cacheManager.invalidateObject(true, Server.class, server.getId(), ObjectOperation.UPDATE); + LogAction.edit(getUserId(), server); + return Response.ok(server).build(); } @Path("geocode") @@ -106,4 +130,35 @@ public class ServerResource extends BaseResource { return Arrays.asList(TimeZone.getAvailableIDs()); } + @Path("file/{path}") + @POST + @Consumes("*/*") + public Response uploadFile(@PathParam("path") String path, File inputFile) throws IOException, StorageException { + permissionsService.checkAdmin(getUserId()); + String root = config.getString(Keys.WEB_OVERRIDE, config.getString(Keys.WEB_PATH)); + + var rootPath = Paths.get(root).normalize(); + var outputPath = rootPath.resolve(path).normalize(); + if (!outputPath.startsWith(rootPath)) { + return Response.status(Response.Status.BAD_REQUEST).build(); + } + + var directoryPath = outputPath.getParent(); + if (directoryPath != null) { + Files.createDirectories(directoryPath); + } + + try (var input = new FileInputStream(inputFile); var output = new FileOutputStream(outputPath.toFile())) { + input.transferTo(output); + } + return Response.ok().build(); + } + + @Path("cache") + @GET + public String cache() throws StorageException { + permissionsService.checkAdmin(getUserId()); + return cacheManager.toString(); + } + } diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java index 7025d5fa7..979b62589 100644 --- a/src/main/java/org/traccar/api/resource/SessionResource.java +++ b/src/main/java/org/traccar/api/resource/SessionResource.java @@ -16,39 +16,41 @@ package org.traccar.api.resource; import org.traccar.api.BaseResource; +import org.traccar.api.security.CodeRequiredException; +import org.traccar.api.security.LoginResult; import org.traccar.api.security.LoginService; import org.traccar.api.signature.TokenManager; -import org.traccar.helper.DataConverter; +import org.traccar.database.OpenIdProvider; import org.traccar.helper.LogAction; -import org.traccar.helper.ServletHelper; +import org.traccar.helper.WebHelper; import org.traccar.model.User; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.annotation.security.PermitAll; -import javax.inject.Inject; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import com.nimbusds.oauth2.sdk.ParseException; +import jakarta.annotation.Nullable; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.IOException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Date; +import java.net.URI; @Path("session") @Produces(MediaType.APPLICATION_JSON) @@ -56,13 +58,16 @@ import java.util.Date; public class SessionResource extends BaseResource { public static final String USER_ID_KEY = "userId"; - public static final String USER_COOKIE_KEY = "user"; - public static final String PASS_COOKIE_KEY = "password"; + public static final String EXPIRATION_KEY = "expiration"; @Inject private LoginService loginService; @Inject + @Nullable + private OpenIdProvider openIdProvider; + + @Inject private TokenManager tokenManager; @Context @@ -73,48 +78,22 @@ public class SessionResource extends BaseResource { public User get(@QueryParam("token") String token) throws StorageException, IOException, GeneralSecurityException { if (token != null) { - User user = loginService.login(token); - if (user != null) { + LoginResult loginResult = loginService.login(token); + if (loginResult != null) { + User user = loginResult.getUser(); request.getSession().setAttribute(USER_ID_KEY, user.getId()); - LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request)); + request.getSession().setAttribute(EXPIRATION_KEY, loginResult.getExpiration()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); return user; } } Long userId = (Long) request.getSession().getAttribute(USER_ID_KEY); - if (userId == null) { - - Cookie[] cookies = request.getCookies(); - String email = null, password = null; - if (cookies != null) { - for (Cookie cookie : cookies) { - if (cookie.getName().equals(USER_COOKIE_KEY)) { - byte[] emailBytes = DataConverter.parseBase64( - URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII)); - email = new String(emailBytes, StandardCharsets.UTF_8); - } else if (cookie.getName().equals(PASS_COOKIE_KEY)) { - byte[] passwordBytes = DataConverter.parseBase64( - URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII)); - password = new String(passwordBytes, StandardCharsets.UTF_8); - } - } - } - if (email != null && password != null) { - User user = loginService.login(email, password); - if (user != null) { - request.getSession().setAttribute(USER_ID_KEY, user.getId()); - LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request)); - return user; - } - } - - } else { - + if (userId != null) { User user = permissionsService.getUser(userId); if (user != null) { return user; } - } throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); @@ -123,32 +102,44 @@ public class SessionResource extends BaseResource { @Path("{id}") @GET public User get(@PathParam("id") long userId) throws StorageException { - permissionsService.checkAdmin(getUserId()); + permissionsService.checkUser(getUserId(), userId); User user = storage.getObject(User.class, new Request( new Columns.All(), new Condition.Equals("id", userId))); request.getSession().setAttribute(USER_ID_KEY, user.getId()); - LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request)); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); return user; } @PermitAll @POST public User add( - @FormParam("email") String email, @FormParam("password") String password) throws StorageException { - User user = loginService.login(email, password); - if (user != null) { + @FormParam("email") String email, + @FormParam("password") String password, + @FormParam("code") Integer code) throws StorageException { + LoginResult loginResult; + try { + loginResult = loginService.login(email, password, code); + } catch (CodeRequiredException e) { + Response response = Response + .status(Response.Status.UNAUTHORIZED) + .header("WWW-Authenticate", "TOTP") + .build(); + throw new WebApplicationException(response); + } + if (loginResult != null) { + User user = loginResult.getUser(); request.getSession().setAttribute(USER_ID_KEY, user.getId()); - LogAction.login(user.getId(), ServletHelper.retrieveRemoteAddress(request)); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); return user; } else { - LogAction.failedLogin(ServletHelper.retrieveRemoteAddress(request)); + LogAction.failedLogin(WebHelper.retrieveRemoteAddress(request)); throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()); } } @DELETE public Response remove() { - LogAction.logout(getUserId(), ServletHelper.retrieveRemoteAddress(request)); + LogAction.logout(getUserId(), WebHelper.retrieveRemoteAddress(request)); request.getSession().removeAttribute(USER_ID_KEY); return Response.noContent().build(); } @@ -157,7 +148,28 @@ public class SessionResource extends BaseResource { @POST public String requestToken( @FormParam("expiration") Date expiration) throws StorageException, GeneralSecurityException, IOException { + Date currentExpiration = (Date) request.getSession().getAttribute(EXPIRATION_KEY); + if (currentExpiration != null && currentExpiration.before(expiration)) { + expiration = currentExpiration; + } return tokenManager.generateToken(getUserId(), expiration); } + @PermitAll + @Path("openid/auth") + @GET + public Response openIdAuth() { + return Response.seeOther(openIdProvider.createAuthUri()).build(); + } + + @PermitAll + @Path("openid/callback") + @GET + public Response requestToken() throws IOException, StorageException, ParseException, GeneralSecurityException { + StringBuilder requestUrl = new StringBuilder(request.getRequestURL().toString()); + String queryString = request.getQueryString(); + String requestUri = requestUrl.append('?').append(queryString).toString(); + + return Response.seeOther(openIdProvider.handleCallback(URI.create(requestUri), request)).build(); + } } diff --git a/src/main/java/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java index 1f2296f28..0c728c77d 100644 --- a/src/main/java/org/traccar/api/resource/StatisticsResource.java +++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java @@ -23,12 +23,12 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; import java.util.Collection; import java.util.Date; diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java index e41ebbe61..47ea9b07c 100644 --- a/src/main/java/org/traccar/api/resource/UserResource.java +++ b/src/main/java/org/traccar/api/resource/UserResource.java @@ -15,6 +15,11 @@ */ package org.traccar.api.resource; +import com.warrenstrange.googleauth.GoogleAuthenticator; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.Context; import org.traccar.api.BaseObjectResource; import org.traccar.config.Config; import org.traccar.config.Keys; @@ -28,18 +33,17 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.annotation.security.PermitAll; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.util.Collection; -import java.util.Date; @Path("users") @Produces(MediaType.APPLICATION_JSON) @@ -49,6 +53,9 @@ public class UserResource extends BaseObjectResource<User> { @Inject private Config config; + @Context + private HttpServletRequest request; + public UserResource() { super(User.class); } @@ -91,11 +98,11 @@ public class UserResource extends BaseObjectResource<User> { if (!permissionsService.getServer().getRegistration()) { throw new SecurityException("Registration disabled"); } - entity.setDeviceLimit(config.getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT)); - int expirationDays = config.getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS); - if (expirationDays > 0) { - entity.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L)); + if (permissionsService.getServer().getBoolean(Keys.WEB_TOTP_FORCE.getKey()) + && entity.getTotpKey() == null) { + throw new SecurityException("One-time password key is required"); } + UserUtil.setUserDefaults(entity, config); } } @@ -117,4 +124,24 @@ public class UserResource extends BaseObjectResource<User> { return Response.ok(entity).build(); } + @Path("{id}") + @DELETE + public Response remove(@PathParam("id") long id) throws Exception { + Response response = super.remove(id); + if (getUserId() == id) { + request.getSession().removeAttribute(SessionResource.USER_ID_KEY); + } + return response; + } + + @Path("totp") + @PermitAll + @POST + public String generateTotpKey() throws StorageException { + if (!permissionsService.getServer().getBoolean(Keys.WEB_TOTP_ENABLE.getKey())) { + throw new SecurityException("One-time password is disabled"); + } + return new GoogleAuthenticator().createCredentials().getKey(); + } + } diff --git a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java b/src/main/java/org/traccar/api/security/CodeRequiredException.java index 5b610013a..d522c6540 100644 --- a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java +++ b/src/main/java/org/traccar/api/security/CodeRequiredException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.traccar.protocol; +package org.traccar.api.security; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import org.traccar.BaseFrameDecoder; - -public class NdtpV6FrameDecoder extends BaseFrameDecoder { - - @Override - protected Object decode( - ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { - - return buf; +public class CodeRequiredException extends SecurityException { + public CodeRequiredException() { + super("Code not provided"); } - } diff --git a/src/main/java/org/traccar/api/security/LoginResult.java b/src/main/java/org/traccar/api/security/LoginResult.java new file mode 100644 index 000000000..1fccc36d1 --- /dev/null +++ b/src/main/java/org/traccar/api/security/LoginResult.java @@ -0,0 +1,29 @@ +package org.traccar.api.security; + +import org.traccar.model.User; + +import java.util.Date; + +public class LoginResult { + + private final User user; + private final Date expiration; + + public LoginResult(User user) { + this(user, null); + } + + public LoginResult(User user, Date expiration) { + this.user = user; + this.expiration = expiration; + } + + public User getUser() { + return user; + } + + public Date getExpiration() { + return expiration; + } + +} diff --git a/src/main/java/org/traccar/api/security/LoginService.java b/src/main/java/org/traccar/api/security/LoginService.java index 88bafcfb5..930c4fa46 100644 --- a/src/main/java/org/traccar/api/security/LoginService.java +++ b/src/main/java/org/traccar/api/security/LoginService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,12 @@ */ package org.traccar.api.security; +import com.warrenstrange.googleauth.GoogleAuthenticator; import org.traccar.api.signature.TokenManager; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.database.LdapProvider; +import org.traccar.helper.model.UserUtil; import org.traccar.model.User; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; @@ -26,46 +28,54 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.IOException; import java.security.GeneralSecurityException; @Singleton public class LoginService { + private final Config config; private final Storage storage; private final TokenManager tokenManager; private final LdapProvider ldapProvider; private final String serviceAccountToken; private final boolean forceLdap; + private final boolean forceOpenId; @Inject public LoginService( Config config, Storage storage, TokenManager tokenManager, @Nullable LdapProvider ldapProvider) { this.storage = storage; + this.config = config; this.tokenManager = tokenManager; this.ldapProvider = ldapProvider; serviceAccountToken = config.getString(Keys.WEB_SERVICE_ACCOUNT_TOKEN); forceLdap = config.getBoolean(Keys.LDAP_FORCE); + forceOpenId = config.getBoolean(Keys.OPENID_FORCE); } - public User login(String token) throws StorageException, GeneralSecurityException, IOException { + public LoginResult login(String token) throws StorageException, GeneralSecurityException, IOException { if (serviceAccountToken != null && serviceAccountToken.equals(token)) { - return new ServiceAccountUser(); + return new LoginResult(new ServiceAccountUser()); } - long userId = tokenManager.verifyToken(token); + TokenManager.TokenData tokenData = tokenManager.verifyToken(token); User user = storage.getObject(User.class, new Request( - new Columns.All(), new Condition.Equals("id", userId))); + new Columns.All(), new Condition.Equals("id", tokenData.getUserId()))); if (user != null) { checkUserEnabled(user); } - return user; + return new LoginResult(user, tokenData.getExpiration()); } - public User login(String email, String password) throws StorageException { + public LoginResult login(String email, String password, Integer code) throws StorageException { + if (forceOpenId) { + return null; + } + email = email.trim(); User user = storage.getObject(User.class, new Request( new Columns.All(), @@ -75,20 +85,39 @@ public class LoginService { if (user != null) { if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password) || !forceLdap && user.isPasswordValid(password)) { + checkUserCode(user, code); checkUserEnabled(user); - return user; + return new LoginResult(user); } } else { if (ldapProvider != null && ldapProvider.login(email, password)) { user = ldapProvider.getUser(email); user.setId(storage.addObject(user, new Request(new Columns.Exclude("id")))); checkUserEnabled(user); - return user; + return new LoginResult(user); } } return null; } + public LoginResult login(String email, String name, boolean administrator) throws StorageException { + User user = storage.getObject(User.class, new Request( + new Columns.All(), + new Condition.Equals("email", email))); + + if (user == null) { + user = new User(); + UserUtil.setUserDefaults(user, config); + user.setName(name); + user.setEmail(email); + user.setFixedEmail(true); + user.setAdministrator(administrator); + user.setId(storage.addObject(user, new Request(new Columns.Exclude("id")))); + } + checkUserEnabled(user); + return new LoginResult(user); + } + private void checkUserEnabled(User user) throws SecurityException { if (user == null) { throw new SecurityException("Unknown account"); @@ -96,4 +125,17 @@ public class LoginService { user.checkDisabled(); } + private void checkUserCode(User user, Integer code) throws SecurityException { + String key = user.getTotpKey(); + if (key != null && !key.isEmpty()) { + if (code == null) { + throw new CodeRequiredException(); + } + GoogleAuthenticator authenticator = new GoogleAuthenticator(); + if (!authenticator.authorize(key, code)) { + throw new SecurityException("User authorization failed"); + } + } + } + } diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java index 4421572d7..d60bbafb8 100644 --- a/src/main/java/org/traccar/api/security/PermissionsService.java +++ b/src/main/java/org/traccar/api/security/PermissionsService.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ import org.traccar.model.Device; import org.traccar.model.Group; import org.traccar.model.GroupedModel; import org.traccar.model.ManagedUser; -import org.traccar.model.ScheduledModel; +import org.traccar.model.Notification; +import org.traccar.model.Schedulable; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.model.UserRestrictions; @@ -33,7 +34,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.util.Objects; @RequestScoped @@ -129,17 +130,17 @@ public class PermissionsService { GroupedModel before = null; if (!addition) { before = storage.getObject(after.getClass(), new Request( - new Columns.Include("groupId"), new Condition.Equals("id", object.getId()))); + new Columns.Include("groupId"), new Condition.Equals("id", after.getId()))); } if (before == null || before.getGroupId() != after.getGroupId()) { checkPermission(Group.class, userId, after.getGroupId()); } } } - if (object instanceof ScheduledModel) { - ScheduledModel after = ((ScheduledModel) object); + if (object instanceof Schedulable) { + Schedulable after = ((Schedulable) object); if (after.getCalendarId() > 0) { - ScheduledModel before = null; + Schedulable before = null; if (!addition) { before = storage.getObject(after.getClass(), new Request( new Columns.Include("calendarId"), new Condition.Equals("id", object.getId()))); @@ -149,6 +150,19 @@ public class PermissionsService { } } } + if (object instanceof Notification) { + Notification after = ((Notification) object); + if (after.getCommandId() > 0) { + Notification before = null; + if (!addition) { + before = storage.getObject(after.getClass(), new Request( + new Columns.Include("commandId"), new Condition.Equals("id", object.getId()))); + } + if (before == null || before.getCommandId() != after.getCommandId()) { + checkPermission(Command.class, userId, after.getCommandId()); + } + } + } } } @@ -167,7 +181,7 @@ public class PermissionsService { || before.getUserLimit() != after.getUserLimit()) { checkAdmin(userId); } - User user = getUser(userId); + User user = userId > 0 ? getUser(userId) : null; if (user != null && user.getExpirationTime() != null && !Objects.equals(before.getExpirationTime(), after.getExpirationTime()) && (after.getExpirationTime() == null diff --git a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java index 94b6bbf05..12a5dbecf 100644 --- a/src/main/java/org/traccar/api/security/SecurityRequestFilter.java +++ b/src/main/java/org/traccar/api/security/SecurityRequestFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.traccar.api.security; +import com.google.inject.Injector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.api.resource.SessionResource; @@ -23,32 +24,26 @@ import org.traccar.helper.DataConverter; import org.traccar.model.User; import org.traccar.storage.StorageException; -import javax.annotation.security.PermitAll; -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ResourceInfo; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; import java.io.IOException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; +import java.util.Date; public class SecurityRequestFilter implements ContainerRequestFilter { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityRequestFilter.class); - public static final String AUTHORIZATION_HEADER = "Authorization"; - public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; - public static final String BASIC_REALM = "Basic realm=\"api\""; - public static final String BEARER_PREFIX = "Bearer "; - public static final String X_REQUESTED_WITH = "X-Requested-With"; - public static final String XML_HTTP_REQUEST = "XMLHttpRequest"; - public static String[] decodeBasicAuth(String auth) { auth = auth.replaceFirst("[B|b]asic ", ""); byte[] decodedBytes = DataConverter.parseBase64(auth); @@ -70,6 +65,9 @@ public class SecurityRequestFilter implements ContainerRequestFilter { @Inject private StatisticsManager statisticsManager; + @Inject + private Injector injector; + @Override public void filter(ContainerRequestContext requestContext) { @@ -81,20 +79,22 @@ public class SecurityRequestFilter implements ContainerRequestFilter { try { - String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER); + String authHeader = requestContext.getHeaderString("Authorization"); if (authHeader != null) { try { - User user; - if (authHeader.startsWith(BEARER_PREFIX)) { - user = loginService.login(authHeader.substring(BEARER_PREFIX.length())); + LoginResult loginResult; + if (authHeader.startsWith("Bearer ")) { + loginResult = loginService.login(authHeader.substring(7)); } else { String[] auth = decodeBasicAuth(authHeader); - user = loginService.login(auth[0], auth[1]); + loginResult = loginService.login(auth[0], auth[1], null); } - if (user != null) { + if (loginResult != null) { + User user = loginResult.getUser(); statisticsManager.registerRequest(user.getId()); - securityContext = new UserSecurityContext(new UserPrincipal(user.getId())); + securityContext = new UserSecurityContext( + new UserPrincipal(user.getId(), loginResult.getExpiration())); } } catch (StorageException | GeneralSecurityException | IOException e) { throw new WebApplicationException(e); @@ -103,14 +103,19 @@ public class SecurityRequestFilter implements ContainerRequestFilter { } else if (request.getSession() != null) { Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY); + Date expiration = (Date) request.getSession().getAttribute(SessionResource.EXPIRATION_KEY); if (userId != null) { - statisticsManager.registerRequest(userId); - securityContext = new UserSecurityContext(new UserPrincipal(userId)); + User user = injector.getInstance(PermissionsService.class).getUser(userId); + if (user != null) { + user.checkDisabled(); + statisticsManager.registerRequest(userId); + securityContext = new UserSecurityContext(new UserPrincipal(userId, expiration)); + } } } - } catch (SecurityException e) { + } catch (SecurityException | StorageException e) { LOGGER.warn("Authentication error", e); } @@ -120,8 +125,9 @@ public class SecurityRequestFilter implements ContainerRequestFilter { Method method = resourceInfo.getResourceMethod(); if (!method.isAnnotationPresent(PermitAll.class)) { Response.ResponseBuilder responseBuilder = Response.status(Response.Status.UNAUTHORIZED); - if (!XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) { - responseBuilder.header(WWW_AUTHENTICATE, BASIC_REALM); + String accept = request.getHeader("Accept"); + if (accept != null && accept.contains("text/html")) { + responseBuilder.header("WWW-Authenticate", "Basic realm=\"api\""); } throw new WebApplicationException(responseBuilder.build()); } diff --git a/src/main/java/org/traccar/api/security/UserPrincipal.java b/src/main/java/org/traccar/api/security/UserPrincipal.java index 18b84a0e1..83bd06fe9 100644 --- a/src/main/java/org/traccar/api/security/UserPrincipal.java +++ b/src/main/java/org/traccar/api/security/UserPrincipal.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,26 @@ package org.traccar.api.security; import java.security.Principal; +import java.util.Date; public class UserPrincipal implements Principal { private final long userId; + private final Date expiration; - public UserPrincipal(long userId) { + public UserPrincipal(long userId, Date expiration) { this.userId = userId; + this.expiration = expiration; } public Long getUserId() { return userId; } + public Date getExpiration() { + return expiration; + } + @Override public String getName() { return null; diff --git a/src/main/java/org/traccar/api/security/UserSecurityContext.java b/src/main/java/org/traccar/api/security/UserSecurityContext.java index 97df6b6c7..f7adeac64 100644 --- a/src/main/java/org/traccar/api/security/UserSecurityContext.java +++ b/src/main/java/org/traccar/api/security/UserSecurityContext.java @@ -15,7 +15,7 @@ */ package org.traccar.api.security; -import javax.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.SecurityContext; import java.security.Principal; public class UserSecurityContext implements SecurityContext { diff --git a/src/main/java/org/traccar/api/signature/CryptoManager.java b/src/main/java/org/traccar/api/signature/CryptoManager.java index 249d5bd97..71f56e0fb 100644 --- a/src/main/java/org/traccar/api/signature/CryptoManager.java +++ b/src/main/java/org/traccar/api/signature/CryptoManager.java @@ -20,8 +20,8 @@ import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyPair; diff --git a/src/main/java/org/traccar/api/signature/TokenManager.java b/src/main/java/org/traccar/api/signature/TokenManager.java index 6a0d90b40..824433b08 100644 --- a/src/main/java/org/traccar/api/signature/TokenManager.java +++ b/src/main/java/org/traccar/api/signature/TokenManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; import org.traccar.storage.StorageException; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Date; @@ -35,11 +35,19 @@ public class TokenManager { private final ObjectMapper objectMapper; private final CryptoManager cryptoManager; - public static class Data { + public static class TokenData { @JsonProperty("u") private long userId; @JsonProperty("e") private Date expiration; + + public long getUserId() { + return userId; + } + + public Date getExpiration() { + return expiration; + } } @Inject @@ -54,7 +62,7 @@ public class TokenManager { public String generateToken( long userId, Date expiration) throws IOException, GeneralSecurityException, StorageException { - Data data = new Data(); + TokenData data = new TokenData(); data.userId = userId; if (expiration != null) { data.expiration = expiration; @@ -65,13 +73,13 @@ public class TokenManager { return Base64.encodeBase64URLSafeString(cryptoManager.sign(encoded)); } - public long verifyToken(String token) throws IOException, GeneralSecurityException, StorageException { + public TokenData verifyToken(String token) throws IOException, GeneralSecurityException, StorageException { byte[] encoded = cryptoManager.verify(Base64.decodeBase64(token)); - Data data = objectMapper.readValue(encoded, Data.class); + TokenData data = objectMapper.readValue(encoded, TokenData.class); if (data.expiration.before(new Date())) { throw new SecurityException("Token has expired"); } - return data.userId; + return data; } } diff --git a/src/main/java/org/traccar/broadcast/BaseBroadcastService.java b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java new file mode 100644 index 000000000..01b212c60 --- /dev/null +++ b/src/main/java/org/traccar/broadcast/BaseBroadcastService.java @@ -0,0 +1,128 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.broadcast; + +import java.util.HashSet; +import java.util.Set; + +import org.traccar.model.BaseModel; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; +import org.traccar.model.Permission; +import org.traccar.model.Position; + +public abstract class BaseBroadcastService implements BroadcastService { + + private final Set<BroadcastInterface> listeners = new HashSet<>(); + + @Override + public boolean singleInstance() { + return true; + } + + @Override + public void registerListener(BroadcastInterface listener) { + listeners.add(listener); + } + + @Override + public void updateDevice(boolean local, Device device) { + BroadcastMessage message = new BroadcastMessage(); + message.setDevice(device); + sendMessage(message); + } + + @Override + public void updatePosition(boolean local, Position position) { + BroadcastMessage message = new BroadcastMessage(); + message.setPosition(position); + sendMessage(message); + } + + @Override + public void updateEvent(boolean local, long userId, Event event) { + BroadcastMessage message = new BroadcastMessage(); + message.setUserId(userId); + message.setEvent(event); + sendMessage(message); + } + + @Override + public void updateCommand(boolean local, long deviceId) { + BroadcastMessage message = new BroadcastMessage(); + message.setCommandDeviceId(deviceId); + sendMessage(message); + } + + @Override + public <T extends BaseModel> void invalidateObject( + boolean local, Class<T> clazz, long id, ObjectOperation operation) { + BroadcastMessage message = new BroadcastMessage(); + var invalidateObject = new BroadcastMessage.InvalidateObject(); + invalidateObject.setClazz(Permission.getKey(clazz)); + invalidateObject.setId(id); + invalidateObject.setOperation(operation); + message.setInvalidateObject(invalidateObject); + sendMessage(message); + } + + @Override + public synchronized <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission( + boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) { + BroadcastMessage message = new BroadcastMessage(); + var invalidatePermission = new BroadcastMessage.InvalidatePermission(); + invalidatePermission.setClazz1(Permission.getKey(clazz1)); + invalidatePermission.setId1(id1); + invalidatePermission.setClazz2(Permission.getKey(clazz2)); + invalidatePermission.setId2(id2); + invalidatePermission.setLink(link); + message.setInvalidatePermission(invalidatePermission); + sendMessage(message); + } + + protected abstract void sendMessage(BroadcastMessage message); + + protected void handleMessage(BroadcastMessage message) throws Exception { + if (message.getDevice() != null) { + listeners.forEach(listener -> listener.updateDevice(false, message.getDevice())); + } else if (message.getPosition() != null) { + listeners.forEach(listener -> listener.updatePosition(false, message.getPosition())); + } else if (message.getUserId() != null && message.getEvent() != null) { + listeners.forEach(listener -> listener.updateEvent(false, message.getUserId(), message.getEvent())); + } else if (message.getCommandDeviceId() != null) { + listeners.forEach(listener -> listener.updateCommand(false, message.getCommandDeviceId())); + } else if (message.getInvalidateObject() != null) { + var invalidateObject = message.getInvalidateObject(); + for (BroadcastInterface listener : listeners) { + listener.invalidateObject( + false, + Permission.getKeyClass(invalidateObject.getClazz()), invalidateObject.getId(), + invalidateObject.getOperation()); + } + } else if (message.getInvalidatePermission() != null) { + var invalidatePermission = message.getInvalidatePermission(); + for (BroadcastInterface listener : listeners) { + listener.invalidatePermission( + false, + Permission.getKeyClass(invalidatePermission.getClazz1()), invalidatePermission.getId1(), + Permission.getKeyClass(invalidatePermission.getClazz2()), invalidatePermission.getId2(), + invalidatePermission.getLink()); + } + } + } + +} diff --git a/src/main/java/org/traccar/broadcast/BroadcastInterface.java b/src/main/java/org/traccar/broadcast/BroadcastInterface.java index 673ebd8b8..d0a491cd2 100644 --- a/src/main/java/org/traccar/broadcast/BroadcastInterface.java +++ b/src/main/java/org/traccar/broadcast/BroadcastInterface.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.traccar.broadcast; import org.traccar.model.BaseModel; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; import org.traccar.model.Position; public interface BroadcastInterface { @@ -34,12 +35,12 @@ public interface BroadcastInterface { default void updateCommand(boolean local, long deviceId) { } - default void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) { + default <T extends BaseModel> void invalidateObject( + boolean local, Class<T> clazz, long id, ObjectOperation operation) throws Exception { } - default void invalidatePermission( - boolean local, - Class<? extends BaseModel> clazz1, long id1, - Class<? extends BaseModel> clazz2, long id2) { + default <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission( + boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) throws Exception { } + } diff --git a/src/main/java/org/traccar/broadcast/BroadcastMessage.java b/src/main/java/org/traccar/broadcast/BroadcastMessage.java index 985848d04..0d15d7495 100644 --- a/src/main/java/org/traccar/broadcast/BroadcastMessage.java +++ b/src/main/java/org/traccar/broadcast/BroadcastMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,9 @@ package org.traccar.broadcast; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; import org.traccar.model.Position; -import java.util.Map; - public class BroadcastMessage { private Device device; @@ -73,13 +72,112 @@ public class BroadcastMessage { this.commandDeviceId = commandDeviceId; } - private Map<String, Long> changes; + public static class InvalidateObject { + + private String clazz; + + public String getClazz() { + return clazz; + } + + public void setClazz(String clazz) { + this.clazz = clazz; + } + + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + private ObjectOperation operation; + + public ObjectOperation getOperation() { + return operation; + } + + public void setOperation(ObjectOperation operation) { + this.operation = operation; + } - public Map<String, Long> getChanges() { - return changes; } - public void setChanges(Map<String, Long> changes) { - this.changes = changes; + private InvalidateObject invalidateObject; + + public InvalidateObject getInvalidateObject() { + return invalidateObject; + } + + public void setInvalidateObject(InvalidateObject invalidateObject) { + this.invalidateObject = invalidateObject; } + + public static class InvalidatePermission { + + private String clazz1; + + public String getClazz1() { + return clazz1; + } + + public void setClazz1(String clazz1) { + this.clazz1 = clazz1; + } + + private long id1; + + public long getId1() { + return id1; + } + + public void setId1(long id1) { + this.id1 = id1; + } + + private String clazz2; + + public String getClazz2() { + return clazz2; + } + + public void setClazz2(String clazz2) { + this.clazz2 = clazz2; + } + + private long id2; + + public long getId2() { + return id2; + } + + public void setId2(long id2) { + this.id2 = id2; + } + + private boolean link; + + public boolean getLink() { + return link; + } + + public void setLink(boolean link) { + this.link = link; + } + + } + + private InvalidatePermission invalidatePermission; + + public InvalidatePermission getInvalidatePermission() { + return invalidatePermission; + } + + public void setInvalidatePermission(InvalidatePermission invalidatePermission) { + this.invalidatePermission = invalidatePermission; + } + } diff --git a/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java index b1b66f1e3..793c6df36 100644 --- a/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java +++ b/src/main/java/org/traccar/broadcast/MulticastBroadcastService.java @@ -20,11 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.model.BaseModel; -import org.traccar.model.Device; -import org.traccar.model.Event; -import org.traccar.model.Permission; -import org.traccar.model.Position; import java.io.IOException; import java.net.DatagramPacket; @@ -34,13 +29,10 @@ import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.nio.charset.StandardCharsets; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -public class MulticastBroadcastService implements BroadcastService { +public class MulticastBroadcastService extends BaseBroadcastService { private static final Logger LOGGER = LoggerFactory.getLogger(MulticastBroadcastService.class); @@ -55,8 +47,6 @@ public class MulticastBroadcastService implements BroadcastService { private final ExecutorService service = Executors.newSingleThreadExecutor(); private final byte[] receiverBuffer = new byte[4096]; - private final Set<BroadcastInterface> listeners = new HashSet<>(); - public MulticastBroadcastService(Config config, ObjectMapper objectMapper) throws IOException { this.objectMapper = objectMapper; port = config.getInteger(Keys.BROADCAST_PORT); @@ -76,57 +66,7 @@ public class MulticastBroadcastService implements BroadcastService { } @Override - public void registerListener(BroadcastInterface listener) { - listeners.add(listener); - } - - @Override - public void updateDevice(boolean local, Device device) { - BroadcastMessage message = new BroadcastMessage(); - message.setDevice(device); - sendMessage(message); - } - - @Override - public void updatePosition(boolean local, Position position) { - BroadcastMessage message = new BroadcastMessage(); - message.setPosition(position); - sendMessage(message); - } - - @Override - public void updateEvent(boolean local, long userId, Event event) { - BroadcastMessage message = new BroadcastMessage(); - message.setUserId(userId); - message.setEvent(event); - sendMessage(message); - } - - @Override - public void updateCommand(boolean local, long deviceId) { - BroadcastMessage message = new BroadcastMessage(); - message.setCommandDeviceId(deviceId); - sendMessage(message); - } - - @Override - public void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) { - BroadcastMessage message = new BroadcastMessage(); - message.setChanges(Map.of(Permission.getKey(clazz), id)); - sendMessage(message); - } - - @Override - public void invalidatePermission( - boolean local, - Class<? extends BaseModel> clazz1, long id1, - Class<? extends BaseModel> clazz2, long id2) { - BroadcastMessage message = new BroadcastMessage(); - message.setChanges(Map.of(Permission.getKey(clazz1), id1, Permission.getKey(clazz2), id2)); - sendMessage(message); - } - - private void sendMessage(BroadcastMessage message) { + protected void sendMessage(BroadcastMessage message) { try { byte[] buffer = objectMapper.writeValueAsString(message).getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group); @@ -136,34 +76,6 @@ public class MulticastBroadcastService implements BroadcastService { } } - private void handleMessage(BroadcastMessage message) { - if (message.getDevice() != null) { - listeners.forEach(listener -> listener.updateDevice(false, message.getDevice())); - } else if (message.getPosition() != null) { - listeners.forEach(listener -> listener.updatePosition(false, message.getPosition())); - } else if (message.getUserId() != null && message.getEvent() != null) { - listeners.forEach(listener -> listener.updateEvent(false, message.getUserId(), message.getEvent())); - } else if (message.getCommandDeviceId() != null) { - listeners.forEach(listener -> listener.updateCommand(false, message.getCommandDeviceId())); - } else if (message.getChanges() != null) { - var iterator = message.getChanges().entrySet().iterator(); - if (iterator.hasNext()) { - var first = iterator.next(); - if (iterator.hasNext()) { - var second = iterator.next(); - listeners.forEach(listener -> listener.invalidatePermission( - false, - Permission.getKeyClass(first.getKey()), first.getValue(), - Permission.getKeyClass(second.getKey()), second.getValue())); - } else { - listeners.forEach(listener -> listener.invalidateObject( - false, - Permission.getKeyClass(first.getKey()), first.getValue())); - } - } - } - } - @Override public void start() throws IOException { service.submit(receiver); @@ -191,7 +103,7 @@ public class MulticastBroadcastService implements BroadcastService { } publisherSocket = null; socket.leaveGroup(group, networkInterface); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException(e); } } diff --git a/src/main/java/org/traccar/broadcast/RedisBroadcastService.java b/src/main/java/org/traccar/broadcast/RedisBroadcastService.java new file mode 100644 index 000000000..697c45a4a --- /dev/null +++ b/src/main/java/org/traccar/broadcast/RedisBroadcastService.java @@ -0,0 +1,125 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.broadcast; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPubSub; +import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.exceptions.JedisException; + +public class RedisBroadcastService extends BaseBroadcastService { + + private static final Logger LOGGER = LoggerFactory.getLogger(RedisBroadcastService.class); + + private final ObjectMapper objectMapper; + + private final ExecutorService service = Executors.newSingleThreadExecutor(); + + private final String channel = "traccar"; + + private Jedis subscriber; + private Jedis publisher; + + private final String id = UUID.randomUUID().toString(); + + public RedisBroadcastService(Config config, ObjectMapper objectMapper) throws IOException { + this.objectMapper = objectMapper; + String url = config.getString(Keys.BROADCAST_ADDRESS); + + try { + subscriber = new Jedis(url); + publisher = new Jedis(url); + subscriber.connect(); + } catch (JedisConnectionException e) { + throw new IOException(e); + } + } + + @Override + public boolean singleInstance() { + return false; + } + + @Override + protected void sendMessage(BroadcastMessage message) { + try { + String payload = id + ":" + objectMapper.writeValueAsString(message); + publisher.publish(channel, payload); + } catch (IOException | JedisConnectionException e) { + LOGGER.warn("Broadcast failed", e); + } + } + + @Override + public void start() throws IOException { + service.submit(receiver); + } + + @Override + public void stop() { + try { + if (subscriber != null) { + subscriber.close(); + subscriber = null; + } + } catch (JedisException e) { + LOGGER.warn("Subscriber close failed", e); + } + try { + if (publisher != null) { + publisher.close(); + publisher = null; + } + } catch (JedisException e) { + LOGGER.warn("Publisher close failed", e); + } + service.shutdown(); + } + + private final Runnable receiver = new Runnable() { + @Override + public void run() { + try { + subscriber.subscribe(new JedisPubSub() { + @Override + public void onMessage(String messageChannel, String message) { + try { + String[] parts = message.split(":", 2); + if (messageChannel.equals(channel) && parts.length == 2 && !id.equals(parts[0])) { + handleMessage(objectMapper.readValue(parts[1], BroadcastMessage.class)); + } + } catch (Exception e) { + LOGGER.warn("Broadcast handleMessage failed", e); + } + } + }, channel); + } catch (JedisException e) { + throw new RuntimeException(e); + } + } + }; + +} diff --git a/src/main/java/org/traccar/config/Config.java b/src/main/java/org/traccar/config/Config.java index c73be6475..47e1f0707 100644 --- a/src/main/java/org/traccar/config/Config.java +++ b/src/main/java/org/traccar/config/Config.java @@ -19,8 +19,8 @@ import com.google.common.annotations.VisibleForTesting; import com.google.inject.name.Named; import org.traccar.helper.Log; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 093f4298c..02e684875 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -234,11 +234,18 @@ public final class Keys { List.of(KeyType.CONFIG, KeyType.DEVICE)); /** + * Disable commands for the protocol. Not all protocols support this option. + */ + public static final ConfigSuffix<Boolean> PROTOCOL_DISABLE_COMMANDS = new BooleanConfigSuffix( + ".disableCommands", + List.of(KeyType.CONFIG)); + + /** * Protocol format. Used by protocols that have configurable message format. */ public static final ConfigSuffix<String> PROTOCOL_FORMAT = new StringConfigSuffix( ".format", - List.of(KeyType.DEVICE)); + List.of(KeyType.CONFIG, KeyType.DEVICE)); /** * Protocol date format. Used by protocols that have configurable date format. @@ -293,6 +300,13 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Send device responses immediately before writing it in the database. + */ + public static final ConfigKey<Boolean> SERVER_INSTANT_ACKNOWLEDGEMENT = new BooleanConfigKey( + "server.instantAcknowledgement", + List.of(KeyType.CONFIG)); + + /** * Address for uploading aggregated anonymous usage statistics. Uploaded information is the same you can see on the * statistics screen in the web app. It does not include any sensitive (e.g. locations). */ @@ -327,6 +341,22 @@ public final class Keys { 0.0); /** + * Disable device sharing on the server. + */ + public static final ConfigKey<Boolean> DEVICE_SHARE_DISABLE = new BooleanConfigKey( + "disableShare", + List.of(KeyType.SERVER)); + + /** + * Speed limit threshold multiplier. For example, if the speed limit is 100, but we only want to generate an event + * if the speed is higher than 105, this parameter can be set to 1.05. Default multiplier is 1.0. + */ + public static final ConfigKey<Double> EVENT_OVERSPEED_THRESHOLD_MULTIPLIER = new DoubleConfigKey( + "event.overspeed.thresholdMultiplier", + List.of(KeyType.CONFIG), + 1.0); + + /** * Minimal over speed duration to trigger the event. Value in seconds. */ public static final ConfigKey<Long> EVENT_OVERSPEED_MINIMAL_DURATION = new LongConfigKey( @@ -366,14 +396,15 @@ public final class Keys { */ public static final ConfigKey<Boolean> EVENT_MOTION_PROCESS_INVALID_POSITIONS = new BooleanConfigKey( "event.motion.processInvalidPositions", - List.of(KeyType.CONFIG)); + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); /** * If the speed is above specified value, the object is considered to be in motion. Default value is 0.01 knots. */ public static final ConfigKey<Double> EVENT_MOTION_SPEED_THRESHOLD = new DoubleConfigKey( "event.motion.speedThreshold", - List.of(KeyType.CONFIG), + List.of(KeyType.CONFIG, KeyType.DEVICE), 0.01); /** @@ -386,6 +417,13 @@ public final class Keys { 25.0); /** + * Enable in-memory database instead of an SQL database. + */ + public static final ConfigKey<Boolean> DATABASE_MEMORY = new BooleanConfigKey( + "database.memory", + List.of(KeyType.CONFIG)); + + /** * Path to the database driver JAR file. Traccar includes drivers for MySQL, PostgreSQL and H2 databases. If you use * one of those, you don't need to specify this parameter. */ @@ -459,14 +497,6 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * By default, server syncs with the database if it encounters and unknown device. This flag allows to disable that - * behavior to improve performance in some cases. - */ - public static final ConfigKey<Boolean> DATABASE_IGNORE_UNKNOWN = new BooleanConfigKey( - "database.ignoreUnknown", - List.of(KeyType.CONFIG)); - - /** * Automatically register unknown devices in the database. */ public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new BooleanConfigKey( @@ -488,6 +518,13 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Automatically register unknown devices with regex filter. + */ + public static final ConfigKey<String> DATABASE_REGISTER_UNKNOWN_REGEX = new StringConfigKey( + "database.registerUnknown.regex", + List.of(KeyType.CONFIG), "\\w{3,15}"); + + /** * Store empty messages as positions. For example, heartbeats. */ public static final ConfigKey<Boolean> DATABASE_SAVE_EMPTY = new BooleanConfigKey( @@ -590,6 +627,85 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Force OpenID Connect authentication. When enabled, the Traccar login page will be skipped + * and users are redirected to the OpenID Connect provider. + */ + public static final ConfigKey<Boolean> OPENID_FORCE = new BooleanConfigKey( + "openid.force", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Client ID. + * This is a unique ID assigned to each application you register with your identity provider. + * Required to enable SSO. + */ + public static final ConfigKey<String> OPENID_CLIENT_ID = new StringConfigKey( + "openid.clientId", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Client Secret. + * This is a secret assigned to each application you register with your identity provider. + * Required to enable SSO. + */ + public static final ConfigKey<String> OPENID_CLIENT_SECRET = new StringConfigKey( + "openid.clientSecret", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Issuer (Base) URL. + * This is used to automatically configure the authorization, token and user info URLs if provided. + */ + public static final ConfigKey<String> OPENID_ISSUER_URL = new StringConfigKey( + "openid.issuerUrl", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect Authorization URL. + * This can usually be found in the documentation of your identity provider or by using the well-known + * configuration endpoint, e.g. https://auth.example.com//.well-known/openid-configuration + * Required to enable SSO if openid.issuerUrl is not set. + */ + public static final ConfigKey<String> OPENID_AUTH_URL = new StringConfigKey( + "openid.authUrl", + List.of(KeyType.CONFIG)); + /** + * OpenID Connect Token URL. + * This can be found in the same ways at openid.authUrl. + * Required to enable SSO if openid.issuerUrl is not set. + */ + public static final ConfigKey<String> OPENID_TOKEN_URL = new StringConfigKey( + "openid.tokenUrl", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect User Info URL. + * This can be found in the same ways at openid.authUrl. + * Required to enable SSO if openid.issuerUrl is not set. + */ + public static final ConfigKey<String> OPENID_USERINFO_URL = new StringConfigKey( + "openid.userInfoUrl", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect group to restrict access to. + * If this is not provided, all OpenID users will have access to Traccar. + * This option will only work if your OpenID provider supports the groups scope. + */ + public static final ConfigKey<String> OPENID_ALLOW_GROUP = new StringConfigKey( + "openid.allowGroup", + List.of(KeyType.CONFIG)); + + /** + * OpenID Connect group to grant admin access. + * If this is not provided, no groups will be granted admin access. + * This option will only work if your OpenID provider supports the groups scope. + */ + public static final ConfigKey<String> OPENID_ADMIN_GROUP = new StringConfigKey( + "openid.adminGroup", + List.of(KeyType.CONFIG)); + + /** * If no data is reported by a device for the given amount of time, status changes from online to unknown. Value is * in seconds. Default timeout is 10 minutes. */ @@ -638,6 +754,14 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Maximum API request duration in seconds. + */ + public static final ConfigKey<Integer> WEB_MAX_REQUEST_SECONDS = new IntegerConfigKey( + "web.maxRequestSec", + List.of(KeyType.CONFIG), + 600); + + /** * Sanitize all strings returned via API. This is needed to fix XSS issues in the old web interface. New React-based * interface doesn't require this. */ @@ -653,12 +777,19 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * WebSocket connection timeout in milliseconds. Default timeout is 10 minutes. + * Path to a folder with overrides. It can be used for branding to keep custom logos in a separate place. + */ + public static final ConfigKey<String> WEB_OVERRIDE = new StringConfigKey( + "web.override", + List.of(KeyType.CONFIG)); + + /** + * WebSocket connection timeout in milliseconds. Default timeout is 5 minutes. */ public static final ConfigKey<Long> WEB_TIMEOUT = new LongConfigKey( "web.timeout", List.of(KeyType.CONFIG), - 60000L); + 300000L); /** * Authentication sessions timeout in seconds. By default no timeout. @@ -706,6 +837,27 @@ public final class Keys { "max-age=3600,public"); /** + * Enable TOTP authentication on the server. + */ + public static final ConfigKey<Boolean> WEB_TOTP_ENABLE = new BooleanConfigKey( + "totpEnable", + List.of(KeyType.SERVER)); + + /** + * Server attribute that indicates that TOTP authentication is required for new users. + */ + public static final ConfigKey<Boolean> WEB_TOTP_FORCE = new BooleanConfigKey( + "totpForce", + List.of(KeyType.SERVER)); + + /** + * Host for raw data forwarding. + */ + public static final ConfigKey<String> SERVER_FORWARD = new StringConfigKey( + "server.forward", + List.of(KeyType.CONFIG)); + + /** * Position forwarding format. Available options are "url", "json" and "kafka". Default is "url". */ public static final ConfigKey<String> FORWARD_TYPE = new StringConfigKey( @@ -714,7 +866,15 @@ public final class Keys { "url"); /** - * Position forwarding Kafka topic. + * Position forwarding AMQP exchange. + */ + public static final ConfigKey<String> FORWARD_EXCHANGE = new StringConfigKey( + "forward.exchange", + List.of(KeyType.CONFIG), + "traccar"); + + /** + * Position forwarding Kafka topic or AQMP Routing Key. */ public static final ConfigKey<String> FORWARD_TOPIC = new StringConfigKey( "forward.topic", @@ -783,7 +943,15 @@ public final class Keys { "json"); /** - * Events forwarding Kafka topic. + * Events forwarding AMQP exchange. + */ + public static final ConfigKey<String> EVENT_FORWARD_EXCHANGE = new StringConfigKey( + "event.forward.exchange", + List.of(KeyType.CONFIG), + "traccar"); + + /** + * Events forwarding Kafka topic or AQMP Routing Key. */ public static final ConfigKey<String> EVENT_FORWARD_TOPIC = new StringConfigKey( "event.forward.topic", @@ -822,6 +990,13 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Restrict global SMTP configuration to system messages only (e.g. password reset). + */ + public static final ConfigKey<Boolean> MAIL_SMTP_SYSTEM_ONLY = new BooleanConfigKey( + "mail.smtp.systemOnly", + List.of(KeyType.CONFIG)); + + /** * Force SMTP settings from the config file and ignore user attributes. */ public static final ConfigKey<Boolean> MAIL_SMTP_IGNORE_USER_CONFIG = new BooleanConfigKey( @@ -992,6 +1167,15 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * If the event time is too old, we should not send notifications. This parameter is the threshold value in + * milliseconds. Default value is 15 minutes. + */ + public static final ConfigKey<Long> NOTIFICATOR_TIME_THRESHOLD = new LongConfigKey( + "notificator.timeThreshold", + List.of(KeyType.CONFIG), + 15 * 60 * 1000L); + + /** * Traccar notification API key. */ public static final ConfigKey<String> NOTIFICATOR_TRACCAR_KEY = new StringConfigKey( @@ -1041,19 +1225,56 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Enable user expiration email notification. + */ + public static final ConfigKey<Boolean> NOTIFICATION_EXPIRATION_USER = new BooleanConfigKey( + "notification.expiration.user", + List.of(KeyType.CONFIG)); + + /** + * User expiration reminder. Value in milliseconds. + */ + public static final ConfigKey<Long> NOTIFICATION_EXPIRATION_USER_REMINDER = new LongConfigKey( + "notification.expiration.user.reminder", + List.of(KeyType.CONFIG)); + + /** + * Enable device expiration email notification. + */ + public static final ConfigKey<Boolean> NOTIFICATION_EXPIRATION_DEVICE = new BooleanConfigKey( + "notification.expiration.device", + List.of(KeyType.CONFIG)); + + /** + * Device expiration reminder. Value in milliseconds. + */ + public static final ConfigKey<Long> NOTIFICATION_EXPIRATION_DEVICE_REMINDER = new LongConfigKey( + "notification.expiration.device.reminder", + List.of(KeyType.CONFIG)); + + /** * Maximum time period for reports in seconds. Can be useful to prevent users to request unreasonably long reports. - * By default there is no limit. + * By default, there is no limit. */ public static final ConfigKey<Long> REPORT_PERIOD_LIMIT = new LongConfigKey( "report.periodLimit", List.of(KeyType.CONFIG)); /** + * Time threshold for fast reports. Fast reports are more efficient, but less accurate and missing some information. + * The value is in seconds. One day by default. + */ + public static final ConfigKey<Long> REPORT_FAST_THRESHOLD = new LongConfigKey( + "report.fastThreshold", + List.of(KeyType.CONFIG), + 86400L); + + /** * Trips less than minimal duration and minimal distance are ignored. 300 seconds and 500 meters are default. */ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DISTANCE = new LongConfigKey( "report.trip.minimalTripDistance", - List.of(KeyType.CONFIG), + List.of(KeyType.CONFIG, KeyType.DEVICE), 500L); /** @@ -1061,7 +1282,7 @@ public final class Keys { */ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_TRIP_DURATION = new LongConfigKey( "report.trip.minimalTripDuration", - List.of(KeyType.CONFIG), + List.of(KeyType.CONFIG, KeyType.DEVICE), 300L); /** @@ -1069,7 +1290,7 @@ public final class Keys { */ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_PARKING_DURATION = new LongConfigKey( "report.trip.minimalParkingDuration", - List.of(KeyType.CONFIG), + List.of(KeyType.CONFIG, KeyType.DEVICE), 300L); /** @@ -1077,7 +1298,7 @@ public final class Keys { */ public static final ConfigKey<Long> REPORT_TRIP_MINIMAL_NO_DATA_DURATION = new LongConfigKey( "report.trip.minimalNoDataDuration", - List.of(KeyType.CONFIG), + List.of(KeyType.CONFIG, KeyType.DEVICE), 3600L); /** @@ -1085,7 +1306,8 @@ public final class Keys { */ public static final ConfigKey<Boolean> REPORT_TRIP_USE_IGNITION = new BooleanConfigKey( "report.trip.useIgnition", - List.of(KeyType.CONFIG)); + List.of(KeyType.CONFIG, KeyType.DEVICE), + false); /** * Ignore odometer value reported by the device and use server-calculated total distance instead. This is useful @@ -1093,7 +1315,8 @@ public final class Keys { */ public static final ConfigKey<Boolean> REPORT_IGNORE_ODOMETER = new BooleanConfigKey( "report.ignoreOdometer", - List.of(KeyType.CONFIG)); + List.of(KeyType.CONFIG), + false); /** * Boolean flag to enable or disable position filtering. @@ -1125,6 +1348,14 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Filter messages that do not have GPS location. If they are not filtered, they will include the last known + * location. + */ + public static final ConfigKey<Boolean> FILTER_OUTDATED = new BooleanConfigKey( + "filter.outdated", + List.of(KeyType.CONFIG)); + + /** * Filter records with fix time in the future. The value is specified in seconds. Records that have fix time more * than the specified number of seconds later than current server time would be filtered out. */ @@ -1187,6 +1418,13 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Filter position if the daily limit is exceeded for the device. + */ + public static final ConfigKey<Integer> FILTER_DAILY_LIMIT = new IntegerConfigKey( + "filter.dailyLimit", + List.of(KeyType.CONFIG)); + + /** * If false, the server expects all locations to come sequentially (for each device). Filter checks for duplicates, * distance, speed, or time period only against the location that was last received by server. * If true, the server expects locations to come at random order (since tracking device might go offline). @@ -1294,13 +1532,42 @@ public final class Keys { List.of(KeyType.CONFIG, KeyType.DEVICE)); /** - * Enable computed attributes processing. + * Include device attributes in the computed attribute context. */ public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES = new BooleanConfigKey( "processing.computedAttributes.deviceAttributes", List.of(KeyType.CONFIG)); /** + * Include last position attributes in the computed attribute context. + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_LAST_ATTRIBUTES = new BooleanConfigKey( + "processing.computedAttributes.lastAttributes", + List.of(KeyType.CONFIG)); + + /** + * Enable local variables declaration. + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_LOCAL_VARIABLES = new BooleanConfigKey( + "processing.computedAttributes.localVariables", + List.of(KeyType.CONFIG)); + + /** + * Enable loops processing. + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_LOOPS = new BooleanConfigKey( + "processing.computedAttributes.loops", + List.of(KeyType.CONFIG)); + + /** + * Enable new instances creation. + * When disabled, parsing a script/expression using 'new(...)' will throw a parsing exception; + */ + public static final ConfigKey<Boolean> PROCESSING_COMPUTED_ATTRIBUTES_NEW_INSTANCE_CREATION = new BooleanConfigKey( + "processing.computedAttributes.newInstanceCreation", + List.of(KeyType.CONFIG)); + + /** * Boolean flag to enable or disable reverse geocoder. */ public static final ConfigKey<Boolean> GEOCODER_ENABLE = new BooleanConfigKey( @@ -1323,13 +1590,6 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * App id for use with Here provider. - */ - public static final ConfigKey<String> GEOCODER_ID = new StringConfigKey( - "geocoder.id", - List.of(KeyType.CONFIG)); - - /** * Provider API key. Most providers require API keys. */ public static final ConfigKey<String> GEOCODER_KEY = new StringConfigKey( @@ -1433,6 +1693,13 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Process geolocation only when Wi-Fi information is available. This makes the result more accurate. + */ + public static final ConfigKey<Boolean> GEOLOCATION_REQUIRE_WIFI = new BooleanConfigKey( + "geolocation.requireWifi", + List.of(KeyType.CONFIG)); + + /** * Default MCC value to use if device doesn't report MCC. */ public static final ConfigKey<Integer> GEOLOCATION_MCC = new IntegerConfigKey( @@ -1468,6 +1735,14 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Search radius for speed limit. Value is in meters. Default value is 100. + */ + public static final ConfigKey<Integer> SPEED_LIMIT_ACCURACY = new IntegerConfigKey( + "speedLimit.accuracy", + List.of(KeyType.CONFIG), + 100); + + /** * Override latitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can be * N for North or S for South. */ @@ -1523,7 +1798,7 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * Public URL for the web app. Used for notification and report link. + * Public URL for the web app. Used for notification, report link and OpenID Connect. * If not provided, Traccar will attempt to get a URL from the server IP address, but it might be a local address. */ public static final ConfigKey<String> WEB_URL = new StringConfigKey( @@ -1531,6 +1806,27 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Show logs from unknown devices. + */ + public static final ConfigKey<Boolean> WEB_SHOW_UNKNOWN_DEVICES = new BooleanConfigKey( + "web.showUnknownDevices", + List.of(KeyType.CONFIG)); + + /** + * Enable commands for a shared device. + */ + public static final ConfigKey<Boolean> WEB_SHARE_DEVICE_COMMANDS = new BooleanConfigKey( + "web.shareDevice.commands", + List.of(KeyType.CONFIG)); + + /** + * Enable reports for a shared device. + */ + public static final ConfigKey<Boolean> WEB_SHARE_DEVICE_REPORTS = new BooleanConfigKey( + "web.shareDevice.reports", + List.of(KeyType.CONFIG)); + + /** * Output logging to the standard terminal output instead of a log file. */ public static final ConfigKey<Boolean> LOGGER_CONSOLE = new BooleanConfigKey( @@ -1593,6 +1889,14 @@ public final class Keys { "time,position,speed,course,accuracy,result"); /** + * Broadcast method. Available options are "multicast" and "redis". By default (if the value is not + * specified or does not matches available options) server disables broadcast. + */ + public static final ConfigKey<String> BROADCAST_TYPE = new StringConfigKey( + "broadcast.type", + List.of(KeyType.CONFIG)); + + /** * Multicast interface. It can be either an IP address or an interface name. */ public static final ConfigKey<String> BROADCAST_INTERFACE = new StringConfigKey( @@ -1600,7 +1904,7 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * Multicast address for broadcasting synchronization events. + * Multicast address or Redis URL for broadcasting synchronization events. */ public static final ConfigKey<String> BROADCAST_ADDRESS = new StringConfigKey( "broadcast.address", diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java index df399cd7a..90180b989 100644 --- a/src/main/java/org/traccar/database/CommandsManager.java +++ b/src/main/java/org/traccar/database/CommandsManager.java @@ -22,6 +22,7 @@ import org.traccar.broadcast.BroadcastInterface; import org.traccar.broadcast.BroadcastService; import org.traccar.model.Command; import org.traccar.model.Device; +import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.QueuedCommand; import org.traccar.session.ConnectionManager; @@ -34,10 +35,12 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Collectors; @Singleton @@ -48,20 +51,23 @@ public class CommandsManager implements BroadcastInterface { private final SmsManager smsManager; private final ConnectionManager connectionManager; private final BroadcastService broadcastService; + private final NotificationManager notificationManager; @Inject public CommandsManager( Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager, - ConnectionManager connectionManager, BroadcastService broadcastService) { + ConnectionManager connectionManager, BroadcastService broadcastService, + NotificationManager notificationManager) { this.storage = storage; this.serverManager = serverManager; this.smsManager = smsManager; this.connectionManager = connectionManager; this.broadcastService = broadcastService; + this.notificationManager = notificationManager; broadcastService.registerListener(this); } - public boolean sendCommand(Command command) throws Exception { + public QueuedCommand sendCommand(Command command) throws Exception { long deviceId = command.getDeviceId(); if (command.getTextChannel()) { if (smsManager == null) { @@ -84,12 +90,13 @@ public class CommandsManager implements BroadcastInterface { if (deviceSession != null && deviceSession.supportsLiveCommands()) { deviceSession.sendCommand(command); } else { - storage.addObject(QueuedCommand.fromCommand(command), new Request(new Columns.Exclude("id"))); + QueuedCommand queuedCommand = QueuedCommand.fromCommand(command); + queuedCommand.setId(storage.addObject(queuedCommand, new Request(new Columns.Exclude("id")))); broadcastService.updateCommand(true, deviceId); - return false; + return queuedCommand; } } - return true; + return null; } public Collection<Command> readQueuedCommands(long deviceId) { @@ -102,10 +109,16 @@ public class CommandsManager implements BroadcastInterface { new Columns.All(), new Condition.Equals("deviceId", deviceId), new Order("id", false, count))); + Map<Event, Position> events = new HashMap<>(); for (var command : commands) { storage.removeObject(QueuedCommand.class, new Request( new Condition.Equals("id", command.getId()))); + + Event event = new Event(Event.TYPE_QUEUED_COMMAND_SENT, command.getDeviceId()); + event.set("id", command.getId()); + events.put(event, null); } + notificationManager.updateEvents(events); return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList()); } catch (StorageException e) { throw new RuntimeException(e); diff --git a/src/main/java/org/traccar/database/DeviceLookupService.java b/src/main/java/org/traccar/database/DeviceLookupService.java index 583b2ae35..90d23531e 100644 --- a/src/main/java/org/traccar/database/DeviceLookupService.java +++ b/src/main/java/org/traccar/database/DeviceLookupService.java @@ -29,8 +29,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -49,7 +49,7 @@ public class DeviceLookupService { private final boolean throttlingEnabled; - private static class IdentifierInfo { + private static final class IdentifierInfo { private long lastQuery; private long delay; private Timeout timeout; diff --git a/src/main/java/org/traccar/database/MediaManager.java b/src/main/java/org/traccar/database/MediaManager.java index c1ef810ee..2f2369c96 100644 --- a/src/main/java/org/traccar/database/MediaManager.java +++ b/src/main/java/org/traccar/database/MediaManager.java @@ -21,8 +21,8 @@ import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; diff --git a/src/main/java/org/traccar/database/NotificationManager.java b/src/main/java/org/traccar/database/NotificationManager.java index cb971b082..65437f0a1 100644 --- a/src/main/java/org/traccar/database/NotificationManager.java +++ b/src/main/java/org/traccar/database/NotificationManager.java @@ -23,12 +23,12 @@ import org.traccar.config.Keys; import org.traccar.forward.EventData; import org.traccar.forward.EventForwarder; import org.traccar.geocoder.Geocoder; +import org.traccar.helper.DateUtil; import org.traccar.model.Calendar; import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Maintenance; -import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.notification.MessageException; import org.traccar.notification.NotificatorManager; @@ -38,9 +38,9 @@ import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Request; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Arrays; import java.util.Map; import java.util.Map.Entry; @@ -58,6 +58,7 @@ public class NotificationManager { private final Geocoder geocoder; private final boolean geocodeOnRequest; + private final long timeThreshold; @Inject public NotificationManager( @@ -69,6 +70,7 @@ public class NotificationManager { this.notificatorManager = notificatorManager; this.geocoder = geocoder; geocodeOnRequest = config.getBoolean(Keys.GEOCODER_ON_REQUEST); + timeThreshold = config.getLong(Keys.NOTIFICATOR_TIME_THRESHOLD); } private void updateEvent(Event event, Position position) { @@ -78,7 +80,14 @@ public class NotificationManager { LOGGER.warn("Event save error", error); } - var notifications = cacheManager.getDeviceObjects(event.getDeviceId(), Notification.class).stream() + forwardEvent(event, position); + + if (System.currentTimeMillis() - event.getEventTime().getTime() > timeThreshold) { + LOGGER.info("Skipping notifications for old event"); + return; + } + + var notifications = cacheManager.getDeviceNotifications(event.getDeviceId()).stream() .filter(notification -> notification.getType().equals(event.getType())) .filter(notification -> { if (event.getType().equals(Event.TYPE_ALARM)) { @@ -98,6 +107,14 @@ public class NotificationManager { }) .collect(Collectors.toUnmodifiableList()); + Device device = cacheManager.getObject(Device.class, event.getDeviceId()); + LOGGER.info( + "Event id: {}, time: {}, type: {}, notifications: {}", + device.getUniqueId(), + DateUtil.formatDate(event.getEventTime(), false), + event.getType(), + notifications.size()); + if (!notifications.isEmpty()) { if (position != null && position.getAddress() == null && geocodeOnRequest && geocoder != null) { position.setAddress(geocoder.getAddress(position.getLatitude(), position.getLongitude(), null)); @@ -107,16 +124,14 @@ public class NotificationManager { cacheManager.getNotificationUsers(notification.getId(), event.getDeviceId()).forEach(user -> { for (String notificator : notification.getNotificatorsTypes()) { try { - notificatorManager.getNotificator(notificator).send(user, event, position); - } catch (MessageException | InterruptedException exception) { + notificatorManager.getNotificator(notificator).send(notification, user, event, position); + } catch (MessageException exception) { LOGGER.warn("Notification failed", exception); } } }); }); } - - forwardEvent(event, position); } private void forwardEvent(Event event, Position position) { @@ -146,7 +161,7 @@ public class NotificationManager { try { cacheManager.addDevice(event.getDeviceId()); updateEvent(event, position); - } catch (StorageException e) { + } catch (Exception e) { throw new RuntimeException(e); } finally { cacheManager.removeDevice(event.getDeviceId()); diff --git a/src/main/java/org/traccar/database/OpenIdProvider.java b/src/main/java/org/traccar/database/OpenIdProvider.java new file mode 100644 index 000000000..93297f7ab --- /dev/null +++ b/src/main/java/org/traccar/database/OpenIdProvider.java @@ -0,0 +1,204 @@ +/* + * Copyright 2023 Daniel Raper (me@danr.uk) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.database; + +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.api.resource.SessionResource; +import org.traccar.api.security.LoginService; +import org.traccar.model.User; +import org.traccar.storage.StorageException; +import org.traccar.helper.LogAction; +import org.traccar.helper.WebHelper; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.Map; +import java.io.IOException; +import jakarta.servlet.http.HttpServletRequest; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Inject; + +import com.nimbusds.oauth2.sdk.http.HTTPResponse; +import com.nimbusds.oauth2.sdk.AuthorizationCode; +import com.nimbusds.oauth2.sdk.ResponseType; +import com.nimbusds.oauth2.sdk.Scope; +import com.nimbusds.oauth2.sdk.AuthorizationGrant; +import com.nimbusds.oauth2.sdk.TokenRequest; +import com.nimbusds.oauth2.sdk.TokenResponse; +import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant; +import com.nimbusds.oauth2.sdk.ParseException; +import com.nimbusds.oauth2.sdk.AuthorizationResponse; +import com.nimbusds.oauth2.sdk.auth.Secret; +import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic; +import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; +import com.nimbusds.oauth2.sdk.token.BearerAccessToken; +import com.nimbusds.oauth2.sdk.id.State; +import com.nimbusds.oauth2.sdk.id.ClientID; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponse; +import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser; +import com.nimbusds.openid.connect.sdk.UserInfoResponse; +import com.nimbusds.openid.connect.sdk.UserInfoRequest; +import com.nimbusds.openid.connect.sdk.AuthenticationRequest; +import com.nimbusds.openid.connect.sdk.claims.UserInfo; + +public class OpenIdProvider { + private final Boolean force; + private final ClientID clientId; + private final ClientAuthentication clientAuth; + private final URI callbackUrl; + private final URI authUrl; + private final URI tokenUrl; + private final URI userInfoUrl; + private final URI baseUrl; + private final String adminGroup; + private final String allowGroup; + + private final LoginService loginService; + + @Inject + public OpenIdProvider(Config config, LoginService loginService, HttpClient httpClient, ObjectMapper objectMapper) + throws InterruptedException, IOException, URISyntaxException { + + this.loginService = loginService; + + force = config.getBoolean(Keys.OPENID_FORCE); + clientId = new ClientID(config.getString(Keys.OPENID_CLIENT_ID)); + clientAuth = new ClientSecretBasic(clientId, new Secret(config.getString(Keys.OPENID_CLIENT_SECRET))); + + baseUrl = new URI(WebHelper.retrieveWebUrl(config)); + callbackUrl = new URI(WebHelper.retrieveWebUrl(config) + "/api/session/openid/callback"); + + if (config.hasKey(Keys.OPENID_ISSUER_URL)) { + HttpRequest httpRequest = HttpRequest.newBuilder( + URI.create(config.getString(Keys.OPENID_ISSUER_URL) + "/.well-known/openid-configuration")) + .header("Accept", "application/json") + .build(); + + String httpResponse = httpClient.send(httpRequest, BodyHandlers.ofString()).body(); + + Map<String, Object> discoveryMap = objectMapper.readValue(httpResponse, new TypeReference<>() { + }); + + authUrl = new URI((String) discoveryMap.get("authorization_endpoint")); + tokenUrl = new URI((String) discoveryMap.get("token_endpoint")); + userInfoUrl = new URI((String) discoveryMap.get("userinfo_endpoint")); + } else { + authUrl = new URI(config.getString(Keys.OPENID_AUTH_URL)); + tokenUrl = new URI(config.getString(Keys.OPENID_TOKEN_URL)); + userInfoUrl = new URI(config.getString(Keys.OPENID_USERINFO_URL)); + } + + adminGroup = config.getString(Keys.OPENID_ADMIN_GROUP); + allowGroup = config.getString(Keys.OPENID_ALLOW_GROUP); + } + + public URI createAuthUri() { + Scope scope = new Scope("openid", "profile", "email"); + + if (adminGroup != null) { + scope.add("groups"); + } + + AuthenticationRequest.Builder request = new AuthenticationRequest.Builder( + new ResponseType("code"), + scope, + clientId, + callbackUrl); + + return request.endpointURI(authUrl) + .state(new State()) + .build() + .toURI(); + } + + private OIDCTokenResponse getToken(AuthorizationCode code) + throws IOException, ParseException, GeneralSecurityException { + AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, callbackUrl); + TokenRequest tokenRequest = new TokenRequest(tokenUrl, clientAuth, codeGrant); + + HTTPResponse tokenResponse = tokenRequest.toHTTPRequest().send(); + TokenResponse token = OIDCTokenResponseParser.parse(tokenResponse); + if (!token.indicatesSuccess()) { + throw new GeneralSecurityException("Unable to authenticate with the OpenID Connect provider."); + } + + return (OIDCTokenResponse) token.toSuccessResponse(); + } + + private UserInfo getUserInfo(BearerAccessToken token) throws IOException, ParseException, GeneralSecurityException { + HTTPResponse httpResponse = new UserInfoRequest(userInfoUrl, token) + .toHTTPRequest() + .send(); + + UserInfoResponse userInfoResponse = UserInfoResponse.parse(httpResponse); + + if (!userInfoResponse.indicatesSuccess()) { + throw new GeneralSecurityException( + "Failed to access OpenID Connect user info endpoint. Please contact your administrator."); + } + + return userInfoResponse.toSuccessResponse().getUserInfo(); + } + + public URI handleCallback(URI requestUri, HttpServletRequest request) + throws StorageException, ParseException, IOException, GeneralSecurityException { + + AuthorizationResponse response = AuthorizationResponse.parse(requestUri); + + if (!response.indicatesSuccess()) { + throw new GeneralSecurityException(response.toErrorResponse().getErrorObject().getDescription()); + } + + AuthorizationCode authCode = response.toSuccessResponse().getAuthorizationCode(); + + if (authCode == null) { + throw new GeneralSecurityException("Malformed OpenID callback."); + } + + OIDCTokenResponse tokens = getToken(authCode); + + BearerAccessToken bearerToken = tokens.getOIDCTokens().getBearerAccessToken(); + + UserInfo userInfo = getUserInfo(bearerToken); + + List<String> userGroups = userInfo.getStringListClaim("groups"); + boolean administrator = adminGroup != null && userGroups.contains(adminGroup); + + if (!(administrator || allowGroup == null || userGroups.contains(allowGroup))) { + throw new GeneralSecurityException("Your OpenID Groups do not permit access to Traccar."); + } + + User user = loginService.login( + userInfo.getEmailAddress(), userInfo.getName(), administrator).getUser(); + + request.getSession().setAttribute(SessionResource.USER_ID_KEY, user.getId()); + LogAction.login(user.getId(), WebHelper.retrieveRemoteAddress(request)); + + return baseUrl; + } + + public boolean getForce() { + return force; + } +} diff --git a/src/main/java/org/traccar/database/StatisticsManager.java b/src/main/java/org/traccar/database/StatisticsManager.java index e0995dabc..445e53e7c 100644 --- a/src/main/java/org/traccar/database/StatisticsManager.java +++ b/src/main/java/org/traccar/database/StatisticsManager.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.api.security.ServiceAccountUser; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.DateUtil; @@ -28,11 +29,11 @@ import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.core.Form; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Form; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -57,6 +58,7 @@ public class StatisticsManager { private final Set<Long> users = new HashSet<>(); private final Map<Long, String> deviceProtocols = new HashMap<>(); + private final Map<Long, Integer> deviceMessages = new HashMap<>(); private int requests; private int messagesReceived; @@ -98,8 +100,11 @@ public class StatisticsManager { statistics.setProtocols(protocols); } + statistics.set("modern", config.getString(Keys.WEB_PATH).contains("modern")); + users.clear(); deviceProtocols.clear(); + deviceMessages.clear(); requests = 0; messagesReceived = 0; messagesStored = 0; @@ -138,6 +143,13 @@ public class StatisticsManager { LOGGER.warn("Failed to serialize protocols", e); } } + if (!statistics.getAttributes().isEmpty()) { + try { + form.param("attributes", objectMapper.writeValueAsString(statistics.getAttributes())); + } catch (JsonProcessingException e) { + LOGGER.warn("Failed to serialize attributes", e); + } + } client.target(url).request().async().post(Entity.form(form)); } @@ -147,7 +159,7 @@ public class StatisticsManager { public synchronized void registerRequest(long userId) { checkSplit(); requests += 1; - if (userId != 0) { + if (userId != 0 && userId != ServiceAccountUser.ID) { users.add(userId); } } @@ -162,9 +174,14 @@ public class StatisticsManager { messagesStored += 1; if (deviceId != 0) { deviceProtocols.put(deviceId, protocol); + deviceMessages.merge(deviceId, 1, Integer::sum); } } + public synchronized int messageStoredCount(long deviceId) { + return deviceMessages.getOrDefault(deviceId, 0); + } + public synchronized void registerMail() { checkSplit(); mailSent += 1; diff --git a/src/main/java/org/traccar/forward/AmqpClient.java b/src/main/java/org/traccar/forward/AmqpClient.java new file mode 100644 index 000000000..361cfffee --- /dev/null +++ b/src/main/java/org/traccar/forward/AmqpClient.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.rabbitmq.client.BuiltinExchangeType; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.MessageProperties; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.TimeoutException; + +public class AmqpClient { + private final Channel channel; + private final String exchange; + private final String topic; + + AmqpClient(String connectionUrl, String exchange, String topic) { + this.exchange = exchange; + this.topic = topic; + + ConnectionFactory factory = new ConnectionFactory(); + try { + factory.setUri(connectionUrl); + } catch (NoSuchAlgorithmException | URISyntaxException | KeyManagementException e) { + throw new RuntimeException("Error while setting URI for RabbitMQ connection factory", e); + } + + try { + Connection connection = factory.newConnection(); + channel = connection.createChannel(); + channel.exchangeDeclare(exchange, BuiltinExchangeType.TOPIC, true); + } catch (IOException | TimeoutException e) { + throw new RuntimeException("Error while creating and configuring RabbitMQ channel", e); + } + } + + public void publishMessage(String message) throws IOException { + channel.basicPublish(exchange, topic, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes()); + } +} diff --git a/src/main/java/org/traccar/forward/EventForwarderAmqp.java b/src/main/java/org/traccar/forward/EventForwarderAmqp.java new file mode 100644 index 000000000..5c38a4459 --- /dev/null +++ b/src/main/java/org/traccar/forward/EventForwarderAmqp.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; + +public class EventForwarderAmqp implements EventForwarder { + + private final AmqpClient amqpClient; + private final ObjectMapper objectMapper; + + public EventForwarderAmqp(Config config, ObjectMapper objectMapper) { + String connectionUrl = config.getString(Keys.EVENT_FORWARD_URL); + String exchange = config.getString(Keys.EVENT_FORWARD_EXCHANGE); + String topic = config.getString(Keys.EVENT_FORWARD_TOPIC); + this.objectMapper = objectMapper; + amqpClient = new AmqpClient(connectionUrl, exchange, topic); + } + + @Override + public void forward(EventData eventData, ResultHandler resultHandler) { + try { + String value = objectMapper.writeValueAsString(eventData); + amqpClient.publishMessage(value); + resultHandler.onResult(true, null); + } catch (IOException e) { + resultHandler.onResult(false, e); + } + } +} diff --git a/src/main/java/org/traccar/forward/EventForwarderJson.java b/src/main/java/org/traccar/forward/EventForwarderJson.java index 7527d568a..df53d3d46 100644 --- a/src/main/java/org/traccar/forward/EventForwarderJson.java +++ b/src/main/java/org/traccar/forward/EventForwarderJson.java @@ -18,10 +18,10 @@ package org.traccar.forward; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.Response; public class EventForwarderJson implements EventForwarder { diff --git a/src/main/java/org/traccar/forward/EventForwarderMqtt.java b/src/main/java/org/traccar/forward/EventForwarderMqtt.java index dc95cb4e2..7f4e29384 100644 --- a/src/main/java/org/traccar/forward/EventForwarderMqtt.java +++ b/src/main/java/org/traccar/forward/EventForwarderMqtt.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,6 @@ import com.hivemq.client.mqtt.datatypes.MqttQos; import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient; import com.hivemq.client.mqtt.mqtt5.Mqtt5Client; import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; @@ -32,7 +30,6 @@ import java.util.UUID; public class EventForwarderMqtt implements EventForwarder { - private static final Logger LOGGER = LoggerFactory.getLogger(EventForwarderMqtt.class); private final Mqtt5AsyncClient client; private final ObjectMapper objectMapper; @@ -63,7 +60,7 @@ public class EventForwarderMqtt implements EventForwarder { String host = url.getHost(); int port = url.getPort(); client = Mqtt5Client.builder() - .identifier("traccar-" + UUID.randomUUID().toString()) + .identifier("traccar-" + UUID.randomUUID()) .serverHost(host) .serverPort(port) .simpleAuth(simpleAuth) diff --git a/src/main/java/org/traccar/forward/NetworkForwarder.java b/src/main/java/org/traccar/forward/NetworkForwarder.java new file mode 100644 index 000000000..86c9a77f3 --- /dev/null +++ b/src/main/java/org/traccar/forward/NetworkForwarder.java @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +@Singleton +public class NetworkForwarder { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetworkForwarder.class); + + private final InetAddress destination; + private final DatagramSocket connectionUdp; + private final Map<InetSocketAddress, Socket> connectionsTcp = new HashMap<>(); + + @Inject + public NetworkForwarder(Config config) throws IOException { + destination = InetAddress.getByName(config.getString(Keys.SERVER_FORWARD)); + connectionUdp = new DatagramSocket(); + } + + public void forward(InetSocketAddress source, int port, boolean datagram, byte[] data) { + try { + if (datagram) { + connectionUdp.send(new DatagramPacket(data, data.length, destination, port)); + } else { + Socket connectionTcp = connectionsTcp.get(source); + if (connectionTcp == null || connectionTcp.isClosed()) { + connectionTcp = new Socket(destination, port); + connectionsTcp.put(source, connectionTcp); + } + connectionTcp.getOutputStream().write(data); + } + } catch (IOException e) { + LOGGER.warn("Network forwarding error", e); + } + } + + public void disconnect(InetSocketAddress source) { + Socket connectionTcp = connectionsTcp.remove(source); + if (connectionTcp != null) { + try { + connectionTcp.close(); + } catch (IOException e) { + LOGGER.warn("Connection close error", e); + } + } + } + +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderAmqp.java b/src/main/java/org/traccar/forward/PositionForwarderAmqp.java new file mode 100644 index 000000000..3996bda15 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderAmqp.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import java.io.IOException; + +public class PositionForwarderAmqp implements PositionForwarder { + + private final AmqpClient amqpClient; + private final ObjectMapper objectMapper; + + public PositionForwarderAmqp(Config config, ObjectMapper objectMapper) { + String connectionUrl = config.getString(Keys.FORWARD_URL); + String exchange = config.getString(Keys.FORWARD_EXCHANGE); + String topic = config.getString(Keys.FORWARD_TOPIC); + amqpClient = new AmqpClient(connectionUrl, exchange, topic); + this.objectMapper = objectMapper; + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + try { + String value = objectMapper.writeValueAsString(positionData); + amqpClient.publishMessage(value); + resultHandler.onResult(true, null); + } catch (IOException e) { + resultHandler.onResult(false, e); + } + } +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderJson.java b/src/main/java/org/traccar/forward/PositionForwarderJson.java index 27b96308e..a0ad8ffd0 100644 --- a/src/main/java/org/traccar/forward/PositionForwarderJson.java +++ b/src/main/java/org/traccar/forward/PositionForwarderJson.java @@ -20,12 +20,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; public class PositionForwarderJson implements PositionForwarder { diff --git a/src/main/java/org/traccar/forward/PositionForwarderRedis.java b/src/main/java/org/traccar/forward/PositionForwarderRedis.java new file mode 100644 index 000000000..539d247b6 --- /dev/null +++ b/src/main/java/org/traccar/forward/PositionForwarderRedis.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.forward; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import redis.clients.jedis.Jedis; + +public class PositionForwarderRedis implements PositionForwarder { + + private final String url; + + private final ObjectMapper objectMapper; + + public PositionForwarderRedis(Config config, ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + this.url = config.getString(Keys.FORWARD_URL); + } + + @Override + public void forward(PositionData positionData, ResultHandler resultHandler) { + + try { + String key = "positions." + positionData.getDevice().getUniqueId(); + String value = objectMapper.writeValueAsString(positionData.getPosition()); + try (Jedis jedis = new Jedis(url)) { + jedis.lpush(key, value); + } + resultHandler.onResult(true, null); + } catch (JsonProcessingException e) { + resultHandler.onResult(false, e); + } + } + +} diff --git a/src/main/java/org/traccar/forward/PositionForwarderUrl.java b/src/main/java/org/traccar/forward/PositionForwarderUrl.java index 53cc7ad24..33474d40b 100644 --- a/src/main/java/org/traccar/forward/PositionForwarderUrl.java +++ b/src/main/java/org/traccar/forward/PositionForwarderUrl.java @@ -23,9 +23,9 @@ import org.traccar.helper.Checksum; import org.traccar.model.Device; import org.traccar.model.Position; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.InvocationCallback; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; +import jakarta.ws.rs.core.Response; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/geocoder/BanGeocoder.java b/src/main/java/org/traccar/geocoder/BanGeocoder.java index f878a8bab..e2ff72311 100644 --- a/src/main/java/org/traccar/geocoder/BanGeocoder.java +++ b/src/main/java/org/traccar/geocoder/BanGeocoder.java @@ -20,9 +20,9 @@ package org.traccar.geocoder; * API documentation: https://adresse.data.gouv.fr/api */ -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class BanGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java index 01e33c2ea..bc3b15ce7 100644 --- a/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java +++ b/src/main/java/org/traccar/geocoder/BingMapsGeocoder.java @@ -16,9 +16,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class BingMapsGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java index 384f46b0e..6c8891316 100644 --- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java +++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java @@ -16,8 +16,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class FactualGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java index 4748d6a2c..35a47bb88 100644 --- a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java @@ -15,9 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GeoapifyGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java index 2af95910f..80b00b3cc 100644 --- a/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GeocodeFarmGeocoder.java @@ -15,8 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GeocodeFarmGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java index 96491ece3..e88962e1a 100644 --- a/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GeocodeXyzGeocoder.java @@ -15,8 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GeocodeXyzGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java index 0589eb000..062e795eb 100644 --- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java @@ -15,8 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class GisgraphyGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java index 4d9ec8f36..93f128b46 100644 --- a/src/main/java/org/traccar/geocoder/GoogleGeocoder.java +++ b/src/main/java/org/traccar/geocoder/GoogleGeocoder.java @@ -15,10 +15,10 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.ws.rs.client.Client; public class GoogleGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java index eb639995e..4767eabad 100644 --- a/src/main/java/org/traccar/geocoder/HereGeocoder.java +++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,70 +15,64 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class HereGeocoder extends JsonGeocoder { - private static String formatUrl(String url, String id, String key, String language) { + private static String formatUrl(String url, String key, String language) { if (url == null) { - url = "https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json"; + url = "https://revgeocode.search.hereapi.com/v1/revgeocode"; } - url += "?mode=retrieveAddresses&maxresults=1"; - url += "&prox=%f,%f,0"; - url += "&app_id=" + id; - url += "&app_code=" + key; + url += "?types=address&limit=1"; + url += "&at=%f,%f"; url += "&apiKey=" + key; if (language != null) { - url += "&language=" + language; + url += "&lang=" + language; } return url; } public HereGeocoder( - Client client, String url, String id, String key, String language, + Client client, String url, String key, String language, int cacheSize, AddressFormat addressFormat) { - super(client, formatUrl(url, id, key, language), cacheSize, addressFormat); + super(client, formatUrl(url, key, language), cacheSize, addressFormat); } @Override public Address parseAddress(JsonObject json) { JsonObject result = json - .getJsonObject("Response") - .getJsonArray("View") + .getJsonArray("items") .getJsonObject(0) - .getJsonArray("Result") - .getJsonObject(0) - .getJsonObject("Location") - .getJsonObject("Address"); + .getJsonObject("address"); if (result != null) { Address address = new Address(); - if (result.containsKey("Label")) { - address.setFormattedAddress(result.getString("Label")); + if (result.containsKey("label")) { + address.setFormattedAddress(result.getString("label")); } - if (result.containsKey("HouseNumber")) { - address.setHouse(result.getString("HouseNumber")); + if (result.containsKey("houseNumber")) { + address.setHouse(result.getString("houseNumber")); } - if (result.containsKey("Street")) { - address.setStreet(result.getString("Street")); + if (result.containsKey("street")) { + address.setStreet(result.getString("street")); } - if (result.containsKey("City")) { - address.setSettlement(result.getString("City")); + if (result.containsKey("city")) { + address.setSettlement(result.getString("city")); } - if (result.containsKey("District")) { - address.setDistrict(result.getString("District")); + if (result.containsKey("district")) { + address.setDistrict(result.getString("district")); } - if (result.containsKey("State")) { - address.setState(result.getString("State")); + if (result.containsKey("state")) { + address.setState(result.getString("state")); } - if (result.containsKey("Country")) { - address.setCountry(result.getString("Country").toUpperCase()); + if (result.containsKey("countryCode")) { + address.setCountry(result.getString("countryCode").toUpperCase()); } - if (result.containsKey("PostalCode")) { - address.setPostcode(result.getString("PostalCode")); + if (result.containsKey("postalCode")) { + address.setPostcode(result.getString("postalCode")); } return address; diff --git a/src/main/java/org/traccar/geocoder/JsonGeocoder.java b/src/main/java/org/traccar/geocoder/JsonGeocoder.java index 6105e8cfd..f9b039f43 100644 --- a/src/main/java/org/traccar/geocoder/JsonGeocoder.java +++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java @@ -19,10 +19,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.database.StatisticsManager; -import javax.json.JsonObject; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; import java.util.AbstractMap; import java.util.Collections; import java.util.LinkedHashMap; diff --git a/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java index f2ffe02d6..f304ffeff 100644 --- a/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java +++ b/src/main/java/org/traccar/geocoder/LocationIqGeocoder.java @@ -15,7 +15,7 @@ */ package org.traccar.geocoder; -import javax.ws.rs.client.Client; +import jakarta.ws.rs.client.Client; public class LocationIqGeocoder extends NominatimGeocoder { diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java index 3f2554c6e..1b6c8adcc 100644 --- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java @@ -16,9 +16,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class MapQuestGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java index 203f5f99b..24c9da2ad 100644 --- a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java @@ -15,9 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class MapTilerGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java index 72bfb53f5..9fa6b8d88 100644 --- a/src/main/java/org/traccar/geocoder/MapboxGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapboxGeocoder.java @@ -15,10 +15,10 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.ws.rs.client.Client; public class MapboxGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java index dea295cca..b68db07bc 100644 --- a/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java +++ b/src/main/java/org/traccar/geocoder/MapmyIndiaGeocoder.java @@ -15,9 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class MapmyIndiaGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java index b731549f7..1e26d0042 100644 --- a/src/main/java/org/traccar/geocoder/NominatimGeocoder.java +++ b/src/main/java/org/traccar/geocoder/NominatimGeocoder.java @@ -15,8 +15,8 @@ */ package org.traccar.geocoder; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class NominatimGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java index fb61440aa..4607fdc87 100644 --- a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java +++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java @@ -16,9 +16,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class OpenCageGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java index 9778d9eda..4aed27fc5 100644 --- a/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java +++ b/src/main/java/org/traccar/geocoder/PositionStackGeocoder.java @@ -15,9 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class PositionStackGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java index 9bb36efc2..4d452fd43 100644 --- a/src/main/java/org/traccar/geocoder/TomTomGeocoder.java +++ b/src/main/java/org/traccar/geocoder/TomTomGeocoder.java @@ -15,9 +15,9 @@ */ package org.traccar.geocoder; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; public class TomTomGeocoder extends JsonGeocoder { diff --git a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java index 8f0f3b704..9425e9111 100644 --- a/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/GoogleGeolocationProvider.java @@ -15,7 +15,7 @@ */ package org.traccar.geolocation; -import javax.ws.rs.client.Client; +import jakarta.ws.rs.client.Client; public class GoogleGeolocationProvider extends UniversalGeolocationProvider { diff --git a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java index 3b4ba4e1f..7eb22dcca 100644 --- a/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/MozillaGeolocationProvider.java @@ -15,7 +15,7 @@ */ package org.traccar.geolocation; -import javax.ws.rs.client.Client; +import jakarta.ws.rs.client.Client; public class MozillaGeolocationProvider extends UniversalGeolocationProvider { diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java index 82fcf42ab..72a05d10f 100644 --- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java @@ -18,9 +18,9 @@ package org.traccar.geolocation; import org.traccar.model.CellTower; import org.traccar.model.Network; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; public class OpenCellIdGeolocationProvider implements GeolocationProvider { diff --git a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java index 7a3f71ee1..9086d8ce3 100644 --- a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java @@ -17,10 +17,10 @@ package org.traccar.geolocation; import org.traccar.model.Network; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; public class UniversalGeolocationProvider implements GeolocationProvider { diff --git a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java index 14893b6a3..4f1c5617e 100644 --- a/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java +++ b/src/main/java/org/traccar/geolocation/UnwiredGeolocationProvider.java @@ -23,10 +23,10 @@ import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.WifiAccessPoint; -import javax.json.JsonObject; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.InvocationCallback; import java.util.Collection; public class UnwiredGeolocationProvider implements GeolocationProvider { diff --git a/src/main/java/org/traccar/handler/AcknowledgementHandler.java b/src/main/java/org/traccar/handler/AcknowledgementHandler.java new file mode 100644 index 000000000..4c1085998 --- /dev/null +++ b/src/main/java/org/traccar/handler/AcknowledgementHandler.java @@ -0,0 +1,121 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class AcknowledgementHandler extends ChannelOutboundHandlerAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(AcknowledgementHandler.class); + + public interface Event { + } + + public static class EventReceived implements Event { + } + + public static class EventDecoded implements Event { + private final Collection<Object> objects; + + public EventDecoded(Collection<Object> objects) { + this.objects = objects; + } + + public Collection<Object> getObjects() { + return objects; + } + } + + public static class EventHandled implements Event { + private final Object object; + + public EventHandled(Object object) { + this.object = object; + } + + public Object getObject() { + return object; + } + } + + private static final class Entry { + private final Object message; + private final ChannelPromise promise; + + private Entry(Object message, ChannelPromise promise) { + this.message = message; + this.promise = promise; + } + + public Object getMessage() { + return message; + } + + public ChannelPromise getPromise() { + return promise; + } + } + + private List<Entry> queue; + private final Set<Object> waiting = new HashSet<>(); + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + List<Entry> output = new LinkedList<>(); + synchronized (this) { + if (msg instanceof Event) { + if (msg instanceof EventReceived) { + LOGGER.debug("Event received"); + if (queue == null) { + queue = new LinkedList<>(); + } + } else if (msg instanceof EventDecoded) { + EventDecoded event = (EventDecoded) msg; + LOGGER.debug("Event decoded {}", event.getObjects().size()); + waiting.addAll(event.getObjects()); + } else if (msg instanceof EventHandled) { + EventHandled event = (EventHandled) msg; + LOGGER.debug("Event handled"); + waiting.remove(event.getObject()); + } + if (!(msg instanceof EventReceived) && waiting.isEmpty()) { + output.addAll(queue); + queue = null; + } + } else if (queue != null) { + LOGGER.debug("Message queued"); + queue.add(new Entry(msg, promise)); + } else { + LOGGER.debug("Message sent"); + output.add(new Entry(msg, promise)); + } + } + for (Entry entry : output) { + ctx.write(entry.getMessage(), entry.getPromise()); + } + } + +} diff --git a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java index 620852502..8b010ceae 100644 --- a/src/main/java/org/traccar/handler/ComputedAttributesHandler.java +++ b/src/main/java/org/traccar/handler/ComputedAttributesHandler.java @@ -24,11 +24,15 @@ import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.List; import io.netty.channel.ChannelHandler; -import org.apache.commons.jexl2.JexlEngine; -import org.apache.commons.jexl2.JexlException; -import org.apache.commons.jexl2.MapContext; +import org.apache.commons.jexl3.JexlFeatures; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.introspection.JexlSandbox; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.MapContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.BaseDataHandler; @@ -39,8 +43,8 @@ import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable @@ -52,15 +56,33 @@ public class ComputedAttributesHandler extends BaseDataHandler { private final JexlEngine engine; + private final JexlFeatures features; + private final boolean includeDeviceAttributes; + private final boolean includeLastAttributes; @Inject public ComputedAttributesHandler(Config config, CacheManager cacheManager) { this.cacheManager = cacheManager; - engine = new JexlEngine(); - engine.setStrict(true); - engine.setFunctions(Collections.singletonMap("math", Math.class)); + JexlSandbox sandbox = new JexlSandbox(false); + sandbox.allow("com.safe.Functions"); + sandbox.allow(Math.class.getName()); + List.of( + Double.class, Float.class, Integer.class, Long.class, Short.class, + Character.class, Boolean.class, String.class, Byte.class) + .forEach((type) -> sandbox.allow(type.getName())); + features = new JexlFeatures() + .localVar(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOCAL_VARIABLES)) + .loops(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LOOPS)) + .newInstance(config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_NEW_INSTANCE_CREATION)) + .structuredLiteral(true); + engine = new JexlBuilder() + .strict(true) + .namespaces(Collections.singletonMap("math", Math.class)) + .sandbox(sandbox) + .create(); includeDeviceAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_DEVICE_ATTRIBUTES); + includeLastAttributes = config.getBoolean(Keys.PROCESSING_COMPUTED_ATTRIBUTES_LAST_ATTRIBUTES); } private MapContext prepareContext(Position position) { @@ -68,13 +90,17 @@ public class ComputedAttributesHandler extends BaseDataHandler { if (includeDeviceAttributes) { Device device = cacheManager.getObject(Device.class, position.getDeviceId()); if (device != null) { - for (Object key : device.getAttributes().keySet()) { - result.set((String) key, device.getAttributes().get(key)); + for (String key : device.getAttributes().keySet()) { + result.set(key, device.getAttributes().get(key)); } } } + Position last = null; + if (includeLastAttributes) { + last = cacheManager.getPosition(position.getDeviceId()); + } Set<Method> methods = new HashSet<>(Arrays.asList(position.getClass().getMethods())); - methods.removeAll(Arrays.asList(Object.class.getMethods())); + Arrays.asList(Object.class.getMethods()).forEach(methods::remove); for (Method method : methods) { if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { String name = Character.toLowerCase(method.getName().charAt(3)) + method.getName().substring(4); @@ -82,9 +108,17 @@ public class ComputedAttributesHandler extends BaseDataHandler { try { if (!method.getReturnType().equals(Map.class)) { result.set(name, method.invoke(position)); + if (last != null) { + result.set(prefixAttribute("last", name), method.invoke(last)); + } } else { - for (Object key : ((Map) method.invoke(position)).keySet()) { - result.set((String) key, ((Map) method.invoke(position)).get(key)); + for (Map.Entry<?, ?> entry : ((Map<?, ?>) method.invoke(position)).entrySet()) { + result.set((String) entry.getKey(), entry.getValue()); + } + if (last != null) { + for (Map.Entry<?, ?> entry : ((Map<?, ?>) method.invoke(last)).entrySet()) { + result.set(prefixAttribute("last", (String) entry.getKey()), entry.getValue()); + } } } } catch (IllegalAccessException | InvocationTargetException error) { @@ -95,12 +129,18 @@ public class ComputedAttributesHandler extends BaseDataHandler { return result; } + private String prefixAttribute(String prefix, String key) { + return prefix + Character.toUpperCase(key.charAt(0)) + key.substring(1); + } + /** * @deprecated logic needs to be extracted to be used in API resource */ @Deprecated public Object computeAttribute(Attribute attribute, Position position) throws JexlException { - return engine.createExpression(attribute.getExpression()).evaluate(prepareContext(position)); + return engine + .createScript(features, engine.createInfo(), attribute.getExpression()) + .execute(prepareContext(position)); } @Override diff --git a/src/main/java/org/traccar/handler/CopyAttributesHandler.java b/src/main/java/org/traccar/handler/CopyAttributesHandler.java index e5c9bc29a..42b438e41 100644 --- a/src/main/java/org/traccar/handler/CopyAttributesHandler.java +++ b/src/main/java/org/traccar/handler/CopyAttributesHandler.java @@ -24,8 +24,8 @@ import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/DefaultDataHandler.java b/src/main/java/org/traccar/handler/DefaultDataHandler.java index 89255a5fe..cca6dcd0a 100644 --- a/src/main/java/org/traccar/handler/DefaultDataHandler.java +++ b/src/main/java/org/traccar/handler/DefaultDataHandler.java @@ -24,8 +24,8 @@ import org.traccar.storage.Storage; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java index 30dc9ff2b..db8c73779 100644 --- a/src/main/java/org/traccar/handler/DistanceHandler.java +++ b/src/main/java/org/traccar/handler/DistanceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2015 Amila Silva * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +17,8 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.traccar.BaseDataHandler; import org.traccar.config.Config; import org.traccar.config.Keys; @@ -24,11 +26,6 @@ import org.traccar.helper.DistanceCalculator; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; -import java.math.BigDecimal; -import java.math.RoundingMode; - @Singleton @ChannelHandler.Sharable public class DistanceHandler extends BaseDataHandler { @@ -36,15 +33,15 @@ public class DistanceHandler extends BaseDataHandler { private final CacheManager cacheManager; private final boolean filter; - private final int coordinatesMinError; - private final int coordinatesMaxError; + private final int minError; + private final int maxError; @Inject public DistanceHandler(Config config, CacheManager cacheManager) { this.cacheManager = cacheManager; this.filter = config.getBoolean(Keys.COORDINATES_FILTER); - this.coordinatesMinError = config.getInteger(Keys.COORDINATES_MIN_ERROR); - this.coordinatesMaxError = config.getInteger(Keys.COORDINATES_MAX_ERROR); + this.minError = config.getInteger(Keys.COORDINATES_MIN_ERROR); + this.maxError = config.getInteger(Keys.COORDINATES_MAX_ERROR); } @Override @@ -54,8 +51,7 @@ public class DistanceHandler extends BaseDataHandler { if (position.hasAttribute(Position.KEY_DISTANCE)) { distance = position.getDouble(Position.KEY_DISTANCE); } - double totalDistance = 0.0; - + double totalDistance; Position last = cacheManager.getPosition(position.getDeviceId()); if (last != null) { totalDistance = last.getDouble(Position.KEY_TOTAL_DISTANCE); @@ -63,11 +59,10 @@ public class DistanceHandler extends BaseDataHandler { distance = DistanceCalculator.distance( position.getLatitude(), position.getLongitude(), last.getLatitude(), last.getLongitude()); - distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); } if (filter && last.getLatitude() != 0 && last.getLongitude() != 0) { - boolean satisfiesMin = coordinatesMinError == 0 || distance > coordinatesMinError; - boolean satisfiesMax = coordinatesMaxError == 0 || distance < coordinatesMaxError; + boolean satisfiesMin = minError == 0 || distance > minError; + boolean satisfiesMax = maxError == 0 || distance < maxError || position.getValid(); if (!satisfiesMin || !satisfiesMax) { position.setValid(last.getValid()); position.setLatitude(last.getLatitude()); @@ -75,10 +70,11 @@ public class DistanceHandler extends BaseDataHandler { distance = 0; } } + } else { + totalDistance = 0.0; } position.set(Position.KEY_DISTANCE, distance); - totalDistance = BigDecimal.valueOf(totalDistance + distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); - position.set(Position.KEY_TOTAL_DISTANCE, totalDistance); + position.set(Position.KEY_TOTAL_DISTANCE, totalDistance + distance); return position; } diff --git a/src/main/java/org/traccar/handler/EngineHoursHandler.java b/src/main/java/org/traccar/handler/EngineHoursHandler.java index c10fe9064..621205b34 100644 --- a/src/main/java/org/traccar/handler/EngineHoursHandler.java +++ b/src/main/java/org/traccar/handler/EngineHoursHandler.java @@ -21,8 +21,8 @@ import org.traccar.BaseDataHandler; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java index 994276bb6..a15d3ffad 100644 --- a/src/main/java/org/traccar/handler/FilterHandler.java +++ b/src/main/java/org/traccar/handler/FilterHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,16 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.traccar.BaseDataHandler; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.database.StatisticsManager; import org.traccar.helper.UnitsConverter; import org.traccar.helper.model.AttributeUtil; +import org.traccar.model.Calendar; import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; @@ -33,13 +36,13 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Date; @Singleton @ChannelHandler.Sharable -public class FilterHandler extends BaseDataHandler { +public class FilterHandler extends ChannelInboundHandlerAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(FilterHandler.class); @@ -47,6 +50,7 @@ public class FilterHandler extends BaseDataHandler { private final boolean filterInvalid; private final boolean filterZero; private final boolean filterDuplicate; + private final boolean filterOutdated; private final long filterFuture; private final long filterPast; private final boolean filterApproximate; @@ -55,19 +59,23 @@ public class FilterHandler extends BaseDataHandler { private final int filterDistance; private final int filterMaxSpeed; private final long filterMinPeriod; + private final int filterDailyLimit; private final boolean filterRelative; private final long skipLimit; private final boolean skipAttributes; private final CacheManager cacheManager; private final Storage storage; + private final StatisticsManager statisticsManager; @Inject - public FilterHandler(Config config, CacheManager cacheManager, Storage storage) { + public FilterHandler( + Config config, CacheManager cacheManager, Storage storage, StatisticsManager statisticsManager) { enabled = config.getBoolean(Keys.FILTER_ENABLE); filterInvalid = config.getBoolean(Keys.FILTER_INVALID); filterZero = config.getBoolean(Keys.FILTER_ZERO); filterDuplicate = config.getBoolean(Keys.FILTER_DUPLICATE); + filterOutdated = config.getBoolean(Keys.FILTER_OUTDATED); filterFuture = config.getLong(Keys.FILTER_FUTURE) * 1000; filterPast = config.getLong(Keys.FILTER_PAST) * 1000; filterAccuracy = config.getInteger(Keys.FILTER_ACCURACY); @@ -76,11 +84,13 @@ public class FilterHandler extends BaseDataHandler { filterDistance = config.getInteger(Keys.FILTER_DISTANCE); filterMaxSpeed = config.getInteger(Keys.FILTER_MAX_SPEED); filterMinPeriod = config.getInteger(Keys.FILTER_MIN_PERIOD) * 1000L; + filterDailyLimit = config.getInteger(Keys.FILTER_DAILY_LIMIT); filterRelative = config.getBoolean(Keys.FILTER_RELATIVE); skipLimit = config.getLong(Keys.FILTER_SKIP_LIMIT) * 1000; skipAttributes = config.getBoolean(Keys.FILTER_SKIP_ATTRIBUTES_ENABLE); this.cacheManager = cacheManager; this.storage = storage; + this.statisticsManager = statisticsManager; } private Position getPrecedingPosition(long deviceId, Date date) throws StorageException { @@ -114,6 +124,10 @@ public class FilterHandler extends BaseDataHandler { return false; } + private boolean filterOutdated(Position position) { + return filterOutdated && position.getOutdated(); + } + private boolean filterFuture(Position position) { return filterFuture != 0 && position.getFixTime().getTime() > System.currentTimeMillis() + filterFuture; } @@ -158,6 +172,13 @@ public class FilterHandler extends BaseDataHandler { return false; } + private boolean filterDailyLimit(Position position) { + if (filterDailyLimit != 0) { + return statisticsManager.messageStoredCount(position.getDeviceId()) >= filterDailyLimit; + } + return false; + } + private boolean skipLimit(Position position, Position last) { if (skipLimit != 0 && last != null) { return (position.getServerTime().getTime() - last.getServerTime().getTime()) > skipLimit; @@ -177,7 +198,7 @@ public class FilterHandler extends BaseDataHandler { return false; } - private boolean filter(Position position) { + protected boolean filter(Position position) { StringBuilder filterType = new StringBuilder(); @@ -188,6 +209,9 @@ public class FilterHandler extends BaseDataHandler { if (filterZero(position)) { filterType.append("Zero "); } + if (filterOutdated(position)) { + filterType.append("Outdated "); + } if (filterFuture(position)) { filterType.append("Future "); } @@ -200,6 +224,9 @@ public class FilterHandler extends BaseDataHandler { if (filterApproximate(position)) { filterType.append("Approximate "); } + if (filterDailyLimit(position)) { + filterType.append("DailyLimit "); + } // filter out excessive data long deviceId = position.getDeviceId(); @@ -233,9 +260,16 @@ public class FilterHandler extends BaseDataHandler { } } + Device device = cacheManager.getObject(Device.class, deviceId); + if (device.getCalendarId() > 0) { + Calendar calendar = cacheManager.getObject(Calendar.class, device.getCalendarId()); + if (!calendar.checkMoment(position.getFixTime())) { + filterType.append("Calendar "); + } + } + if (filterType.length() > 0) { - String uniqueId = cacheManager.getObject(Device.class, deviceId).getUniqueId(); - LOGGER.info("Position filtered by {}filters from device: {}", filterType, uniqueId); + LOGGER.info("Position filtered by {}filters from device: {}", filterType, device.getUniqueId()); return true; } @@ -243,11 +277,17 @@ public class FilterHandler extends BaseDataHandler { } @Override - protected Position handlePosition(Position position) { - if (enabled && filter(position)) { - return null; + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg instanceof Position) { + Position position = (Position) msg; + if (enabled && filter(position)) { + ctx.writeAndFlush(new AcknowledgementHandler.EventHandled(position)); + } else { + ctx.fireChannelRead(position); + } + } else { + super.channelRead(ctx, msg); } - return position; } } diff --git a/src/main/java/org/traccar/handler/GeofenceHandler.java b/src/main/java/org/traccar/handler/GeofenceHandler.java new file mode 100644 index 000000000..68bc6dbf0 --- /dev/null +++ b/src/main/java/org/traccar/handler/GeofenceHandler.java @@ -0,0 +1,52 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.handler; + +import io.netty.channel.ChannelHandler; +import org.traccar.BaseDataHandler; +import org.traccar.config.Config; +import org.traccar.helper.model.GeofenceUtil; +import org.traccar.model.Position; +import org.traccar.session.cache.CacheManager; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import java.util.List; + +@Singleton +@ChannelHandler.Sharable +public class GeofenceHandler extends BaseDataHandler { + + private final Config config; + private final CacheManager cacheManager; + + @Inject + public GeofenceHandler(Config config, CacheManager cacheManager) { + this.config = config; + this.cacheManager = cacheManager; + } + + @Override + protected Position handlePosition(Position position) { + + List<Long> geofenceIds = GeofenceUtil.getCurrentGeofences(config, cacheManager, position); + if (!geofenceIds.isEmpty()) { + position.setGeofenceIds(geofenceIds); + } + return position; + } + +} diff --git a/src/main/java/org/traccar/handler/GeolocationHandler.java b/src/main/java/org/traccar/handler/GeolocationHandler.java index e7389f22d..a54ea03e3 100644 --- a/src/main/java/org/traccar/handler/GeolocationHandler.java +++ b/src/main/java/org/traccar/handler/GeolocationHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { private final StatisticsManager statisticsManager; private final boolean processInvalidPositions; private final boolean reuse; + private final boolean requireWifi; public GeolocationHandler( Config config, GeolocationProvider geolocationProvider, CacheManager cacheManager, @@ -46,6 +47,7 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { this.statisticsManager = statisticsManager; processInvalidPositions = config.getBoolean(Keys.GEOLOCATION_PROCESS_INVALID_POSITIONS); reuse = config.getBoolean(Keys.GEOLOCATION_REUSE); + requireWifi = config.getBoolean(Keys.GEOLOCATION_REQUIRE_WIFI); } @Override @@ -53,7 +55,8 @@ public class GeolocationHandler extends ChannelInboundHandlerAdapter { if (message instanceof Position) { final Position position = (Position) message; if ((position.getOutdated() || processInvalidPositions && !position.getValid()) - && position.getNetwork() != null) { + && position.getNetwork() != null + && (!requireWifi || position.getNetwork().getWifiAccessPoints() != null)) { if (reuse) { Position lastPosition = cacheManager.getPosition(position.getDeviceId()); if (lastPosition != null && position.getNetwork().equals(lastPosition.getNetwork())) { diff --git a/src/main/java/org/traccar/handler/HemisphereHandler.java b/src/main/java/org/traccar/handler/HemisphereHandler.java index ccbde9fe5..294e449db 100644 --- a/src/main/java/org/traccar/handler/HemisphereHandler.java +++ b/src/main/java/org/traccar/handler/HemisphereHandler.java @@ -21,8 +21,8 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/MotionHandler.java b/src/main/java/org/traccar/handler/MotionHandler.java index 10312f9b3..68a31a16a 100644 --- a/src/main/java/org/traccar/handler/MotionHandler.java +++ b/src/main/java/org/traccar/handler/MotionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,27 +18,31 @@ package org.traccar.handler; import io.netty.channel.ChannelHandler; import org.traccar.BaseDataHandler; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Position; -import org.traccar.reports.common.TripsConfig; +import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable public class MotionHandler extends BaseDataHandler { - private final double speedThreshold; + private final CacheManager cacheManager; @Inject - public MotionHandler(TripsConfig tripsConfig) { - speedThreshold = tripsConfig.getSpeedThreshold(); + public MotionHandler(CacheManager cacheManager) { + this.cacheManager = cacheManager; } @Override protected Position handlePosition(Position position) { if (!position.hasAttribute(Position.KEY_MOTION)) { - position.set(Position.KEY_MOTION, position.getSpeed() > speedThreshold); + double threshold = AttributeUtil.lookup( + cacheManager, Keys.EVENT_MOTION_SPEED_THRESHOLD, position.getDeviceId()); + position.set(Position.KEY_MOTION, position.getSpeed() > threshold); } return position; } diff --git a/src/main/java/org/traccar/handler/NetworkForwarderHandler.java b/src/main/java/org/traccar/handler/NetworkForwarderHandler.java new file mode 100644 index 000000000..470e175ca --- /dev/null +++ b/src/main/java/org/traccar/handler/NetworkForwarderHandler.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.socket.DatagramChannel; +import io.netty.channel.socket.DatagramPacket; +import org.traccar.forward.NetworkForwarder; + +import jakarta.inject.Inject; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +public class NetworkForwarderHandler extends ChannelInboundHandlerAdapter { + + private final int port; + + private NetworkForwarder networkForwarder; + + public NetworkForwarderHandler(int port) { + this.port = port; + } + + @Inject + public void setNetworkForwarder(NetworkForwarder networkForwarder) { + this.networkForwarder = networkForwarder; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + boolean datagram = ctx.channel() instanceof DatagramChannel; + SocketAddress remoteAddress; + ByteBuf buffer; + if (datagram) { + DatagramPacket message = (DatagramPacket) msg; + remoteAddress = message.recipient(); + buffer = message.content(); + } else { + remoteAddress = ctx.channel().remoteAddress(); + buffer = (ByteBuf) msg; + } + + byte[] data = new byte[buffer.readableBytes()]; + buffer.getBytes(buffer.readerIndex(), data); + networkForwarder.forward((InetSocketAddress) remoteAddress, port, datagram, data); + super.channelRead(ctx, msg); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + if (!(ctx.channel() instanceof DatagramChannel)) { + networkForwarder.disconnect((InetSocketAddress) ctx.channel().remoteAddress()); + } + super.channelInactive(ctx); + } + +} diff --git a/src/main/java/org/traccar/handler/RemoteAddressHandler.java b/src/main/java/org/traccar/handler/RemoteAddressHandler.java index e18d34ef2..61ada5b91 100644 --- a/src/main/java/org/traccar/handler/RemoteAddressHandler.java +++ b/src/main/java/org/traccar/handler/RemoteAddressHandler.java @@ -22,8 +22,8 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.net.InetSocketAddress; @Singleton diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java index 0c6025999..6edb6e912 100644 --- a/src/main/java/org/traccar/handler/SpeedLimitHandler.java +++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java @@ -23,8 +23,8 @@ import org.slf4j.LoggerFactory; import org.traccar.model.Position; import org.traccar.speedlimit.SpeedLimitProvider; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/StandardLoggingHandler.java b/src/main/java/org/traccar/handler/StandardLoggingHandler.java index 84492e2a5..5978d632e 100644 --- a/src/main/java/org/traccar/handler/StandardLoggingHandler.java +++ b/src/main/java/org/traccar/handler/StandardLoggingHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,68 +20,73 @@ import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.NetworkMessage; import org.traccar.helper.NetworkUtil; +import org.traccar.model.LogRecord; +import org.traccar.session.ConnectionManager; import java.net.InetSocketAddress; -import java.net.SocketAddress; public class StandardLoggingHandler extends ChannelDuplexHandler { private static final Logger LOGGER = LoggerFactory.getLogger(StandardLoggingHandler.class); private final String protocol; + private ConnectionManager connectionManager; public StandardLoggingHandler(String protocol) { this.protocol = protocol; } + @Inject + public void setConnectionManager(ConnectionManager connectionManager) { + this.connectionManager = connectionManager; + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - log(ctx, false, msg); + LogRecord record = createLogRecord(msg); + log(ctx, false, record); super.channelRead(ctx, msg); + if (record != null) { + connectionManager.updateLog(record); + } } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - log(ctx, true, msg); + log(ctx, true, createLogRecord(msg)); super.write(ctx, msg, promise); } - public void log(ChannelHandlerContext ctx, boolean downstream, Object o) { - if (o instanceof NetworkMessage) { - NetworkMessage networkMessage = (NetworkMessage) o; + private LogRecord createLogRecord(Object msg) { + if (msg instanceof NetworkMessage) { + NetworkMessage networkMessage = (NetworkMessage) msg; if (networkMessage.getMessage() instanceof ByteBuf) { - log(ctx, downstream, networkMessage.getRemoteAddress(), (ByteBuf) networkMessage.getMessage()); + LogRecord record = new LogRecord(); + record.setAddress((InetSocketAddress) networkMessage.getRemoteAddress()); + record.setProtocol(protocol); + record.setData(ByteBufUtil.hexDump((ByteBuf) networkMessage.getMessage())); + return record; } - } else if (o instanceof ByteBuf) { - log(ctx, downstream, ctx.channel().remoteAddress(), (ByteBuf) o); } + return null; } - public void log(ChannelHandlerContext ctx, boolean downstream, SocketAddress remoteAddress, ByteBuf buf) { - StringBuilder message = new StringBuilder(); - - message.append("[").append(NetworkUtil.session(ctx.channel())).append(": "); - message.append(protocol); - if (downstream) { - message.append(" > "); - } else { - message.append(" < "); + private void log(ChannelHandlerContext ctx, boolean downstream, LogRecord record) { + if (record != null) { + StringBuilder message = new StringBuilder(); + message.append("[").append(NetworkUtil.session(ctx.channel())).append(": "); + message.append(protocol); + message.append(downstream ? " > " : " < "); + message.append(record.getAddress().getHostString()); + message.append("] "); + message.append(record.getData()); + LOGGER.info(message.toString()); } - - if (remoteAddress instanceof InetSocketAddress) { - message.append(((InetSocketAddress) remoteAddress).getHostString()); - } else { - message.append("unknown"); - } - message.append("] "); - - message.append(ByteBufUtil.hexDump(buf)); - - LOGGER.info(message.toString()); } } diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java index c98b0bd4c..3c3e17450 100644 --- a/src/main/java/org/traccar/handler/TimeHandler.java +++ b/src/main/java/org/traccar/handler/TimeHandler.java @@ -23,8 +23,8 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Position; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Arrays; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/org/traccar/handler/events/AlertEventHandler.java b/src/main/java/org/traccar/handler/events/AlertEventHandler.java index 9f77df989..531a0f957 100644 --- a/src/main/java/org/traccar/handler/events/AlertEventHandler.java +++ b/src/main/java/org/traccar/handler/events/AlertEventHandler.java @@ -25,8 +25,8 @@ import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/events/BaseEventHandler.java b/src/main/java/org/traccar/handler/events/BaseEventHandler.java index 271aaa35d..4a4fb40ff 100644 --- a/src/main/java/org/traccar/handler/events/BaseEventHandler.java +++ b/src/main/java/org/traccar/handler/events/BaseEventHandler.java @@ -22,7 +22,7 @@ import org.traccar.database.NotificationManager; import org.traccar.model.Event; import org.traccar.model.Position; -import javax.inject.Inject; +import jakarta.inject.Inject; public abstract class BaseEventHandler extends BaseDataHandler { diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java index 51bbd82d6..08ae35fcd 100644 --- a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java +++ b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java @@ -23,8 +23,8 @@ import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Collections; import java.util.Map; diff --git a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java index 772176e9c..b70f8f33b 100644 --- a/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java +++ b/src/main/java/org/traccar/handler/events/CommandResultEventHandler.java @@ -22,8 +22,8 @@ import io.netty.channel.ChannelHandler; import org.traccar.model.Event; import org.traccar.model.Position; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/events/DriverEventHandler.java b/src/main/java/org/traccar/handler/events/DriverEventHandler.java index 51fdc0307..b68327983 100644 --- a/src/main/java/org/traccar/handler/events/DriverEventHandler.java +++ b/src/main/java/org/traccar/handler/events/DriverEventHandler.java @@ -22,8 +22,8 @@ import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Collections; import java.util.Map; diff --git a/src/main/java/org/traccar/handler/events/FuelEventHandler.java b/src/main/java/org/traccar/handler/events/FuelEventHandler.java index 462cc4223..e5085ecc2 100644 --- a/src/main/java/org/traccar/handler/events/FuelEventHandler.java +++ b/src/main/java/org/traccar/handler/events/FuelEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,8 @@ import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Map; @Singleton @@ -60,13 +60,13 @@ public class FuelEventHandler extends BaseEventHandler { if (change > 0) { double threshold = AttributeUtil.lookup( cacheManager, Keys.EVENT_FUEL_INCREASE_THRESHOLD, position.getDeviceId()); - if (change >= threshold) { + if (threshold > 0 && change >= threshold) { return Map.of(new Event(Event.TYPE_DEVICE_FUEL_INCREASE, position), position); } } else if (change < 0) { double threshold = AttributeUtil.lookup( cacheManager, Keys.EVENT_FUEL_DROP_THRESHOLD, position.getDeviceId()); - if (Math.abs(change) >= threshold) { + if (threshold > 0 && Math.abs(change) >= threshold) { return Map.of(new Event(Event.TYPE_DEVICE_FUEL_DROP, position), position); } } diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java index 9414f4b31..dbe2b8118 100644 --- a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java +++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,24 +16,15 @@ package org.traccar.handler.events; import io.netty.channel.ChannelHandler; -import org.traccar.config.Config; -import org.traccar.helper.model.GeofenceUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Calendar; -import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Position; -import org.traccar.session.ConnectionManager; import org.traccar.session.cache.CacheManager; -import org.traccar.storage.Storage; -import org.traccar.storage.StorageException; -import org.traccar.storage.query.Columns; -import org.traccar.storage.query.Condition; -import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -43,62 +34,43 @@ import java.util.Map; @ChannelHandler.Sharable public class GeofenceEventHandler extends BaseEventHandler { - private final Config config; private final CacheManager cacheManager; - private final ConnectionManager connectionManager; - private final Storage storage; @Inject - public GeofenceEventHandler( - Config config, CacheManager cacheManager, ConnectionManager connectionManager, Storage storage) { - this.config = config; + public GeofenceEventHandler(CacheManager cacheManager) { this.cacheManager = cacheManager; - this.connectionManager = connectionManager; - this.storage = storage; } @Override protected Map<Event, Position> analyzePosition(Position position) { - Device device = cacheManager.getObject(Device.class, position.getDeviceId()); - if (device == null) { - return null; - } - if (!PositionUtil.isLatest(cacheManager, position) || !position.getValid()) { + if (!PositionUtil.isLatest(cacheManager, position)) { return null; } - List<Long> currentGeofences = GeofenceUtil.getCurrentGeofences(config, cacheManager, position); List<Long> oldGeofences = new ArrayList<>(); - if (device.getGeofenceIds() != null) { - oldGeofences.addAll(device.getGeofenceIds()); + Position lastPosition = cacheManager.getPosition(position.getDeviceId()); + if (lastPosition != null && lastPosition.getGeofenceIds() != null) { + oldGeofences.addAll(lastPosition.getGeofenceIds()); } - List<Long> newGeofences = new ArrayList<>(currentGeofences); - newGeofences.removeAll(oldGeofences); - oldGeofences.removeAll(currentGeofences); - - - if (!oldGeofences.isEmpty() || !newGeofences.isEmpty()) { - device.setGeofenceIds(currentGeofences.isEmpty() ? null : currentGeofences); - - try { - storage.updateObject(device, new Request( - new Columns.Include("geofenceIds"), - new Condition.Equals("id", device.getId()))); - } catch (StorageException e) { - throw new RuntimeException("Update device geofences error", e); - } - connectionManager.updateDevice(true, device); + List<Long> newGeofences = new ArrayList<>(); + if (position.getGeofenceIds() != null) { + newGeofences.addAll(position.getGeofenceIds()); + newGeofences.removeAll(oldGeofences); + oldGeofences.removeAll(position.getGeofenceIds()); } Map<Event, Position> events = new HashMap<>(); for (long geofenceId : oldGeofences) { - long calendarId = cacheManager.getObject(Geofence.class, geofenceId).getCalendarId(); - Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null; - if (calendar == null || calendar.checkMoment(position.getFixTime())) { - Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position); - event.setGeofenceId(geofenceId); - events.put(event, position); + Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId); + if (geofence != null) { + long calendarId = geofence.getCalendarId(); + Calendar calendar = calendarId != 0 ? cacheManager.getObject(Calendar.class, calendarId) : null; + if (calendar == null || calendar.checkMoment(position.getFixTime())) { + Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position); + event.setGeofenceId(geofenceId); + events.put(event, position); + } } } for (long geofenceId : newGeofences) { diff --git a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java index b2e9a3325..ba4159a42 100644 --- a/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java +++ b/src/main/java/org/traccar/handler/events/IgnitionEventHandler.java @@ -26,8 +26,8 @@ import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable diff --git a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java index 909950acf..2fa2e8869 100644 --- a/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MaintenanceEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,8 +25,8 @@ import org.traccar.model.Maintenance; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton @ChannelHandler.Sharable @@ -49,8 +49,8 @@ public class MaintenanceEventHandler extends BaseEventHandler { Map<Event, Position> events = new HashMap<>(); for (Maintenance maintenance : cacheManager.getDeviceObjects(position.getDeviceId(), Maintenance.class)) { if (maintenance.getPeriod() != 0) { - double oldValue = lastPosition.getDouble(maintenance.getType()); - double newValue = position.getDouble(maintenance.getType()); + double oldValue = getValue(lastPosition, maintenance.getType()); + double newValue = getValue(position, maintenance.getType()); if (oldValue != 0.0 && newValue != 0.0 && newValue >= maintenance.getStart()) { if (oldValue < maintenance.getStart() || (long) ((oldValue - maintenance.getStart()) / maintenance.getPeriod()) @@ -67,4 +67,17 @@ public class MaintenanceEventHandler extends BaseEventHandler { return events; } + private double getValue(Position position, String type) { + switch (type) { + case "serverTime": + return position.getServerTime().getTime(); + case "deviceTime": + return position.getDeviceTime().getTime(); + case "fixTime": + return position.getFixTime().getTime(); + default: + return position.getDouble(type); + } + } + } diff --git a/src/main/java/org/traccar/handler/events/MediaEventHandler.java b/src/main/java/org/traccar/handler/events/MediaEventHandler.java index a49e08e8d..52d8e6961 100644 --- a/src/main/java/org/traccar/handler/events/MediaEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MediaEventHandler.java @@ -19,8 +19,8 @@ import io.netty.channel.ChannelHandler; import org.traccar.model.Event; import org.traccar.model.Position; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/src/main/java/org/traccar/handler/events/MotionEventHandler.java b/src/main/java/org/traccar/handler/events/MotionEventHandler.java index c406bd935..15902d6d4 100644 --- a/src/main/java/org/traccar/handler/events/MotionEventHandler.java +++ b/src/main/java/org/traccar/handler/events/MotionEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +19,8 @@ package org.traccar.handler.events; import io.netty.channel.ChannelHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; import org.traccar.model.Event; @@ -33,8 +35,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Collections; import java.util.Map; @@ -46,14 +48,11 @@ public class MotionEventHandler extends BaseEventHandler { private final CacheManager cacheManager; private final Storage storage; - private final TripsConfig tripsConfig; @Inject - public MotionEventHandler( - CacheManager cacheManager, Storage storage, TripsConfig tripsConfig) { + public MotionEventHandler(CacheManager cacheManager, Storage storage) { this.cacheManager = cacheManager; this.storage = storage; - this.tripsConfig = tripsConfig; } @Override @@ -61,14 +60,16 @@ public class MotionEventHandler extends BaseEventHandler { long deviceId = position.getDeviceId(); Device device = cacheManager.getObject(Device.class, deviceId); - if (device == null) { + if (device == null || !PositionUtil.isLatest(cacheManager, position)) { return null; } - if (!PositionUtil.isLatest(cacheManager, position) - || !tripsConfig.getProcessInvalidPositions() && !position.getValid()) { + boolean processInvalid = AttributeUtil.lookup( + cacheManager, Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS, deviceId); + if (!processInvalid && !position.getValid()) { return null; } + TripsConfig tripsConfig = new TripsConfig(new AttributeUtil.CacheProvider(cacheManager, deviceId)); MotionState state = MotionState.fromDevice(device); MotionProcessor.updateState(state, position, position.getBoolean(Position.KEY_MOTION), tripsConfig); if (state.isChanged()) { diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java index 4d6aa8857..3bb5f713c 100644 --- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java +++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,8 +36,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Collections; import java.util.Map; @@ -52,6 +52,7 @@ public class OverspeedEventHandler extends BaseEventHandler { private final long minimalDuration; private final boolean preferLowest; + private final double multiplier; @Inject public OverspeedEventHandler( @@ -60,6 +61,7 @@ public class OverspeedEventHandler extends BaseEventHandler { this.storage = storage; minimalDuration = config.getLong(Keys.EVENT_OVERSPEED_MINIMAL_DURATION) * 1000; preferLowest = config.getBoolean(Keys.EVENT_OVERSPEED_PREFER_LOWEST); + multiplier = config.getDouble(Keys.EVENT_OVERSPEED_THRESHOLD_MULTIPLIER); } @Override @@ -84,8 +86,8 @@ public class OverspeedEventHandler extends BaseEventHandler { double geofenceSpeedLimit = 0; long overspeedGeofenceId = 0; - if (device.getGeofenceIds() != null) { - for (long geofenceId : device.getGeofenceIds()) { + if (position.getGeofenceIds() != null) { + for (long geofenceId : position.getGeofenceIds()) { Geofence geofence = cacheManager.getObject(Geofence.class, geofenceId); if (geofence != null) { double currentSpeedLimit = geofence.getDouble(Keys.EVENT_OVERSPEED_LIMIT.getKey()); @@ -107,7 +109,7 @@ public class OverspeedEventHandler extends BaseEventHandler { } OverspeedState state = OverspeedState.fromDevice(device); - OverspeedProcessor.updateState(state, position, speedLimit, minimalDuration, overspeedGeofenceId); + OverspeedProcessor.updateState(state, position, speedLimit, multiplier, minimalDuration, overspeedGeofenceId); if (state.isChanged()) { state.toDevice(device); try { diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java index d1025f548..b453c437f 100644 --- a/src/main/java/org/traccar/helper/BufferUtil.java +++ b/src/main/java/org/traccar/helper/BufferUtil.java @@ -71,4 +71,20 @@ public final class BufferUtil { } } + public static boolean isPrintable(ByteBuf buf, int length) { + boolean printable = true; + for (int i = 0; i < length; i++) { + byte b = buf.getByte(buf.readerIndex() + i); + if (b < 32 && b != '\r' && b != '\n') { + printable = false; + break; + } + } + return printable; + } + + public static String readString(ByteBuf buf, int length) { + return buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + } + } diff --git a/src/main/java/org/traccar/helper/ObdDecoder.java b/src/main/java/org/traccar/helper/ObdDecoder.java index b22065f4e..3cbae334a 100644 --- a/src/main/java/org/traccar/helper/ObdDecoder.java +++ b/src/main/java/org/traccar/helper/ObdDecoder.java @@ -51,22 +51,7 @@ public final class ObdDecoder { StringBuilder codes = new StringBuilder(); for (int i = 0; i < value.length() / 4; i++) { int numValue = Integer.parseInt(value.substring(i * 4, (i + 1) * 4), 16); - codes.append(' '); - switch (numValue >> 14) { - case 1: - codes.append('C'); - break; - case 2: - codes.append('B'); - break; - case 3: - codes.append('U'); - break; - default: - codes.append('P'); - break; - } - codes.append(String.format("%04X", numValue & 0x3FFF)); + codes.append(' ').append(decodeCode(numValue)); } if (codes.length() > 0) { return createEntry(Position.KEY_DTCS, codes.toString().trim()); @@ -75,6 +60,25 @@ public final class ObdDecoder { } } + public static String decodeCode(int value) { + char prefix; + switch (value >> 14) { + case 1: + prefix = 'C'; + break; + case 2: + prefix = 'B'; + break; + case 3: + prefix = 'U'; + break; + default: + prefix = 'P'; + break; + } + return String.format("%c%04X", prefix, value & 0x3FFF); + } + public static Map.Entry<String, Object> decodeData(int pid, long value, boolean convert) { switch (pid) { case 0x04: diff --git a/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java index b40e30d76..634950b85 100644 --- a/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java +++ b/src/main/java/org/traccar/helper/ObjectMapperContextResolver.java @@ -17,8 +17,8 @@ package org.traccar.helper; import com.fasterxml.jackson.databind.ObjectMapper; -import javax.inject.Inject; -import javax.ws.rs.ext.ContextResolver; +import jakarta.inject.Inject; +import jakarta.ws.rs.ext.ContextResolver; // This does not work as a lambda public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> { diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java index aa39e1ad7..c2aea28fa 100644 --- a/src/main/java/org/traccar/helper/Parser.java +++ b/src/main/java/org/traccar/helper/Parser.java @@ -50,6 +50,17 @@ public class Parser { public boolean hasNext(int number) { for (int i = position; i < position + number; i++) { String value = matcher.group(i); + if (value == null || value.isEmpty()) { + position += number; + return false; + } + } + return true; + } + + public boolean hasNextAny(int number) { + for (int i = position; i < position + number; i++) { + String value = matcher.group(i); if (value != null && !value.isEmpty()) { return true; } diff --git a/src/main/java/org/traccar/helper/ServletHelper.java b/src/main/java/org/traccar/helper/WebHelper.java index b6c587ec3..9533fe84b 100644 --- a/src/main/java/org/traccar/helper/ServletHelper.java +++ b/src/main/java/org/traccar/helper/WebHelper.java @@ -15,11 +15,18 @@ */ package org.traccar.helper; -import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; -public final class ServletHelper { +import jakarta.servlet.http.HttpServletRequest; - private ServletHelper() { +import org.eclipse.jetty.util.URIUtil; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +public final class WebHelper { + + private WebHelper() { } public static String retrieveRemoteAddress(HttpServletRequest request) { @@ -42,4 +49,17 @@ public final class ServletHelper { } } + public static String retrieveWebUrl(Config config) { + if (config.hasKey(Keys.WEB_URL)) { + return config.getString(Keys.WEB_URL).replaceAll("/$", ""); + } else { + String address; + try { + address = config.getString(Keys.WEB_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException e) { + address = "localhost"; + } + return URIUtil.newURI("http", address, config.getInteger(Keys.WEB_PORT), "", ""); + } + } } diff --git a/src/main/java/org/traccar/helper/model/AttributeUtil.java b/src/main/java/org/traccar/helper/model/AttributeUtil.java index 43558e8f7..2630f64f0 100644 --- a/src/main/java/org/traccar/helper/model/AttributeUtil.java +++ b/src/main/java/org/traccar/helper/model/AttributeUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,25 +15,44 @@ */ package org.traccar.helper.model; +import org.traccar.api.security.PermissionsService; +import org.traccar.config.Config; import org.traccar.config.ConfigKey; import org.traccar.config.KeyType; import org.traccar.config.Keys; import org.traccar.model.Device; import org.traccar.model.Group; +import org.traccar.model.Server; import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; public final class AttributeUtil { private AttributeUtil() { } - @SuppressWarnings({ "deprecation", "unchecked" }) + public interface Provider { + Device getDevice(); + Group getGroup(long groupId); + Server getServer(); + Config getConfig(); + } + public static <T> T lookup(CacheManager cacheManager, ConfigKey<T> key, long deviceId) { - Device device = cacheManager.getObject(Device.class, deviceId); + return lookup(new CacheProvider(cacheManager, deviceId), key); + } + + @SuppressWarnings({ "deprecation", "unchecked" }) + public static <T> T lookup(Provider provider, ConfigKey<T> key) { + Device device = provider.getDevice(); Object result = device.getAttributes().get(key.getKey()); long groupId = device.getGroupId(); while (result == null && groupId > 0) { - Group group = cacheManager.getObject(Group.class, groupId); + Group group = provider.getGroup(groupId); if (group != null) { result = group.getAttributes().get(key.getKey()); groupId = group.getGroupId(); @@ -42,10 +61,10 @@ public final class AttributeUtil { } } if (result == null && key.hasType(KeyType.SERVER)) { - result = cacheManager.getServer().getAttributes().get(key.getKey()); + result = provider.getServer().getAttributes().get(key.getKey()); } if (result == null && key.hasType(KeyType.CONFIG)) { - result = cacheManager.getConfig().getString(key.getKey()); + result = provider.getConfig().getString(key.getKey()); } if (result != null) { @@ -91,4 +110,79 @@ public final class AttributeUtil { return defaultPassword; } + public static class CacheProvider implements Provider { + + private final CacheManager cacheManager; + private final long deviceId; + + public CacheProvider(CacheManager cacheManager, long deviceId) { + this.cacheManager = cacheManager; + this.deviceId = deviceId; + } + + @Override + public Device getDevice() { + return cacheManager.getObject(Device.class, deviceId); + } + + @Override + public Group getGroup(long groupId) { + return cacheManager.getObject(Group.class, groupId); + } + + @Override + public Server getServer() { + return cacheManager.getServer(); + } + + @Override + public Config getConfig() { + return cacheManager.getConfig(); + } + } + + public static class StorageProvider implements Provider { + + private final Config config; + private final Storage storage; + private final PermissionsService permissionsService; + private final Device device; + + public StorageProvider(Config config, Storage storage, PermissionsService permissionsService, Device device) { + this.config = config; + this.storage = storage; + this.permissionsService = permissionsService; + this.device = device; + } + + @Override + public Device getDevice() { + return device; + } + + @Override + public Group getGroup(long groupId) { + try { + return storage.getObject( + Group.class, new Request(new Columns.All(), new Condition.Equals("id", groupId))); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + @Override + public Server getServer() { + try { + return permissionsService.getServer(); + } catch (StorageException e) { + throw new RuntimeException(e); + } + } + + @Override + public Config getConfig() { + return config; + } + } + } diff --git a/src/main/java/org/traccar/helper/model/DeviceUtil.java b/src/main/java/org/traccar/helper/model/DeviceUtil.java index 597078caf..5d8cb5f25 100644 --- a/src/main/java/org/traccar/helper/model/DeviceUtil.java +++ b/src/main/java/org/traccar/helper/model/DeviceUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,20 @@ package org.traccar.helper.model; import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.User; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Objects; +import java.util.stream.Collectors; + public final class DeviceUtil { private DeviceUtil() { @@ -30,4 +39,41 @@ public final class DeviceUtil { storage.updateObject(new Device(), new Request(new Columns.Include("status"))); } + + public static Collection<Device> getAccessibleDevices( + Storage storage, long userId, + Collection<Long> deviceIds, Collection<Long> groupIds) throws StorageException { + + var devices = storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Device.class))); + var deviceById = devices.stream() + .collect(Collectors.toUnmodifiableMap(Device::getId, x -> x)); + var devicesByGroup = devices.stream() + .filter(x -> x.getGroupId() > 0) + .collect(Collectors.groupingBy(Device::getGroupId)); + + var groups = storage.getObjects(Group.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Group.class))); + var groupsByGroup = groups.stream() + .filter(x -> x.getGroupId() > 0) + .collect(Collectors.groupingBy(Group::getGroupId)); + + var results = deviceIds.stream() + .map(deviceById::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + var groupQueue = new LinkedList<>(groupIds); + while (!groupQueue.isEmpty()) { + long groupId = groupQueue.pop(); + results.addAll(devicesByGroup.getOrDefault(groupId, Collections.emptyList())); + groupQueue.addAll(groupsByGroup.getOrDefault(groupId, Collections.emptyList()) + .stream().map(Group::getId).collect(Collectors.toUnmodifiableList())); + } + + return results; + } + } diff --git a/src/main/java/org/traccar/helper/model/UserUtil.java b/src/main/java/org/traccar/helper/model/UserUtil.java index 9f93afeae..4b1c404f9 100644 --- a/src/main/java/org/traccar/helper/model/UserUtil.java +++ b/src/main/java/org/traccar/helper/model/UserUtil.java @@ -15,6 +15,8 @@ */ package org.traccar.helper.model; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.storage.Storage; @@ -23,6 +25,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; +import java.util.Date; import java.util.TimeZone; public final class UserUtil { @@ -65,4 +68,11 @@ public final class UserUtil { return preference != null ? preference : defaultValue; } + public static void setUserDefaults(User user, Config config) { + user.setDeviceLimit(config.getInteger(Keys.USERS_DEFAULT_DEVICE_LIMIT)); + int expirationDays = config.getInteger(Keys.USERS_DEFAULT_EXPIRATION_DAYS); + if (expirationDays > 0) { + user.setExpirationTime(new Date(System.currentTimeMillis() + expirationDays * 86400000L)); + } + } } diff --git a/src/main/java/org/traccar/mail/LogMailManager.java b/src/main/java/org/traccar/mail/LogMailManager.java index b6b912d6c..90de3bcce 100644 --- a/src/main/java/org/traccar/mail/LogMailManager.java +++ b/src/main/java/org/traccar/mail/LogMailManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.model.User; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeBodyPart; public class LogMailManager implements MailManager { @@ -32,13 +32,17 @@ public class LogMailManager implements MailManager { } @Override - public void sendMessage(User user, String subject, String body) throws MessagingException { - sendMessage(user, subject, body, null); + public void sendMessage( + User user, boolean system, String subject, String body) throws MessagingException { + sendMessage(user, system, subject, body, null); } @Override - public void sendMessage(User user, String subject, String body, MimeBodyPart attachment) throws MessagingException { - LOGGER.info("\nTo: " + user.getEmail() + "\nSubject: " + subject + "\nBody:\n" + body); + public void sendMessage( + User user, boolean system, String subject, String body, MimeBodyPart attachment) throws MessagingException { + LOGGER.info( + "Email sent\nTo: {}\nSubject: {}\nAttachment: {}\nBody:\n{}", + user.getEmail(), subject, attachment != null ? attachment.getFileName() : null, body); } } diff --git a/src/main/java/org/traccar/mail/MailManager.java b/src/main/java/org/traccar/mail/MailManager.java index 69efbed32..d05a07de9 100644 --- a/src/main/java/org/traccar/mail/MailManager.java +++ b/src/main/java/org/traccar/mail/MailManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,17 @@ package org.traccar.mail; import org.traccar.model.User; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeBodyPart; public interface MailManager { boolean getEmailEnabled(); - void sendMessage(User user, String subject, String body) throws MessagingException; + void sendMessage( + User user, boolean system, String subject, String body) throws MessagingException; - void sendMessage(User user, String subject, String body, MimeBodyPart attachment) throws MessagingException; + void sendMessage( + User user, boolean system, String subject, String body, MimeBodyPart attachment) throws MessagingException; } diff --git a/src/main/java/org/traccar/mail/SmtpMailManager.java b/src/main/java/org/traccar/mail/SmtpMailManager.java index 4a0b7048f..70099d879 100644 --- a/src/main/java/org/traccar/mail/SmtpMailManager.java +++ b/src/main/java/org/traccar/mail/SmtpMailManager.java @@ -23,16 +23,16 @@ import org.traccar.database.StatisticsManager; import org.traccar.model.User; import org.traccar.notification.PropertiesProvider; -import javax.mail.BodyPart; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; +import jakarta.mail.BodyPart; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Multipart; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; import java.io.UnsupportedEncodingException; import java.util.Date; import java.util.Properties; @@ -93,19 +93,21 @@ public final class SmtpMailManager implements MailManager { return config.hasKey(Keys.MAIL_SMTP_HOST); } + @Override public void sendMessage( - User user, String subject, String body) throws MessagingException { - sendMessage(user, subject, body, null); + User user, boolean system, String subject, String body) throws MessagingException { + sendMessage(user, system, subject, body, null); } + @Override public void sendMessage( - User user, String subject, String body, MimeBodyPart attachment) throws MessagingException { + User user, boolean system, String subject, String body, MimeBodyPart attachment) throws MessagingException { Properties properties = null; if (!config.getBoolean(Keys.MAIL_SMTP_IGNORE_USER_CONFIG)) { properties = getProperties(new PropertiesProvider(user)); } - if (properties == null) { + if (properties == null && (system || !config.getBoolean(Keys.MAIL_SMTP_SYSTEM_ONLY))) { properties = getProperties(new PropertiesProvider(config)); } if (properties == null) { diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java index 62c51cc4a..03f1995ba 100644 --- a/src/main/java/org/traccar/model/Calendar.java +++ b/src/main/java/org/traccar/model/Calendar.java @@ -34,6 +34,7 @@ import java.time.Duration; import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; @StorageName("tc_calendars") public class Calendar extends ExtendedModel { @@ -68,16 +69,22 @@ public class Calendar extends ExtendedModel { return calendar; } - public Collection<VEvent> findEvents(Date date) { + private Collection<VEvent> findEvents(Date date) { if (calendar != null) { - Period period = new Period(new DateTime(date), Duration.ZERO); - Filter<VEvent> filter = new Filter<>(new PeriodRule<>(period)); + var filter = new Filter<VEvent>(new PeriodRule<>(new Period(new DateTime(date), Duration.ZERO))); return filter.filter(calendar.getComponents(CalendarComponent.VEVENT)); } else { return List.of(); } } + public Collection<Period> findPeriods(Date date) { + var calendarDate = new net.fortuna.ical4j.model.Date(date); + return findEvents(date).stream() + .flatMap((event) -> event.getConsumedTime(calendarDate, calendarDate).stream()) + .collect(Collectors.toSet()); + } + public boolean checkMoment(Date date) { return !findEvents(date).isEmpty(); } diff --git a/src/main/java/org/traccar/model/CellTower.java b/src/main/java/org/traccar/model/CellTower.java index 355594c64..4277cc4c4 100644 --- a/src/main/java/org/traccar/model/CellTower.java +++ b/src/main/java/org/traccar/model/CellTower.java @@ -104,7 +104,7 @@ public class CellTower { } public void setSignalStrength(Integer signalStrength) { - this.signalStrength = signalStrength; + this.signalStrength = signalStrength > 0 ? -signalStrength : signalStrength; } public void setOperator(long operator) { diff --git a/src/main/java/org/traccar/model/Device.java b/src/main/java/org/traccar/model/Device.java index b8c87921d..a3088a613 100644 --- a/src/main/java/org/traccar/model/Device.java +++ b/src/main/java/org/traccar/model/Device.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +15,26 @@ */ package org.traccar.model; -import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; - import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; +import java.util.Date; + @StorageName("tc_devices") -public class Device extends GroupedModel implements Disableable { +public class Device extends GroupedModel implements Disableable, Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String name; @@ -43,7 +53,10 @@ public class Device extends GroupedModel implements Disableable { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; + if (uniqueId.contains("../") || uniqueId.contains("..\\")) { + throw new IllegalArgumentException("Invalid unique id"); + } + this.uniqueId = uniqueId.trim(); } public static final String STATUS_UNKNOWN = "unknown"; @@ -83,21 +96,6 @@ public class Device extends GroupedModel implements Disableable { this.positionId = positionId; } - private List<Long> geofenceIds; - - @QueryIgnore - public List<Long> getGeofenceIds() { - return geofenceIds; - } - - public void setGeofenceIds(List<? extends Number> geofenceIds) { - if (geofenceIds != null) { - this.geofenceIds = geofenceIds.stream().map(Number::longValue).collect(Collectors.toList()); - } else { - this.geofenceIds = null; - } - } - private String phone; public String getPhone() { @@ -105,7 +103,7 @@ public class Device extends GroupedModel implements Disableable { } public void setPhone(String phone) { - this.phone = phone; + this.phone = phone != null ? phone.trim() : null; } private String model; diff --git a/src/main/java/org/traccar/model/Driver.java b/src/main/java/org/traccar/model/Driver.java index b9e023088..ca5714e51 100644 --- a/src/main/java/org/traccar/model/Driver.java +++ b/src/main/java/org/traccar/model/Driver.java @@ -38,7 +38,7 @@ public class Driver extends ExtendedModel { } public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; + this.uniqueId = uniqueId.trim(); } } diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java index 0e851d748..6f90de9da 100644 --- a/src/main/java/org/traccar/model/Event.java +++ b/src/main/java/org/traccar/model/Event.java @@ -46,6 +46,7 @@ public class Event extends Message { public static final String TYPE_DEVICE_UNKNOWN = "deviceUnknown"; public static final String TYPE_DEVICE_OFFLINE = "deviceOffline"; public static final String TYPE_DEVICE_INACTIVE = "deviceInactive"; + public static final String TYPE_QUEUED_COMMAND_SENT = "queuedCommandSent"; public static final String TYPE_DEVICE_MOVING = "deviceMoving"; public static final String TYPE_DEVICE_STOPPED = "deviceStopped"; diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index 7a61eda8c..d5cd094da 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -89,14 +89,19 @@ public class ExtendedModel extends BaseModel { } } - public String getString(String key) { + public String getString(String key, String defaultValue) { if (attributes.containsKey(key)) { - return attributes.get(key).toString(); + Object value = attributes.get(key); + return value != null ? value.toString() : null; } else { - return null; + return defaultValue; } } + public String getString(String key) { + return getString(key, null); + } + public double getDouble(String key) { if (attributes.containsKey(key)) { Object value = attributes.get(key); diff --git a/src/main/java/org/traccar/model/Geofence.java b/src/main/java/org/traccar/model/Geofence.java index 9259028fb..ca6293651 100644 --- a/src/main/java/org/traccar/model/Geofence.java +++ b/src/main/java/org/traccar/model/Geofence.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,19 @@ import org.traccar.storage.StorageName; import java.text.ParseException; @StorageName("tc_geofences") -public class Geofence extends ScheduledModel { +public class Geofence extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String name; diff --git a/src/main/java/org/traccar/model/LogRecord.java b/src/main/java/org/traccar/model/LogRecord.java new file mode 100644 index 000000000..c19163af3 --- /dev/null +++ b/src/main/java/org/traccar/model/LogRecord.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.net.InetSocketAddress; + +public class LogRecord { + + private InetSocketAddress address; + + public void setAddress(InetSocketAddress address) { + this.address = address; + } + + @JsonIgnore + public InetSocketAddress getAddress() { + return address; + } + + public String getHost() { + return address.getHostString(); + } + + private String protocol; + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + private String uniqueId; + + public String getUniqueId() { + return uniqueId; + } + + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + private long deviceId; + + public long getDeviceId() { + return deviceId; + } + + public void setDeviceId(long deviceId) { + this.deviceId = deviceId; + } + + private String data; + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + +} diff --git a/src/main/java/org/traccar/model/Notification.java b/src/main/java/org/traccar/model/Notification.java index 95e446132..6dcd9c9de 100644 --- a/src/main/java/org/traccar/model/Notification.java +++ b/src/main/java/org/traccar/model/Notification.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,19 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.StorageName; @StorageName("tc_notifications") -public class Notification extends ScheduledModel { +public class Notification extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private boolean always; @@ -46,6 +58,16 @@ public class Notification extends ScheduledModel { this.type = type; } + private long commandId; + + public long getCommandId() { + return commandId; + } + + public void setCommandId(long commandId) { + this.commandId = commandId; + } + private String notificators; public String getNotificators() { diff --git a/src/main/java/org/traccar/model/ObjectOperation.java b/src/main/java/org/traccar/model/ObjectOperation.java new file mode 100644 index 000000000..b462580bb --- /dev/null +++ b/src/main/java/org/traccar/model/ObjectOperation.java @@ -0,0 +1,7 @@ +package org.traccar.model; + +public enum ObjectOperation { + ADD, + UPDATE, + DELETE, +} diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java index 1286db5f2..39f63217d 100644 --- a/src/main/java/org/traccar/model/Position.java +++ b/src/main/java/org/traccar/model/Position.java @@ -16,6 +16,8 @@ package org.traccar.model; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonIgnore; import org.traccar.storage.QueryIgnore; @@ -40,7 +42,7 @@ public class Position extends Message { public static final String KEY_ODOMETER = "odometer"; // meters public static final String KEY_ODOMETER_SERVICE = "serviceOdometer"; // meters public static final String KEY_ODOMETER_TRIP = "tripOdometer"; // meters - public static final String KEY_HOURS = "hours"; + public static final String KEY_HOURS = "hours"; // milliseconds public static final String KEY_STEPS = "steps"; public static final String KEY_HEART_RATE = "heartRate"; public static final String KEY_INPUT = "input"; @@ -83,20 +85,23 @@ public class Position extends Message { public static final String KEY_OPERATOR = "operator"; public static final String KEY_COMMAND = "command"; public static final String KEY_BLOCKED = "blocked"; + public static final String KEY_LOCK = "lock"; public static final String KEY_DOOR = "door"; public static final String KEY_AXLE_WEIGHT = "axleWeight"; public static final String KEY_G_SENSOR = "gSensor"; public static final String KEY_ICCID = "iccid"; public static final String KEY_PHONE = "phone"; public static final String KEY_SPEED_LIMIT = "speedLimit"; + public static final String KEY_DRIVING_TIME = "drivingTime"; public static final String KEY_DTCS = "dtcs"; - public static final String KEY_OBD_SPEED = "obdSpeed"; // knots + public static final String KEY_OBD_SPEED = "obdSpeed"; // km/h public static final String KEY_OBD_ODOMETER = "obdOdometer"; // meters public static final String KEY_RESULT = "result"; public static final String KEY_DRIVER_UNIQUE_ID = "driverUniqueId"; + public static final String KEY_CARD = "card"; // Start with 1 not 0 public static final String PREFIX_TEMP = "temp"; @@ -306,6 +311,20 @@ public class Position extends Message { this.network = network; } + private List<Long> geofenceIds; + + public List<Long> getGeofenceIds() { + return geofenceIds; + } + + public void setGeofenceIds(List<? extends Number> geofenceIds) { + if (geofenceIds != null) { + this.geofenceIds = geofenceIds.stream().map(Number::longValue).collect(Collectors.toList()); + } else { + this.geofenceIds = null; + } + } + @JsonIgnore @QueryIgnore @Override diff --git a/src/main/java/org/traccar/model/Report.java b/src/main/java/org/traccar/model/Report.java index 1556ecc9e..2ee7ae288 100644 --- a/src/main/java/org/traccar/model/Report.java +++ b/src/main/java/org/traccar/model/Report.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,19 @@ package org.traccar.model; import org.traccar.storage.StorageName; @StorageName("tc_reports") -public class Report extends ScheduledModel { +public class Report extends ExtendedModel implements Schedulable { + + private long calendarId; + + @Override + public long getCalendarId() { + return calendarId; + } + + @Override + public void setCalendarId(long calendarId) { + this.calendarId = calendarId; + } private String type; diff --git a/src/main/java/org/traccar/model/ScheduledModel.java b/src/main/java/org/traccar/model/Schedulable.java index 9e6a4b9a6..331e77583 100644 --- a/src/main/java/org/traccar/model/ScheduledModel.java +++ b/src/main/java/org/traccar/model/Schedulable.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,15 +16,7 @@ */ package org.traccar.model; -public class ScheduledModel extends ExtendedModel { - - private long calendarId; - - public long getCalendarId() { - return calendarId; - } - - public void setCalendarId(long calendarId) { - this.calendarId = calendarId; - } +public interface Schedulable { + long getCalendarId(); + void setCalendarId(long calendarId); } diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java index 73645721b..6442186b6 100644 --- a/src/main/java/org/traccar/model/Server.java +++ b/src/main/java/org/traccar/model/Server.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.traccar.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + import org.traccar.storage.QueryIgnore; import org.traccar.storage.StorageName; @@ -227,6 +228,18 @@ public class Server extends ExtendedModel implements UserRestrictions { private boolean geocoderEnabled; + private boolean textEnabled; + + @QueryIgnore + public void setTextEnabled(boolean textEnabled) { + this.textEnabled = textEnabled; + } + + @QueryIgnore + public Boolean getTextEnabled() { + return textEnabled; + } + @QueryIgnore public void setGeocoderEnabled(boolean geocoderEnabled) { this.geocoderEnabled = geocoderEnabled; @@ -261,4 +274,27 @@ public class Server extends ExtendedModel implements UserRestrictions { this.newServer = newServer; } + private boolean openIdEnabled; + + @QueryIgnore + public boolean getOpenIdEnabled() { + return openIdEnabled; + } + + @QueryIgnore + public void setOpenIdEnabled(boolean openIdEnabled) { + this.openIdEnabled = openIdEnabled; + } + + private boolean openIdForce; + + @QueryIgnore + public boolean getOpenIdForce() { + return openIdForce; + } + + @QueryIgnore + public void setOpenIdForce(boolean openIdForce) { + this.openIdForce = openIdForce; + } } diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java index 53594fe07..8cfee0f48 100644 --- a/src/main/java/org/traccar/model/User.java +++ b/src/main/java/org/traccar/model/User.java @@ -63,7 +63,7 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable } public void setPhone(String phone) { - this.phone = phone; + this.phone = phone != null ? phone.trim() : null; } private boolean readonly; @@ -251,6 +251,26 @@ public class User extends ExtendedModel implements UserRestrictions, Disableable this.poiLayer = poiLayer; } + private String totpKey; + + public String getTotpKey() { + return totpKey; + } + + public void setTotpKey(String totpKey) { + this.totpKey = totpKey; + } + + private boolean temporary; + + public boolean getTemporary() { + return temporary; + } + + public void setTemporary(boolean temporary) { + this.temporary = temporary; + } + @QueryIgnore public String getPassword() { return null; diff --git a/src/main/java/org/traccar/model/WifiAccessPoint.java b/src/main/java/org/traccar/model/WifiAccessPoint.java index e28c1b935..64858f4c7 100644 --- a/src/main/java/org/traccar/model/WifiAccessPoint.java +++ b/src/main/java/org/traccar/model/WifiAccessPoint.java @@ -52,7 +52,7 @@ public class WifiAccessPoint { } public void setSignalStrength(Integer signalStrength) { - this.signalStrength = signalStrength; + this.signalStrength = signalStrength > 0 ? -signalStrength : signalStrength; } private Integer channel; diff --git a/src/main/java/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java index 9ee3b97b6..7685eac0d 100644 --- a/src/main/java/org/traccar/notification/NotificationFormatter.java +++ b/src/main/java/org/traccar/notification/NotificationFormatter.java @@ -19,16 +19,18 @@ package org.traccar.notification; import org.apache.velocity.VelocityContext; import org.traccar.helper.model.UserUtil; import org.traccar.model.Device; +import org.traccar.model.Driver; import org.traccar.model.Event; import org.traccar.model.Geofence; import org.traccar.model.Maintenance; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.session.cache.CacheManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton public class NotificationFormatter { @@ -43,13 +45,15 @@ public class NotificationFormatter { this.textTemplateFormatter = textTemplateFormatter; } - public NotificationMessage formatMessage(User user, Event event, Position position, String templatePath) { + public NotificationMessage formatMessage( + Notification notification, User user, Event event, Position position, String templatePath) { Server server = cacheManager.getServer(); Device device = cacheManager.getObject(Device.class, event.getDeviceId()); VelocityContext velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("notification", notification); velocityContext.put("device", device); velocityContext.put("event", event); if (position != null) { @@ -66,7 +70,8 @@ public class NotificationFormatter { } String driverUniqueId = event.getString(Position.KEY_DRIVER_UNIQUE_ID); if (driverUniqueId != null) { - velocityContext.put("driver", cacheManager.findDriverByUniqueId(device.getId(), driverUniqueId)); + velocityContext.put("driver", cacheManager.getDeviceObjects(device.getId(), Driver.class).stream() + .filter(driver -> driver.getUniqueId().equals(driverUniqueId)).findFirst().orElse(null)); } return textTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath); diff --git a/src/main/java/org/traccar/notification/NotificationMessage.java b/src/main/java/org/traccar/notification/NotificationMessage.java index 0fb8d7654..551a2d823 100644 --- a/src/main/java/org/traccar/notification/NotificationMessage.java +++ b/src/main/java/org/traccar/notification/NotificationMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,12 +16,16 @@ */ package org.traccar.notification; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + public class NotificationMessage { - private String subject; - private String body; + private final String subject; + private final String body; - public NotificationMessage(String subject, String body) { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public NotificationMessage(@JsonProperty("subject") String subject, @JsonProperty("body") String body) { this.subject = subject; this.body = body; } diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java index 1d9f4f423..8d41ee354 100644 --- a/src/main/java/org/traccar/notification/NotificatorManager.java +++ b/src/main/java/org/traccar/notification/NotificatorManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,23 +17,21 @@ package org.traccar.notification; import com.google.inject.Injector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Typed; import org.traccar.notificators.Notificator; +import org.traccar.notificators.NotificatorCommand; import org.traccar.notificators.NotificatorFirebase; import org.traccar.notificators.NotificatorMail; -import org.traccar.notificators.NotificatorNull; import org.traccar.notificators.NotificatorPushover; import org.traccar.notificators.NotificatorSms; import org.traccar.notificators.NotificatorTelegram; import org.traccar.notificators.NotificatorTraccar; import org.traccar.notificators.NotificatorWeb; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.Arrays; import java.util.HashSet; import java.util.Map; @@ -43,9 +41,8 @@ import java.util.stream.Collectors; @Singleton public class NotificatorManager { - private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorManager.class); - private static final Map<String, Class<? extends Notificator>> NOTIFICATORS_ALL = Map.of( + "command", NotificatorCommand.class, "web", NotificatorWeb.class, "mail", NotificatorMail.class, "sms", NotificatorSms.class, @@ -69,14 +66,13 @@ public class NotificatorManager { public Notificator getNotificator(String type) { var clazz = NOTIFICATORS_ALL.get(type); - if (clazz != null) { + if (clazz != null && types.contains(type)) { var notificator = injector.getInstance(clazz); if (notificator != null) { return notificator; } } - LOGGER.warn("Failed to get notificator {}", type); - return new NotificatorNull(); + throw new RuntimeException("Failed to get notificator " + type); } public Set<Typed> getAllNotificatorTypes() { diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java index 444f4a7c2..adcfc2ab5 100644 --- a/src/main/java/org/traccar/notification/TextTemplateFormatter.java +++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java @@ -15,10 +15,11 @@ */ package org.traccar.notification; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; -import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.tools.generic.DateTool; import org.apache.velocity.tools.generic.NumberTool; import org.slf4j.Logger; @@ -29,8 +30,6 @@ import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.storage.StorageException; -import javax.inject.Inject; -import javax.inject.Singleton; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; @@ -75,19 +74,8 @@ public class TextTemplateFormatter { } public Template getTemplate(String name, String path) { - - String templateFilePath; - Template template; - - try { - templateFilePath = Paths.get(path, name + ".vm").toString(); - template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); - } catch (ResourceNotFoundException error) { - LOGGER.warn("Notification template error", error); - templateFilePath = Paths.get(path, "unknown.vm").toString(); - template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); - } - return template; + String templateFilePath = Paths.get(path, name + ".vm").toString(); + return velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); } public NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) { diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java index 052365c7a..59216b1b6 100644 --- a/src/main/java/org/traccar/notificators/Notificator.java +++ b/src/main/java/org/traccar/notificators/Notificator.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,12 +17,30 @@ package org.traccar.notificators; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.MessageException; +import org.traccar.notification.NotificationFormatter; +import org.traccar.notification.NotificationMessage; -public interface Notificator { +public abstract class Notificator { - void send(User user, Event event, Position position) throws MessageException, InterruptedException; + private final NotificationFormatter notificationFormatter; + private final String templatePath; + + public Notificator(NotificationFormatter notificationFormatter, String templatePath) { + this.notificationFormatter = notificationFormatter; + this.templatePath = templatePath; + } + + public void send(Notification notification, User user, Event event, Position position) throws MessageException { + var message = notificationFormatter.formatMessage(notification, user, event, position, templatePath); + send(user, message, event, position); + } + + public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/org/traccar/notificators/NotificatorCommand.java b/src/main/java/org/traccar/notificators/NotificatorCommand.java new file mode 100644 index 000000000..712dda274 --- /dev/null +++ b/src/main/java/org/traccar/notificators/NotificatorCommand.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 - 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.notificators; + +import org.traccar.database.CommandsManager; +import org.traccar.model.Command; +import org.traccar.model.Event; +import org.traccar.model.Notification; +import org.traccar.model.Position; +import org.traccar.model.User; +import org.traccar.notification.MessageException; +import org.traccar.storage.Storage; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +@Singleton +public class NotificatorCommand extends Notificator { + + private final Storage storage; + private final CommandsManager commandsManager; + + @Inject + public NotificatorCommand(Storage storage, CommandsManager commandsManager) { + super(null, null); + this.storage = storage; + this.commandsManager = commandsManager; + } + + @Override + public void send(Notification notification, User user, Event event, Position position) throws MessageException { + + if (notification == null || notification.getCommandId() <= 0) { + throw new MessageException("Saved command not provided"); + } + + try { + Command command = storage.getObject(Command.class, new Request( + new Columns.All(), new Condition.Equals("id", notification.getCommandId()))); + command.setDeviceId(event.getDeviceId()); + commandsManager.sendCommand(command); + } catch (Exception e) { + throw new MessageException(e); + } + } + +} diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java index 5ce2cbc0b..7440068f8 100644 --- a/src/main/java/org/traccar/notificators/NotificatorFirebase.java +++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,34 +24,50 @@ import com.google.firebase.messaging.AndroidNotification; import com.google.firebase.messaging.ApnsConfig; import com.google.firebase.messaging.Aps; import com.google.firebase.messaging.FirebaseMessaging; -import com.google.firebase.messaging.FirebaseMessagingException; +import com.google.firebase.messaging.MessagingErrorCode; import com.google.firebase.messaging.MulticastMessage; -import com.google.firebase.messaging.Notification; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; +import org.traccar.model.ObjectOperation; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; +import org.traccar.notification.NotificationMessage; +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; @Singleton -public class NotificatorFirebase implements Notificator { +public class NotificatorFirebase extends Notificator { - private final NotificationFormatter notificationFormatter; + private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorFirebase.class); + private final Storage storage; + private final CacheManager cacheManager; @Inject - public NotificatorFirebase(Config config, NotificationFormatter notificationFormatter) throws IOException { + public NotificatorFirebase( + Config config, NotificationFormatter notificationFormatter, + Storage storage, CacheManager cacheManager) throws IOException { - this.notificationFormatter = notificationFormatter; + super(notificationFormatter, "short"); + this.storage = storage; + this.cacheManager = cacheManager; InputStream serviceAccount = new ByteArrayInputStream( config.getString(Keys.NOTIFICATOR_FIREBASE_SERVICE_ACCOUNT).getBytes()); @@ -64,17 +80,16 @@ public class NotificatorFirebase implements Notificator { } @Override - public void send(User user, Event event, Position position) throws MessageException { + public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException { if (user.hasAttribute("notificationTokens")) { - var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + List<String> registrationTokens = new ArrayList<>( + Arrays.asList(user.getString("notificationTokens").split("[, ]"))); - List<String> registrationTokens = Arrays.asList(user.getString("notificationTokens").split("[, ]")); - - MulticastMessage message = MulticastMessage.builder() - .setNotification(Notification.builder() - .setTitle(shortMessage.getSubject()) - .setBody(shortMessage.getBody()) + var messageBuilder = MulticastMessage.builder() + .setNotification(com.google.firebase.messaging.Notification.builder() + .setTitle(message.getSubject()) + .setBody(message.getBody()) .build()) .setAndroidConfig(AndroidConfig.builder() .setNotification(AndroidNotification.builder() @@ -86,19 +101,41 @@ public class NotificatorFirebase implements Notificator { .setSound("default") .build()) .build()) - .addAllTokens(registrationTokens) - .putData("eventId", String.valueOf(event.getId())) - .build(); + .addAllTokens(registrationTokens); + + if (event != null) { + messageBuilder.putData("eventId", String.valueOf(event.getId())); + } try { - var result = FirebaseMessaging.getInstance().sendMulticast(message); - for (var response : result.getResponses()) { + var result = FirebaseMessaging.getInstance().sendEachForMulticast(messageBuilder.build()); + List<String> failedTokens = new LinkedList<>(); + var iterator = result.getResponses().listIterator(); + while (iterator.hasNext()) { + int index = iterator.nextIndex(); + var response = iterator.next(); if (!response.isSuccessful()) { - throw new MessageException(response.getException()); + MessagingErrorCode error = response.getException().getMessagingErrorCode(); + if (error == MessagingErrorCode.INVALID_ARGUMENT || error == MessagingErrorCode.UNREGISTERED) { + failedTokens.add(registrationTokens.get(index)); + } + LOGGER.warn("Firebase user {} error", user.getId(), response.getException()); + } + } + if (!failedTokens.isEmpty()) { + registrationTokens.removeAll(failedTokens); + if (registrationTokens.isEmpty()) { + user.getAttributes().remove("notificationTokens"); + } else { + user.set("notificationTokens", String.join(",", registrationTokens)); } + storage.updateObject(user, new Request( + new Columns.Include("attributes"), + new Condition.Equals("id", user.getId()))); + cacheManager.invalidateObject(true, User.class, user.getId(), ObjectOperation.UPDATE); } - } catch (FirebaseMessagingException e) { - throw new MessageException(e); + } catch (Exception e) { + LOGGER.warn("Firebase error", e); } } } diff --git a/src/main/java/org/traccar/notificators/NotificatorMail.java b/src/main/java/org/traccar/notificators/NotificatorMail.java index 19fde6756..e7ca10157 100644 --- a/src/main/java/org/traccar/notificators/NotificatorMail.java +++ b/src/main/java/org/traccar/notificators/NotificatorMail.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,34 +16,32 @@ */ package org.traccar.notificators; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.mail.MessagingException; import org.traccar.mail.MailManager; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.mail.MessagingException; +import org.traccar.notification.NotificationMessage; @Singleton -public class NotificatorMail implements Notificator { +public class NotificatorMail extends Notificator { private final MailManager mailManager; - private final NotificationFormatter notificationFormatter; @Inject public NotificatorMail(MailManager mailManager, NotificationFormatter notificationFormatter) { + super(notificationFormatter, "full"); this.mailManager = mailManager; - this.notificationFormatter = notificationFormatter; } @Override - public void send(User user, Event event, Position position) throws MessageException { + public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException { try { - var fullMessage = notificationFormatter.formatMessage(user, event, position, "full"); - mailManager.sendMessage(user, fullMessage.getSubject(), fullMessage.getBody()); + mailManager.sendMessage(user, false, message.getSubject(), message.getBody()); } catch (MessagingException e) { throw new MessageException(e); } diff --git a/src/main/java/org/traccar/notificators/NotificatorNull.java b/src/main/java/org/traccar/notificators/NotificatorNull.java deleted file mode 100644 index ba9ade9c6..000000000 --- a/src/main/java/org/traccar/notificators/NotificatorNull.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) - * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.traccar.notificators; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.traccar.model.Event; -import org.traccar.model.Position; -import org.traccar.model.User; - -public class NotificatorNull implements Notificator { - - private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorNull.class); - - @Override - public void send(User user, Event event, Position position) { - LOGGER.warn("You are using null notificatior, please check your configuration, notification not sent"); - } - -} diff --git a/src/main/java/org/traccar/notificators/NotificatorPushover.java b/src/main/java/org/traccar/notificators/NotificatorPushover.java index e00db0579..a9708818b 100644 --- a/src/main/java/org/traccar/notificators/NotificatorPushover.java +++ b/src/main/java/org/traccar/notificators/NotificatorPushover.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +16,21 @@ package org.traccar.notificators; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.NotificationFormatter; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; +import org.traccar.notification.NotificationMessage; @Singleton -public class NotificatorPushover implements Notificator { +public class NotificatorPushover extends Notificator { - private final NotificationFormatter notificationFormatter; private final Client client; private final String url; @@ -53,7 +52,7 @@ public class NotificatorPushover implements Notificator { @Inject public NotificatorPushover(Config config, NotificationFormatter notificationFormatter, Client client) { - this.notificationFormatter = notificationFormatter; + super(notificationFormatter, "short"); this.client = client; url = "https://api.pushover.net/1/messages.json"; token = config.getString(Keys.NOTIFICATOR_PUSHOVER_TOKEN); @@ -61,8 +60,7 @@ public class NotificatorPushover implements Notificator { } @Override - public void send(User user, Event event, Position position) { - var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + public void send(User user, NotificationMessage shortMessage, Event event, Position position) { Message message = new Message(); message.token = token; diff --git a/src/main/java/org/traccar/notificators/NotificatorSms.java b/src/main/java/org/traccar/notificators/NotificatorSms.java index e37d10888..a8086adcc 100644 --- a/src/main/java/org/traccar/notificators/NotificatorSms.java +++ b/src/main/java/org/traccar/notificators/NotificatorSms.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2017 - 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,38 +16,36 @@ */ package org.traccar.notificators; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.traccar.database.StatisticsManager; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.MessageException; import org.traccar.notification.NotificationFormatter; +import org.traccar.notification.NotificationMessage; import org.traccar.sms.SmsManager; -import javax.inject.Inject; -import javax.inject.Singleton; - @Singleton -public class NotificatorSms implements Notificator { +public class NotificatorSms extends Notificator { private final SmsManager smsManager; - private final NotificationFormatter notificationFormatter; private final StatisticsManager statisticsManager; @Inject public NotificatorSms( SmsManager smsManager, NotificationFormatter notificationFormatter, StatisticsManager statisticsManager) { + super(notificationFormatter, "short"); this.smsManager = smsManager; - this.notificationFormatter = notificationFormatter; this.statisticsManager = statisticsManager; } @Override - public void send(User user, Event event, Position position) throws MessageException, InterruptedException { + public void send(User user, NotificationMessage message, Event event, Position position) throws MessageException { if (user.getPhone() != null) { - var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); statisticsManager.registerSms(); - smsManager.sendMessage(user.getPhone(), shortMessage.getBody(), false); + smsManager.sendMessage(user.getPhone(), message.getBody(), false); } } diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java index 38e87c222..4c8d7b8aa 100644 --- a/src/main/java/org/traccar/notificators/NotificatorTelegram.java +++ b/src/main/java/org/traccar/notificators/NotificatorTelegram.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2021 Rafael Miquelino (rafaelmiquelino@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,22 +17,21 @@ package org.traccar.notificators; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.NotificationFormatter; - -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; +import org.traccar.notification.NotificationMessage; @Singleton -public class NotificatorTelegram implements Notificator { +public class NotificatorTelegram extends Notificator { - private final NotificationFormatter notificationFormatter; private final Client client; private final String urlSendText; @@ -64,7 +63,7 @@ public class NotificatorTelegram implements Notificator { @Inject public NotificatorTelegram(Config config, NotificationFormatter notificationFormatter, Client client) { - this.notificationFormatter = notificationFormatter; + super(notificationFormatter, "short"); this.client = client; urlSendText = String.format( "https://api.telegram.org/bot%s/sendMessage", config.getString(Keys.NOTIFICATOR_TELEGRAM_KEY)); @@ -85,8 +84,7 @@ public class NotificatorTelegram implements Notificator { } @Override - public void send(User user, Event event, Position position) { - var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + public void send(User user, NotificationMessage shortMessage, Event event, Position position) { TextMessage message = new TextMessage(); message.chatId = user.getString("telegramChatId"); diff --git a/src/main/java/org/traccar/notificators/NotificatorTraccar.java b/src/main/java/org/traccar/notificators/NotificatorTraccar.java index 9ae39f975..983c4cda6 100644 --- a/src/main/java/org/traccar/notificators/NotificatorTraccar.java +++ b/src/main/java/org/traccar/notificators/NotificatorTraccar.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,46 @@ package org.traccar.notificators; import com.fasterxml.jackson.annotation.JsonProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.model.ObjectOperation; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.NotificationFormatter; +import org.traccar.notification.NotificationMessage; +import org.traccar.session.cache.CacheManager; +import org.traccar.storage.Storage; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; @Singleton -public class NotificatorTraccar implements Notificator { +public class NotificatorTraccar extends Notificator { + + private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorTraccar.class); - private final NotificationFormatter notificationFormatter; private final Client client; + private final Storage storage; + private final CacheManager cacheManager; private final String url; private final String key; - public static class Notification { + public static class NotificationObject { @JsonProperty("title") private String title; @JsonProperty("body") @@ -50,33 +68,69 @@ public class NotificatorTraccar implements Notificator { @JsonProperty("registration_ids") private String[] tokens; @JsonProperty("notification") - private Notification notification; + private NotificationObject notification; } @Inject - public NotificatorTraccar(Config config, NotificationFormatter notificationFormatter, Client client) { - this.notificationFormatter = notificationFormatter; + public NotificatorTraccar( + Config config, NotificationFormatter notificationFormatter, Client client, + Storage storage, CacheManager cacheManager) { + super(notificationFormatter, "short"); this.client = client; + this.storage = storage; + this.cacheManager = cacheManager; this.url = "https://www.traccar.org/push/"; this.key = config.getString(Keys.NOTIFICATOR_TRACCAR_KEY); } @Override - public void send(User user, Event event, Position position) { + public void send(User user, NotificationMessage shortMessage, Event event, Position position) { if (user.hasAttribute("notificationTokens")) { - var shortMessage = notificationFormatter.formatMessage(user, event, position, "short"); + NotificationObject item = new NotificationObject(); + item.title = shortMessage.getSubject(); + item.body = shortMessage.getBody(); + item.sound = "default"; - Notification notification = new Notification(); - notification.title = shortMessage.getSubject(); - notification.body = shortMessage.getBody(); - notification.sound = "default"; + String[] tokenArray = user.getString("notificationTokens").split("[, ]"); + List<String> registrationTokens = new ArrayList<>(Arrays.asList(tokenArray)); Message message = new Message(); message.tokens = user.getString("notificationTokens").split("[, ]"); - message.notification = notification; + message.notification = item; - client.target(url).request().header("Authorization", "key=" + key).post(Entity.json(message)).close(); + var request = client.target(url).request().header("Authorization", "key=" + key); + try (Response result = request.post(Entity.json(message))) { + var json = result.readEntity(JsonObject.class); + List<String> failedTokens = new LinkedList<>(); + var responses = json.getJsonArray("responses"); + for (int i = 0; i < responses.size(); i++) { + var response = responses.getJsonObject(i); + if (!response.getBoolean("success")) { + var error = response.getJsonObject("error"); + String errorCode = error.getString("code"); + if (errorCode.equals("messaging/invalid-argument") + || errorCode.equals("messaging/registration-token-not-registered")) { + failedTokens.add(registrationTokens.get(i)); + } + LOGGER.warn("Push user {} error - {}", user.getId(), error.getString("message")); + } + } + if (!failedTokens.isEmpty()) { + registrationTokens.removeAll(failedTokens); + if (registrationTokens.isEmpty()) { + user.getAttributes().remove("notificationTokens"); + } else { + user.set("notificationTokens", String.join(",", registrationTokens)); + } + storage.updateObject(user, new Request( + new Columns.Include("attributes"), + new Condition.Equals("id", user.getId()))); + cacheManager.invalidateObject(true, User.class, user.getId(), ObjectOperation.UPDATE); + } + } catch (Exception e) { + LOGGER.warn("Push error", e); + } } } diff --git a/src/main/java/org/traccar/notificators/NotificatorWeb.java b/src/main/java/org/traccar/notificators/NotificatorWeb.java index deabeade1..b7bbdac1b 100644 --- a/src/main/java/org/traccar/notificators/NotificatorWeb.java +++ b/src/main/java/org/traccar/notificators/NotificatorWeb.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,29 +17,30 @@ package org.traccar.notificators; import org.traccar.model.Event; +import org.traccar.model.Notification; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.notification.NotificationFormatter; import org.traccar.session.ConnectionManager; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; @Singleton -public final class NotificatorWeb implements Notificator { +public final class NotificatorWeb extends Notificator { private final ConnectionManager connectionManager; private final NotificationFormatter notificationFormatter; @Inject - public NotificatorWeb( - ConnectionManager connectionManager, NotificationFormatter notificationFormatter) { + public NotificatorWeb(ConnectionManager connectionManager, NotificationFormatter notificationFormatter) { + super(null, null); this.connectionManager = connectionManager; this.notificationFormatter = notificationFormatter; } @Override - public void send(User user, Event event, Position position) { + public void send(Notification notification, User user, Event event, Position position) { Event copy = new Event(); copy.setId(event.getId()); @@ -51,7 +52,7 @@ public final class NotificatorWeb implements Notificator { copy.setMaintenanceId(event.getMaintenanceId()); copy.getAttributes().putAll(event.getAttributes()); - var message = notificationFormatter.formatMessage(user, event, position, "short"); + var message = notificationFormatter.formatMessage(notification, user, event, position, "short"); copy.set("message", message.getBody()); connectionManager.updateEvent(true, user.getId(), copy); diff --git a/src/main/java/org/traccar/protocol/AdmProtocol.java b/src/main/java/org/traccar/protocol/AdmProtocol.java index bab1d2339..3856dc906 100644 --- a/src/main/java/org/traccar/protocol/AdmProtocol.java +++ b/src/main/java/org/traccar/protocol/AdmProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AdmProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AisProtocol.java b/src/main/java/org/traccar/protocol/AisProtocol.java index bc975c277..e792a1d3f 100644 --- a/src/main/java/org/traccar/protocol/AisProtocol.java +++ b/src/main/java/org/traccar/protocol/AisProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AisProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AlematicsProtocol.java b/src/main/java/org/traccar/protocol/AlematicsProtocol.java index b85b44382..5219607e7 100644 --- a/src/main/java/org/traccar/protocol/AlematicsProtocol.java +++ b/src/main/java/org/traccar/protocol/AlematicsProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AlematicsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AnytrekProtocol.java b/src/main/java/org/traccar/protocol/AnytrekProtocol.java index b0e974c69..5dce15ce1 100644 --- a/src/main/java/org/traccar/protocol/AnytrekProtocol.java +++ b/src/main/java/org/traccar/protocol/AnytrekProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AnytrekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ApelProtocol.java b/src/main/java/org/traccar/protocol/ApelProtocol.java index f1d6e659c..550581b85 100644 --- a/src/main/java/org/traccar/protocol/ApelProtocol.java +++ b/src/main/java/org/traccar/protocol/ApelProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ApelProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AplicomProtocol.java b/src/main/java/org/traccar/protocol/AplicomProtocol.java index 47bb780cb..48c628d22 100644 --- a/src/main/java/org/traccar/protocol/AplicomProtocol.java +++ b/src/main/java/org/traccar/protocol/AplicomProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AplicomProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AppelloProtocol.java b/src/main/java/org/traccar/protocol/AppelloProtocol.java index 25b2bf3b8..34055d7e4 100644 --- a/src/main/java/org/traccar/protocol/AppelloProtocol.java +++ b/src/main/java/org/traccar/protocol/AppelloProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AppelloProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AquilaProtocol.java b/src/main/java/org/traccar/protocol/AquilaProtocol.java index 6080df33d..bd9c34d08 100644 --- a/src/main/java/org/traccar/protocol/AquilaProtocol.java +++ b/src/main/java/org/traccar/protocol/AquilaProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AquilaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Ardi01Protocol.java b/src/main/java/org/traccar/protocol/Ardi01Protocol.java index b33c2817f..5ddbe9d62 100644 --- a/src/main/java/org/traccar/protocol/Ardi01Protocol.java +++ b/src/main/java/org/traccar/protocol/Ardi01Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Ardi01Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ArknavProtocol.java b/src/main/java/org/traccar/protocol/ArknavProtocol.java index 4f443aa3a..20fec296c 100644 --- a/src/main/java/org/traccar/protocol/ArknavProtocol.java +++ b/src/main/java/org/traccar/protocol/ArknavProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ArknavProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java index 39c6e8009..a8be6f40c 100644 --- a/src/main/java/org/traccar/protocol/ArknavX8Protocol.java +++ b/src/main/java/org/traccar/protocol/ArknavX8Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ArknavX8Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocol.java b/src/main/java/org/traccar/protocol/ArmoliProtocol.java index 32fba3b52..e9c947ecd 100644 --- a/src/main/java/org/traccar/protocol/ArmoliProtocol.java +++ b/src/main/java/org/traccar/protocol/ArmoliProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ArmoliProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java index 091d5c06f..962bcce52 100644 --- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java +++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ArnaviProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java index 361eeeef2..50ceea898 100644 --- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java @@ -21,7 +21,7 @@ import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Protocol; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.net.SocketAddress; public class ArnaviProtocolDecoder extends BaseProtocolDecoder { diff --git a/src/main/java/org/traccar/protocol/AstraProtocol.java b/src/main/java/org/traccar/protocol/AstraProtocol.java index 021a81e07..dcc02d10d 100644 --- a/src/main/java/org/traccar/protocol/AstraProtocol.java +++ b/src/main/java/org/traccar/protocol/AstraProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AstraProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/At2000Protocol.java b/src/main/java/org/traccar/protocol/At2000Protocol.java index 25e9be86f..c7e22f142 100644 --- a/src/main/java/org/traccar/protocol/At2000Protocol.java +++ b/src/main/java/org/traccar/protocol/At2000Protocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class At2000Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AtrackProtocol.java b/src/main/java/org/traccar/protocol/AtrackProtocol.java index 21eb09696..8b86955f4 100644 --- a/src/main/java/org/traccar/protocol/AtrackProtocol.java +++ b/src/main/java/org/traccar/protocol/AtrackProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AtrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java index 340641729..8896dcfb0 100644 --- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java @@ -429,6 +429,208 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { case "MP": buf.readUnsignedByte(); // manifold absolute pressure break; + case "EO": + position.set(Position.KEY_ODOMETER, UnitsConverter.metersFromMiles(buf.readUnsignedInt())); + break; + case "EH": + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 360000); + break; + case "ZO1": + buf.readUnsignedByte(); // brake stroke status + break; + case "ZO2": + buf.readUnsignedByte(); // warning indicator status + break; + case "ZO3": + buf.readUnsignedByte(); // abs control status + break; + case "ZO4": + position.set(Position.KEY_THROTTLE, buf.readUnsignedByte() * 0.4); + break; + case "ZO5": + buf.readUnsignedByte(); // parking brake status + break; + case "ZO6": + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte() * 0.805); + break; + case "ZO7": + buf.readUnsignedByte(); // cruise control status + break; + case "ZO8": + buf.readUnsignedByte(); // accelector pedal position + break; + case "ZO9": + position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte() * 0.5); + break; + case "ZO10": + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.5); + break; + case "ZO11": + buf.readUnsignedByte(); // engine oil pressure + break; + case "ZO12": + buf.readUnsignedByte(); // boost pressure + break; + case "ZO13": + buf.readUnsignedByte(); // intake temperature + break; + case "ZO14": + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte()); + break; + case "ZO15": + buf.readUnsignedByte(); // brake application pressure + break; + case "ZO16": + buf.readUnsignedByte(); // brake primary pressure + break; + case "ZO17": + buf.readUnsignedByte(); // brake secondary pressure + break; + case "ZH1": + buf.readUnsignedShort(); // cargo weight + break; + case "ZH2": + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 16.428 / 3600); + break; + case "ZH3": + position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.25); + break; + case "ZL1": + buf.readUnsignedInt(); // fuel used (natural gas) + break; + case "ZL2": + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 161); + break; + case "ZL3": + buf.readUnsignedInt(); // vehicle hours + break; + case "ZL4": + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 5 * 36000); + break; + case "ZS1": + position.set(Position.KEY_VIN, readString(buf)); + break; + case "JO1": + buf.readUnsignedByte(); // pedals + break; + case "JO2": + buf.readUnsignedByte(); // power takeoff device + break; + case "JO3": + buf.readUnsignedByte(); // accelector pedal position + break; + case "JO4": + position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte()); + break; + case "JO5": + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4); + break; + case "JO6": + buf.readUnsignedByte(); // fms vehicle interface + break; + case "JO7": + buf.readUnsignedByte(); // driver 2 + break; + case "JO8": + buf.readUnsignedByte(); // driver 1 + break; + case "JO9": + buf.readUnsignedByte(); // drivers + break; + case "JO10": + buf.readUnsignedByte(); // system information + break; + case "JO11": + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40); + break; + case "JO12": + buf.readUnsignedByte(); // pto engaged + break; + case "JH1": + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() / 256.0); + break; + case "JH2": + position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125); + break; + case "JH3": + case "JH4": + case "JH5": + case "JH6": + case "JH7": + int index = Integer.parseInt(key.substring(2)) - 2; + position.set("axleWeight" + index, buf.readUnsignedShort() * 0.5); + break; + case "JH8": + position.set(Position.KEY_ODOMETER_SERVICE, buf.readUnsignedShort() * 5); + break; + case "JH9": + buf.readUnsignedShort(); // tachograph speed + break; + case "JH10": + buf.readUnsignedShort(); // ambient air temperature + break; + case "JH11": + position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.05); + break; + case "JH12": + buf.readUnsignedShort(); // fuel economy + break; + case "JL1": + position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.5); + break; + case "JL2": + position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 5 * 36000); + break; + case "JL3": + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000); + break; + case "JL4": + position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.001); + break; + case "JS1": + position.set(Position.KEY_VIN, readString(buf)); + break; + case "JS2": + readString(buf); // fms version supported + break; + case "JS3": + position.set("driver1", readString(buf)); + break; + case "JS4": + position.set("driver2", readString(buf)); + break; + case "JN1": + buf.readUnsignedInt(); // cruise control distance + break; + case "JN2": + buf.readUnsignedInt(); // excessive idling time + break; + case "JN3": + buf.readUnsignedInt(); // excessive idling fuel + break; + case "JN4": + buf.readUnsignedInt(); // pto time + break; + case "JN5": + buf.readUnsignedInt(); // pto fuel + break; + case "IN0": + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + break; + case "IN1": + case "IN2": + case "IN3": + position.set(Position.PREFIX_IN + key.charAt(2), buf.readUnsignedByte() > 0); + break; + case "HA": + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_ACCELERATION : null); + break; + case "HB": + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_BRAKING : null); + break; + case "HC": + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_CORNERING : null); + break; default: break; } diff --git a/src/main/java/org/traccar/protocol/AuroProtocol.java b/src/main/java/org/traccar/protocol/AuroProtocol.java index d37884c8b..728c8e23c 100644 --- a/src/main/java/org/traccar/protocol/AuroProtocol.java +++ b/src/main/java/org/traccar/protocol/AuroProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AuroProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AustinNbProtocol.java b/src/main/java/org/traccar/protocol/AustinNbProtocol.java index 6a68467e2..467deff53 100644 --- a/src/main/java/org/traccar/protocol/AustinNbProtocol.java +++ b/src/main/java/org/traccar/protocol/AustinNbProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AustinNbProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AutoFonProtocol.java b/src/main/java/org/traccar/protocol/AutoFonProtocol.java index 0566b1da6..75bd28d5e 100644 --- a/src/main/java/org/traccar/protocol/AutoFonProtocol.java +++ b/src/main/java/org/traccar/protocol/AutoFonProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AutoFonProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java index bc80e473a..69d9fb4e6 100644 --- a/src/main/java/org/traccar/protocol/AutoGradeProtocol.java +++ b/src/main/java/org/traccar/protocol/AutoGradeProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AutoGradeProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java index 80255d3e9..df489de3c 100644 --- a/src/main/java/org/traccar/protocol/AutoTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/AutoTrackProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AutoTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/AvemaProtocol.java b/src/main/java/org/traccar/protocol/AvemaProtocol.java index b35a447ff..0eeee41b8 100644 --- a/src/main/java/org/traccar/protocol/AvemaProtocol.java +++ b/src/main/java/org/traccar/protocol/AvemaProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class AvemaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Avl301Protocol.java b/src/main/java/org/traccar/protocol/Avl301Protocol.java index c4a0affdc..452bc1501 100644 --- a/src/main/java/org/traccar/protocol/Avl301Protocol.java +++ b/src/main/java/org/traccar/protocol/Avl301Protocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Avl301Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/B2316Protocol.java b/src/main/java/org/traccar/protocol/B2316Protocol.java index 582be0b56..e0a6821d8 100644 --- a/src/main/java/org/traccar/protocol/B2316Protocol.java +++ b/src/main/java/org/traccar/protocol/B2316Protocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class B2316Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java index 635806b2d..b0a5411f7 100644 --- a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java @@ -24,9 +24,9 @@ import org.traccar.model.Network; import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.util.Date; diff --git a/src/main/java/org/traccar/protocol/BceProtocol.java b/src/main/java/org/traccar/protocol/BceProtocol.java index 31fb1bd83..5e1c10abc 100644 --- a/src/main/java/org/traccar/protocol/BceProtocol.java +++ b/src/main/java/org/traccar/protocol/BceProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class BceProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java index 3859a9273..d584af5a1 100644 --- a/src/main/java/org/traccar/protocol/BlackKiteProtocol.java +++ b/src/main/java/org/traccar/protocol/BlackKiteProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class BlackKiteProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java index da195f438..821111ad7 100644 --- a/src/main/java/org/traccar/protocol/BlueProtocol.java +++ b/src/main/java/org/traccar/protocol/BlueProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class BlueProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/BoxProtocol.java b/src/main/java/org/traccar/protocol/BoxProtocol.java index dc6852d50..ac1ba7cae 100644 --- a/src/main/java/org/traccar/protocol/BoxProtocol.java +++ b/src/main/java/org/traccar/protocol/BoxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class BoxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/BstplProtocol.java b/src/main/java/org/traccar/protocol/BstplProtocol.java index dde14a2ca..ccedcf3d9 100644 --- a/src/main/java/org/traccar/protocol/BstplProtocol.java +++ b/src/main/java/org/traccar/protocol/BstplProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class BstplProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/C2stekProtocol.java b/src/main/java/org/traccar/protocol/C2stekProtocol.java index 5cd8ef4fd..5370ea762 100644 --- a/src/main/java/org/traccar/protocol/C2stekProtocol.java +++ b/src/main/java/org/traccar/protocol/C2stekProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class C2stekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CalAmpProtocol.java b/src/main/java/org/traccar/protocol/CalAmpProtocol.java index d67308cf2..06df6e196 100644 --- a/src/main/java/org/traccar/protocol/CalAmpProtocol.java +++ b/src/main/java/org/traccar/protocol/CalAmpProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CalAmpProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CarTrackProtocol.java b/src/main/java/org/traccar/protocol/CarTrackProtocol.java index 0538aad72..366f32034 100644 --- a/src/main/java/org/traccar/protocol/CarTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/CarTrackProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CarTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CarcellProtocol.java b/src/main/java/org/traccar/protocol/CarcellProtocol.java index 832d9bb2d..7ae8159d5 100644 --- a/src/main/java/org/traccar/protocol/CarcellProtocol.java +++ b/src/main/java/org/traccar/protocol/CarcellProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CarcellProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CarscopProtocol.java b/src/main/java/org/traccar/protocol/CarscopProtocol.java index a4413af28..e904c01c5 100644 --- a/src/main/java/org/traccar/protocol/CarscopProtocol.java +++ b/src/main/java/org/traccar/protocol/CarscopProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CarscopProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CastelProtocol.java b/src/main/java/org/traccar/protocol/CastelProtocol.java index 9323b1503..74a9e9ca1 100644 --- a/src/main/java/org/traccar/protocol/CastelProtocol.java +++ b/src/main/java/org/traccar/protocol/CastelProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import org.traccar.model.Command; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CastelProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java index 4aa65245b..b076b9f66 100644 --- a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java @@ -16,7 +16,6 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; @@ -284,15 +283,27 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { case 0x0C: position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); break; + case 0x0D: + position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING); + break; case 0x0E: position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF); break; + case 0x11: + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + break; + case 0x12: + position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); + break; case 0x16: position.set(Position.KEY_IGNITION, true); break; case 0x17: position.set(Position.KEY_IGNITION, false); break; + case 0x1C: + position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); + break; default: break; } @@ -359,9 +370,9 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { int alarmCount = buf.readUnsignedByte(); for (int i = 0; i < alarmCount; i++) { if (buf.readUnsignedByte() != 0) { - int alarm = buf.readUnsignedByte(); + int event = buf.readUnsignedByte(); for (Position p : positions) { - decodeAlarm(p, alarm); + decodeAlarm(p, event); } buf.readUnsignedShortLE(); // description buf.readUnsignedShortLE(); // threshold @@ -421,12 +432,26 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { return position; case MSG_SC_DTCS_PASSENGER: + case MSG_SC_DTCS_COMMERCIAL: position = createPosition(deviceSession); decodeStat(position, buf); buf.readUnsignedByte(); // flag - position.add(ObdDecoder.decodeCodes(ByteBufUtil.hexDump(buf.readSlice(buf.readUnsignedByte())))); + + count = buf.readUnsignedByte(); + StringBuilder codes = new StringBuilder(); + for (int i = 0; i < count; i++) { + if (type == MSG_SC_DTCS_COMMERCIAL) { + codes.append(ObdDecoder.decodeCode(buf.readUnsignedShortLE())); + buf.readUnsignedByte(); // attribute + buf.readUnsignedByte(); // occurrence + } else { + codes.append(ObdDecoder.decodeCode(buf.readUnsignedShortLE())); + } + codes.append(' '); + } + position.set(Position.KEY_DTCS, codes.toString().trim()); return position; diff --git a/src/main/java/org/traccar/protocol/CautelaProtocol.java b/src/main/java/org/traccar/protocol/CautelaProtocol.java index d0ca35ef1..067345f49 100644 --- a/src/main/java/org/traccar/protocol/CautelaProtocol.java +++ b/src/main/java/org/traccar/protocol/CautelaProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CautelaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocol.java b/src/main/java/org/traccar/protocol/CellocatorProtocol.java index 3287928c7..e3325c8b7 100644 --- a/src/main/java/org/traccar/protocol/CellocatorProtocol.java +++ b/src/main/java/org/traccar/protocol/CellocatorProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CellocatorProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CguardProtocol.java b/src/main/java/org/traccar/protocol/CguardProtocol.java index caf0aad42..c0fc9582a 100644 --- a/src/main/java/org/traccar/protocol/CguardProtocol.java +++ b/src/main/java/org/traccar/protocol/CguardProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CguardProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CityeasyProtocol.java b/src/main/java/org/traccar/protocol/CityeasyProtocol.java index 9656b284b..60ed5d135 100644 --- a/src/main/java/org/traccar/protocol/CityeasyProtocol.java +++ b/src/main/java/org/traccar/protocol/CityeasyProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CityeasyProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ContinentalProtocol.java b/src/main/java/org/traccar/protocol/ContinentalProtocol.java index 06e93d79d..9567374fd 100644 --- a/src/main/java/org/traccar/protocol/ContinentalProtocol.java +++ b/src/main/java/org/traccar/protocol/ContinentalProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ContinentalProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/CradlepointProtocol.java b/src/main/java/org/traccar/protocol/CradlepointProtocol.java index 7f201a31d..220db0747 100644 --- a/src/main/java/org/traccar/protocol/CradlepointProtocol.java +++ b/src/main/java/org/traccar/protocol/CradlepointProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class CradlepointProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DingtekProtocol.java b/src/main/java/org/traccar/protocol/DingtekProtocol.java index e9466b7e8..ab3e32fdb 100644 --- a/src/main/java/org/traccar/protocol/DingtekProtocol.java +++ b/src/main/java/org/traccar/protocol/DingtekProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DingtekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DishaProtocol.java b/src/main/java/org/traccar/protocol/DishaProtocol.java index f83b8349a..0a582731d 100644 --- a/src/main/java/org/traccar/protocol/DishaProtocol.java +++ b/src/main/java/org/traccar/protocol/DishaProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DishaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java index 0dab26cda..d15bfa1ca 100644 --- a/src/main/java/org/traccar/protocol/DmtHttpProtocol.java +++ b/src/main/java/org/traccar/protocol/DmtHttpProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DmtHttpProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java index 807850778..c2e617a2a 100644 --- a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java @@ -25,9 +25,9 @@ import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/DmtProtocol.java b/src/main/java/org/traccar/protocol/DmtProtocol.java index de56c9372..e89920cd3 100644 --- a/src/main/java/org/traccar/protocol/DmtProtocol.java +++ b/src/main/java/org/traccar/protocol/DmtProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DmtProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DolphinProtocol.java b/src/main/java/org/traccar/protocol/DolphinProtocol.java index ed627be78..e2acce7dd 100644 --- a/src/main/java/org/traccar/protocol/DolphinProtocol.java +++ b/src/main/java/org/traccar/protocol/DolphinProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DolphinProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DraginoProtocol.java b/src/main/java/org/traccar/protocol/DraginoProtocol.java new file mode 100644 index 000000000..d33efe2ad --- /dev/null +++ b/src/main/java/org/traccar/protocol/DraginoProtocol.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class DraginoProtocol extends BaseProtocol { + + @Inject + public DraginoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new DraginoProtocolDecoder(DraginoProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java b/src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java new file mode 100644 index 000000000..5f576d723 --- /dev/null +++ b/src/main/java/org/traccar/protocol/DraginoProtocolDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.DateUtil; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; + +public class DraginoProtocolDecoder extends BaseHttpProtocolDecoder { + + public DraginoProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + String content = request.content().toString(StandardCharsets.UTF_8); + JsonObject json = Json.createReader(new StringReader(content)).readObject(); + + String deviceId = json.getJsonObject("end_device_ids").getString("device_id"); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + JsonObject message = json.getJsonObject("uplink_message"); + JsonObject decoded = message.getJsonObject("decoded_payload"); + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setTime(DateUtil.parseDate(message.getString("received_at"))); + + position.setValid(true); + position.setLatitude(decoded.getJsonNumber("Latitude").doubleValue()); + position.setLongitude(decoded.getJsonNumber("Longitude").doubleValue()); + + position.set("humidity", decoded.getJsonNumber("Hum").doubleValue()); + position.set(Position.KEY_BATTERY, decoded.getJsonNumber("BatV").doubleValue()); + position.set(Position.PREFIX_TEMP + 1, decoded.getJsonNumber("Tem").doubleValue()); + + if (Boolean.parseBoolean(decoded.getString("ALARM_status"))) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } + + sendResponse(channel, HttpResponseStatus.OK); + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/Dsf22Protocol.java b/src/main/java/org/traccar/protocol/Dsf22Protocol.java index 06c99b0f9..ad349a7ff 100644 --- a/src/main/java/org/traccar/protocol/Dsf22Protocol.java +++ b/src/main/java/org/traccar/protocol/Dsf22Protocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Dsf22Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DualcamProtocol.java b/src/main/java/org/traccar/protocol/DualcamProtocol.java index 363a2c5d9..4725cb180 100644 --- a/src/main/java/org/traccar/protocol/DualcamProtocol.java +++ b/src/main/java/org/traccar/protocol/DualcamProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DualcamProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java index d03f7648d..411e2b9d7 100644 --- a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,13 +42,24 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_COMPLETE = 5; public static final int MSG_FILE_REQUEST = 8; public static final int MSG_INIT_REQUEST = 9; + public static final int MSG_PATH_REQUEST = 0x000C; + public static final int MSG_PATH_RESPONSE = 0x000D; + private String model; private String uniqueId; - private int packetCount; - private int currentPacket; + private int dataSize; + private int dataCurrent; private boolean video; private ByteBuf media; + private boolean isPacketData() { + if (model == null) { + return dataSize < 8192; + } else { + return !"DSM".equals(model); + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -57,15 +68,21 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { int type = buf.readUnsignedShort(); + DeviceSession deviceSession; switch (type) { case MSG_INIT: buf.readUnsignedShort(); // protocol id uniqueId = String.valueOf(buf.readLong()); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, uniqueId); + deviceSession = getDeviceSession(channel, remoteAddress, uniqueId); long settings = buf.readUnsignedInt(); if (channel != null && deviceSession != null) { + model = getDeviceModel(deviceSession); ByteBuf response = Unpooled.buffer(); - if (BitUtil.between(settings, 26, 30) > 0) { + if (BitUtil.check(settings, 25)) { + response.writeShort(MSG_PATH_REQUEST); + response.writeShort(2); + response.writeShort(0); + } else if (BitUtil.between(settings, 26, 30) > 0) { response.writeShort(MSG_FILE_REQUEST); String file; if (BitUtil.check(settings, 26)) { @@ -91,21 +108,29 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { break; case MSG_START: buf.readUnsignedShort(); // length - packetCount = buf.readInt(); - currentPacket = 1; + dataSize = buf.readInt(); + dataCurrent = isPacketData() ? 1 : 0; media = Unpooled.buffer(); if (channel != null) { ByteBuf response = Unpooled.buffer(); response.writeShort(MSG_RESUME); response.writeShort(4); - response.writeInt(currentPacket); + response.writeInt(dataCurrent); channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } break; case MSG_DATA: - buf.readUnsignedShort(); // length - media.writeBytes(buf, buf.readableBytes() - 2); - if (currentPacket == packetCount) { + int length = buf.readUnsignedShort() - 2; + media.writeBytes(buf, length); + boolean finished; + if (isPacketData()) { + finished = dataCurrent == dataSize; + dataCurrent += 1; + } else { + finished = dataCurrent + length == dataSize; + dataCurrent += length; + } + if (finished) { deviceSession = getDeviceSession(channel, remoteAddress); Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -126,8 +151,16 @@ public class DualcamProtocolDecoder extends BaseProtocolDecoder { channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } return position; - } else { - currentPacket += 1; + } + break; + case MSG_PATH_RESPONSE: + String file = buf.readCharSequence(buf.readUnsignedShort(), StandardCharsets.US_ASCII).toString(); + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeShort(MSG_FILE_REQUEST); + response.writeShort(file.length()); + response.writeCharSequence(file, StandardCharsets.US_ASCII); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } break; default: diff --git a/src/main/java/org/traccar/protocol/DwayProtocol.java b/src/main/java/org/traccar/protocol/DwayProtocol.java index 1096c945c..2ba1cf5f1 100644 --- a/src/main/java/org/traccar/protocol/DwayProtocol.java +++ b/src/main/java/org/traccar/protocol/DwayProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class DwayProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java index 39aa61580..25d4ef9a0 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EasyTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java index 805cf1197..b10ff4c64 100644 --- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,26 +79,45 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private static final Pattern PATTERN_OBD = new PatternBuilder() + .text("*").expression("..,") // manufacturer + .number("(d+),") // imei + .text("OB,") // command + .text("BD$") + .number("V(d+.d);") // battery + .number("R(d+);") // rpm + .number("S(d+);") // speed + .number("P(d+.d);") // throttle + .number("O(d+.d);") // engine load + .number("C(d+);") // coolant temperature + .number("L(d+.d);") // fuel level + .number("[XY][MH]d+.d+;") + .number("M(d+);") // mileage + .number("F(d+.d+);") // fuel consumption + .number("T(d+);") // engine time + .any() + .compile(); + private String decodeAlarm(long status) { - if ((status & 0x02000000) != 0) { + if ((status & 0x02000000L) != 0) { return Position.ALARM_GEOFENCE_ENTER; } - if ((status & 0x04000000) != 0) { + if ((status & 0x04000000L) != 0) { return Position.ALARM_GEOFENCE_EXIT; } - if ((status & 0x08000000) != 0) { + if ((status & 0x08000000L) != 0) { return Position.ALARM_LOW_BATTERY; } - if ((status & 0x20000000) != 0) { + if ((status & 0x20000000L) != 0) { return Position.ALARM_VIBRATION; } - if ((status & 0x80000000) != 0) { + if ((status & 0x80000000L) != 0) { return Position.ALARM_OVERSPEED; } - if ((status & 0x00010000) != 0) { + if ((status & 0x00010000L) != 0) { return Position.ALARM_SOS; } - if ((status & 0x00040000) != 0) { + if ((status & 0x00040000L) != 0) { return Position.ALARM_POWER_CUT; } return null; @@ -115,7 +134,9 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { channel.writeAndFlush(new NetworkMessage(sentence + "#", remoteAddress)); } - if (type.equals("JZ")) { + if (type.equals("OB")) { + return decodeObd(channel, remoteAddress, sentence); + } else if (type.equals("JZ")) { return decodeCell(channel, remoteAddress, sentence); } else { return decodeLocation(channel, remoteAddress, sentence); @@ -219,4 +240,35 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_OBD, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + position.set(Position.KEY_BATTERY, parser.nextDouble()); + position.set(Position.KEY_RPM, parser.nextInt()); + position.set(Position.KEY_OBD_SPEED, parser.nextInt()); + position.set(Position.KEY_THROTTLE, parser.nextDouble()); + position.set(Position.KEY_ENGINE_LOAD, parser.nextDouble()); + position.set(Position.KEY_COOLANT_TEMP, parser.nextInt()); + position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble()); + position.set(Position.KEY_ODOMETER, parser.nextInt()); + position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextDouble()); + position.set(Position.KEY_HOURS, parser.nextInt()); + + return position; + } + } diff --git a/src/main/java/org/traccar/protocol/EelinkProtocol.java b/src/main/java/org/traccar/protocol/EelinkProtocol.java index 35fd4fe65..2a3c0bd15 100644 --- a/src/main/java/org/traccar/protocol/EelinkProtocol.java +++ b/src/main/java/org/traccar/protocol/EelinkProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EelinkProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java index cb0e10042..db1b365c3 100644 --- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java @@ -314,7 +314,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { } if (buf.readableBytes() >= 12) { - position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0); + position.set(Position.PREFIX_TEMP + 1, buf.readShort() / 256.0); position.set("humidity", buf.readUnsignedShort() * 0.1); position.set("illuminance", buf.readUnsignedInt() / 256.0); position.set("co2", buf.readUnsignedInt()); diff --git a/src/main/java/org/traccar/protocol/EgtsProtocol.java b/src/main/java/org/traccar/protocol/EgtsProtocol.java index f257271d4..5450d9f01 100644 --- a/src/main/java/org/traccar/protocol/EgtsProtocol.java +++ b/src/main/java/org/traccar/protocol/EgtsProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EgtsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EnforaProtocol.java b/src/main/java/org/traccar/protocol/EnforaProtocol.java index ebde56f70..3b796db25 100644 --- a/src/main/java/org/traccar/protocol/EnforaProtocol.java +++ b/src/main/java/org/traccar/protocol/EnforaProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EnforaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EnnfuProtocol.java b/src/main/java/org/traccar/protocol/EnnfuProtocol.java index e326481fa..a3ff943fa 100644 --- a/src/main/java/org/traccar/protocol/EnnfuProtocol.java +++ b/src/main/java/org/traccar/protocol/EnnfuProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EnnfuProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EnvotechProtocol.java b/src/main/java/org/traccar/protocol/EnvotechProtocol.java index dffa1c991..e432ac07c 100644 --- a/src/main/java/org/traccar/protocol/EnvotechProtocol.java +++ b/src/main/java/org/traccar/protocol/EnvotechProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EnvotechProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EsealProtocol.java b/src/main/java/org/traccar/protocol/EsealProtocol.java index 0ed80dc6f..eae4cf2aa 100644 --- a/src/main/java/org/traccar/protocol/EsealProtocol.java +++ b/src/main/java/org/traccar/protocol/EsealProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EsealProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java index cb2f59dc8..002e268ba 100644 --- a/src/main/java/org/traccar/protocol/EskyProtocol.java +++ b/src/main/java/org/traccar/protocol/EskyProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class EskyProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java index ffc941b69..23a993fe4 100644 --- a/src/main/java/org/traccar/protocol/ExtremTracProtocol.java +++ b/src/main/java/org/traccar/protocol/ExtremTracProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ExtremTracProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocol.java b/src/main/java/org/traccar/protocol/FifotrackProtocol.java index fd2beaabb..e98ad84cc 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocol.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FifotrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index a9d77b46e..59019830f 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -64,7 +64,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // course .number("(-?d+),") // altitude .number("(d+),") // odometer - .number("d+,") // runtime + .number("(d+),") // engine hours .number("(x+),") // status .number("(x+)?,") // input .number("(x+)?,") // output @@ -183,6 +183,8 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { case 30: case 32: return Position.ALARM_JAMMING; + case 31: + return Position.ALARM_FALL_DOWN; case 33: return Position.ALARM_GEOFENCE_EXIT; case 34: @@ -192,6 +194,10 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { case 40: case 41: return Position.ALARM_TEMPERATURE; + case 53: + return Position.ALARM_POWER_ON; + case 54: + return Position.ALARM_POWER_OFF; default: return null; } @@ -235,13 +241,15 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setValid(parser.next().equals("A")); position.setFixTime(position.getDeviceTime()); - position.set(Position.KEY_SATELLITES, parser.nextInt()); position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt())); + position.set(Position.KEY_SATELLITES, parser.nextInt()); position.setLatitude(parser.nextDouble()); position.setLongitude(parser.nextDouble()); } else { + getLastLocation(position, position.getDeviceTime()); + String[] points = parser.next().split("\\|"); for (String point : points) { String[] wifi = point.split(":"); @@ -290,6 +298,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextInt()); position.set(Position.KEY_ODOMETER, parser.nextLong()); + position.set(Position.KEY_HOURS, parser.nextLong() * 1000); long status = parser.nextHexLong(); position.set(Position.KEY_RSSI, BitUtil.between(status, 3, 8)); @@ -308,11 +317,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { - String rfid = parser.next(); - if (rfid.matches("\\p{XDigit}+")) { - position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(rfid, 16))); + String value = parser.next(); + if (value.matches("\\p{XDigit}+")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(value, 16))); } else { - position.set("driverLicense", rfid); + position.set(Position.KEY_CARD, value); } } diff --git a/src/main/java/org/traccar/protocol/FleetGuideProtocol.java b/src/main/java/org/traccar/protocol/FleetGuideProtocol.java new file mode 100644 index 000000000..46611c25c --- /dev/null +++ b/src/main/java/org/traccar/protocol/FleetGuideProtocol.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class FleetGuideProtocol extends BaseProtocol { + + @Inject + public FleetGuideProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new FleetGuideProtocolDecoder(FleetGuideProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java b/src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java new file mode 100644 index 000000000..8f679525b --- /dev/null +++ b/src/main/java/org/traccar/protocol/FleetGuideProtocolDecoder.java @@ -0,0 +1,328 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class FleetGuideProtocolDecoder extends BaseProtocolDecoder { + + public FleetGuideProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int MSG_EMPTY = 0; + public static final int MSG_SYNC_REQ = 1; + public static final int MSG_SYNC_ACK = 2; + public static final int MSG_DATA_R_ACK = 3; + public static final int MSG_DATA_N_ACK = 4; + public static final int MSG_REP_R_ACK = 5; + public static final int MSG_REP_N_ACK = 6; + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.readUnsignedByte(); // signature + int options = buf.readUnsignedShortLE(); + int length = BitUtil.to(options, 11); + + DeviceSession deviceSession; + Long deviceId; + if (BitUtil.check(options, 11)) { + deviceId = buf.readUnsignedIntLE(); + deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId)); + } else { + deviceId = null; + deviceSession = getDeviceSession(channel, remoteAddress); + } + if (deviceSession == null) { + return null; + } + + int type; + Integer index; + if (BitUtil.check(options, 12)) { + int value = buf.readUnsignedByte(); + type = BitUtil.to(value, 4); + index = BitUtil.from(value, 4); + } else { + type = 0; + index = null; + } + + if (type != MSG_DATA_N_ACK && type != MSG_REP_N_ACK) { + Integer responseType; + if (type == MSG_SYNC_REQ) { + responseType = MSG_SYNC_ACK; + } else { + responseType = null; + } + sendResponse(channel, remoteAddress, deviceId, responseType, index); + } + + if (BitUtil.check(options, 13)) { + buf.readUnsignedShortLE(); // acknowledgement + } + + ByteBuf data; + if (BitUtil.check(options, 14)) { + data = decompress(buf.readSlice(length)); + } else { + data = buf.readRetainedSlice(length); + } + + List<Position> positions = new LinkedList<>(); + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + while (data.isReadable()) { + + int recordHeader = data.readUnsignedShortLE(); + int recordLength = BitUtil.to(recordHeader, 10); + int recordType = BitUtil.from(recordHeader, 10); + int recordEndIndex = data.readerIndex() + recordLength; + + if (recordType == 0 && position.getDeviceTime() != null) { + processPosition(positions, position); + position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + } + + switch (recordType) { + case 0: + position.setTime(new Date((data.readUnsignedIntLE() + 1262304000) * 1000)); // since 2010-01-01 + break; + case 1: + position.setLatitude(data.readUnsignedIntLE() * 90.0 / 0xFFFFFFFFL); + position.setLongitude(data.readUnsignedIntLE() * 180.0 / 0xFFFFFFFFL); + int speed = data.readUnsignedShortLE(); + position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speed, 14) * 0.1)); + if (BitUtil.check(speed, 14)) { + position.setLatitude(-position.getLatitude()); + } + if (BitUtil.check(speed, 15)) { + position.setLongitude(-position.getLongitude()); + } + int course = data.readUnsignedShortLE(); + position.setSpeed(BitUtil.to(course, 9)); + int motion = BitUtil.between(course, 9, 11); + if (motion > 0) { + position.set(Position.KEY_MOTION, motion == 1); + } + position.set(Position.KEY_SATELLITES, BitUtil.from(course, 11)); + int altitude = data.readUnsignedShortLE(); + position.setAltitude(BitUtil.to(altitude, 14)); + if (BitUtil.check(altitude, 14)) { + position.setAltitude(-position.getAltitude()); + } + break; + case 3: + int powerLow = data.readUnsignedByte(); + int powerFlags = data.readUnsignedByte(); + int batteryHigh = data.readUnsignedByte(); + position.set(Position.KEY_POWER, (powerLow + (BitUtil.to(powerFlags, 5) << 8)) * 0.01); + position.set(Position.KEY_IGNITION, BitUtil.check(powerFlags, 5)); + position.set(Position.KEY_BATTERY, (BitUtil.from(powerFlags, 6) + (batteryHigh << 2)) * 0.01); + if (recordLength >= 4) { + int extraFlags = data.readUnsignedByte(); + if (BitUtil.check(extraFlags, 0)) { + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + } + if (BitUtil.check(extraFlags, 1)) { + position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); + } + } + break; + case 6: + position.set(Position.KEY_INPUT, data.readUnsignedByte()); + break; + case 7: + position.set(Position.KEY_OUTPUT, data.readUnsignedByte()); + break; + case 8: + int adcMask = data.readUnsignedByte(); + for (int i = 0; i < 8; i++) { + if (BitUtil.check(adcMask, i)) { + position.set(Position.PREFIX_ADC + (i + 1), data.readUnsignedShortLE()); + } + } + break; + case 11: + int fuelMask = data.readUnsignedByte(); + for (int i = 1; i < 8; i++) { + if (BitUtil.check(fuelMask, i)) { + position.set("fuel" + i, data.readUnsignedShortLE()); + } + } + break; + case 12: + int fuelTempMask = data.readUnsignedByte(); + for (int i = 1; i < 8; i++) { + if (BitUtil.check(fuelTempMask, i)) { + position.set("fuelTemp" + i, (int) data.readByte()); + } + } + break; + case 13: + int tempMask = data.readUnsignedByte(); + for (int i = 0; i < 8; i++) { + if (BitUtil.check(tempMask, i)) { + position.set(Position.PREFIX_TEMP + (i + 1), data.readShortLE() * 0.01); + } + } + break; + case 18: + int sensorIndex = data.readUnsignedByte(); + switch (recordLength - 1) { + case 1: + position.set("sensor" + sensorIndex, data.readUnsignedByte()); + break; + case 2: + position.set("sensor" + sensorIndex, data.readUnsignedShortLE()); + break; + case 4: + position.set("sensor" + sensorIndex, data.readUnsignedIntLE()); + break; + default: + break; + } + break; + default: + break; + } + + data.readerIndex(recordEndIndex); + + } + + processPosition(positions, position); + + data.release(); + + return positions.isEmpty() ? null : positions; + } + + private void processPosition(List<Position> positions, Position position) { + if (!position.getAttributes().isEmpty()) { + if (position.getFixTime() == null) { + position.setTime(new Date()); + } + if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) { + getLastLocation(position, null); + } + positions.add(position); + } + } + + + private void sendResponse( + Channel channel, SocketAddress remoteAddress, Long deviceId, Integer type, Integer index) { + if (channel != null) { + + ByteBuf response = Unpooled.buffer(); + response.writeByte(0x53); // signature + + int options = 0; + if (deviceId != null) { + options |= 1 << 11; + } + if (type != null) { + options |= 1 << 12; + } + if (index != null) { + options |= 1 << 13; + } + response.writeShortLE(options); + + if (deviceId != null) { + response.writeIntLE(deviceId.intValue()); + } + if (type != null) { + response.writeByte(type); + } + if (index != null) { + int mask = (1 << (index + 1)) - 1; + response.writeShortLE(mask); + } + response.writeShortLE(Checksum.crc16( + Checksum.CRC16_CCITT_FALSE, response.nioBuffer(1, response.writerIndex() - 1))); + + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + } + + private int readVarSize(ByteBuf buf) { + int b; + int y = 0; + do { + b = buf.readUnsignedByte(); + y = (y << 7) | (b & 0x0000007f); + } while ((b & 0x00000080) > 0); + + return y; + } + + private ByteBuf decompress(ByteBuf in) { + + ByteBuf out = Unpooled.buffer(); + + if (in.readableBytes() < 1) { + return out; + } + + int marker = in.readUnsignedByte(); + + do { + int symbol = in.readUnsignedByte(); + if (symbol == marker) { + if (in.getUnsignedByte(in.readerIndex()) == 0) { + out.writeByte(marker); + in.skipBytes(1); + } else { + int length = readVarSize(in); + int offset = readVarSize(in); + + for (int i = 0; i < length; i++) { + out.writeByte(out.getUnsignedByte(out.writerIndex() - offset)); + } + } + } else { + out.writeByte(symbol); + } + } while (in.isReadable()); + + return out; + } + +} diff --git a/src/main/java/org/traccar/protocol/FlespiProtocol.java b/src/main/java/org/traccar/protocol/FlespiProtocol.java index 374cf77e2..0d8448845 100644 --- a/src/main/java/org/traccar/protocol/FlespiProtocol.java +++ b/src/main/java/org/traccar/protocol/FlespiProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FlespiProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java index 6e6f9c700..ea076afd8 100644 --- a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java @@ -24,12 +24,12 @@ import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; @@ -125,12 +125,13 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { position.set(Position.KEY_PDOP, ((JsonNumber) value).doubleValue()); return true; case "din": + position.set(Position.KEY_INPUT, ((JsonNumber) value).intValue()); + return true; case "dout": - if (name.equals("din")) { - position.set(Position.KEY_INPUT, ((JsonNumber) value).intValue()); - } else { - position.set(Position.KEY_OUTPUT, ((JsonNumber) value).intValue()); - } + position.set(Position.KEY_OUTPUT, ((JsonNumber) value).intValue()); + return true; + case "report.reason": + position.set(Position.KEY_EVENT, ((JsonNumber) value).intValue()); return true; case "gps.vehicle.mileage": position.set(Position.KEY_ODOMETER, ((JsonNumber) value).doubleValue()); @@ -141,6 +142,9 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { case "battery.voltage": position.set(Position.KEY_BATTERY, ((JsonNumber) value).doubleValue()); return true; + case "battery.level": + position.set(Position.KEY_BATTERY_LEVEL, ((JsonNumber) value).intValue()); + return true; case "fuel.level": case "can.fuel.level": position.set(Position.KEY_FUEL_LEVEL, ((JsonNumber) value).doubleValue()); diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocol.java b/src/main/java/org/traccar/protocol/FlexApiProtocol.java index 088072d2d..b2e3f5d00 100644 --- a/src/main/java/org/traccar/protocol/FlexApiProtocol.java +++ b/src/main/java/org/traccar/protocol/FlexApiProtocol.java @@ -24,7 +24,7 @@ import org.traccar.config.Config; import java.nio.charset.StandardCharsets; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FlexApiProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java index 2dec44e64..fb76673ca 100644 --- a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java @@ -23,8 +23,8 @@ import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.util.Date; diff --git a/src/main/java/org/traccar/protocol/FlexCommProtocol.java b/src/main/java/org/traccar/protocol/FlexCommProtocol.java index 5397156cb..293b9b12b 100644 --- a/src/main/java/org/traccar/protocol/FlexCommProtocol.java +++ b/src/main/java/org/traccar/protocol/FlexCommProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FlexCommProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java index 61e315af9..a16e61458 100644 --- a/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java +++ b/src/main/java/org/traccar/protocol/FlexibleReportProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FlexibleReportProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FlextrackProtocol.java b/src/main/java/org/traccar/protocol/FlextrackProtocol.java index ebac8b4de..b6353de9d 100644 --- a/src/main/java/org/traccar/protocol/FlextrackProtocol.java +++ b/src/main/java/org/traccar/protocol/FlextrackProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FlextrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FoxProtocol.java b/src/main/java/org/traccar/protocol/FoxProtocol.java index fa45b3817..edb496f11 100644 --- a/src/main/java/org/traccar/protocol/FoxProtocol.java +++ b/src/main/java/org/traccar/protocol/FoxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FoxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FreedomProtocol.java b/src/main/java/org/traccar/protocol/FreedomProtocol.java index dac117c04..87404094a 100644 --- a/src/main/java/org/traccar/protocol/FreedomProtocol.java +++ b/src/main/java/org/traccar/protocol/FreedomProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FreedomProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocol.java b/src/main/java/org/traccar/protocol/FreematicsProtocol.java index dce4994ab..c4076f330 100644 --- a/src/main/java/org/traccar/protocol/FreematicsProtocol.java +++ b/src/main/java/org/traccar/protocol/FreematicsProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FreematicsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java index 4e5200f37..d0402cc94 100644 --- a/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FreematicsProtocolDecoder.java @@ -153,7 +153,7 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, Integer.parseInt(value)); break; case 0x82: - position.set(Position.KEY_DEVICE_TEMP, Integer.parseInt(value) * 0.1); + position.set(Position.KEY_DEVICE_TEMP, Double.parseDouble(value) * 0.1); break; case 0x104: position.set(Position.KEY_ENGINE_LOAD, Integer.parseInt(value)); @@ -165,7 +165,7 @@ public class FreematicsProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RPM, Integer.parseInt(value)); break; case 0x10d: - position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(value))); + position.set(Position.KEY_OBD_SPEED, Integer.parseInt(value)); break; case 0x111: position.set(Position.KEY_THROTTLE, Integer.parseInt(value)); diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java index 715dd3c9c..20586bede 100644 --- a/src/main/java/org/traccar/protocol/FutureWayProtocol.java +++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class FutureWayProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/G1rusProtocol.java b/src/main/java/org/traccar/protocol/G1rusProtocol.java index f1823762d..b3904b357 100644 --- a/src/main/java/org/traccar/protocol/G1rusProtocol.java +++ b/src/main/java/org/traccar/protocol/G1rusProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class G1rusProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java b/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java index c23d26c83..d90e48287 100644 --- a/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/GalileoFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import org.traccar.BaseFrameDecoder; public class GalileoFrameDecoder extends BaseFrameDecoder { - private static final int MESSAGE_MINIMUM_LENGTH = 5; + private static final int MESSAGE_MINIMUM_LENGTH = 6; @Override protected Object decode( @@ -32,9 +32,15 @@ public class GalileoFrameDecoder extends BaseFrameDecoder { return null; } - int length = buf.getUnsignedShortLE(buf.readerIndex() + 1) & 0x7fff; - if (buf.readableBytes() >= (length + MESSAGE_MINIMUM_LENGTH)) { - return buf.readRetainedSlice(length + MESSAGE_MINIMUM_LENGTH); + int length; + if (buf.getByte(buf.readerIndex()) == 0x01 && buf.getUnsignedMedium(buf.readerIndex() + 3) == 0x01001c) { + length = 3 + buf.getUnsignedShort(buf.readerIndex() + 1); + } else { + length = 5 + (buf.getUnsignedShortLE(buf.readerIndex() + 1) & 0x7fff); + } + + if (buf.readableBytes() >= length) { + return buf.readRetainedSlice(length); } return null; diff --git a/src/main/java/org/traccar/protocol/GalileoProtocol.java b/src/main/java/org/traccar/protocol/GalileoProtocol.java index 90e95574a..95ce4edde 100644 --- a/src/main/java/org/traccar/protocol/GalileoProtocol.java +++ b/src/main/java/org/traccar/protocol/GalileoProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GalileoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GatorProtocol.java b/src/main/java/org/traccar/protocol/GatorProtocol.java index 7341b69a3..d29ed9ce7 100644 --- a/src/main/java/org/traccar/protocol/GatorProtocol.java +++ b/src/main/java/org/traccar/protocol/GatorProtocol.java @@ -20,17 +20,25 @@ import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; +import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GatorProtocol extends BaseProtocol { @Inject public GatorProtocol(Config config) { + setSupportedDataCommands( + Command.TYPE_POSITION_SINGLE, + Command.TYPE_ENGINE_RESUME, + Command.TYPE_ENGINE_STOP, + Command.TYPE_SET_SPEED_LIMIT, + Command.TYPE_SET_ODOMETER); addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 3, 2)); + pipeline.addLast(new GatorProtocolEncoder(GatorProtocol.this)); pipeline.addLast(new GatorProtocolDecoder(GatorProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java index 644caee81..a9c620090 100644 --- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java @@ -37,6 +37,11 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder { } public static final int MSG_HEARTBEAT = 0x21; + public static final int MSG_POSITION_REQUEST = 0x30; + public static final int MSG_OVERSPEED_ALARM = 0x3F; + public static final int MSG_RESET_MILEAGE = 0x6B; + public static final int MSG_RESTORE_OIL_DUCT = 0x38; + public static final int MSG_CLOSE_OIL_DUCT = 0x39; public static final int MSG_POSITION_DATA = 0x80; public static final int MSG_ROLLCALL_RESPONSE = 0x81; public static final int MSG_ALARM_DATA = 0x82; diff --git a/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java b/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java new file mode 100644 index 000000000..6c6b9a54a --- /dev/null +++ b/src/main/java/org/traccar/protocol/GatorProtocolEncoder.java @@ -0,0 +1,93 @@ +/* + * Copyright 2023 Hossain Mohammad Seym (seym45@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +public class GatorProtocolEncoder extends BaseProtocolEncoder { + + public GatorProtocolEncoder(Protocol protocol) { + super(protocol); + } + + public ByteBuf encodeId(long deviceId) { + ByteBuf buf = Unpooled.buffer(); + + String id = getUniqueId(deviceId); + + int firstDigit = Integer.parseInt(id.substring(1, 3)) - 30; + + buf.writeByte(Integer.parseInt(id.substring(3, 5)) | (((firstDigit >> 3) & 1) << 7)); + buf.writeByte(Integer.parseInt(id.substring(5, 7)) | (((firstDigit >> 2) & 1) << 7)); + buf.writeByte(Integer.parseInt(id.substring(7, 9)) | (((firstDigit >> 1) & 1) << 7)); + buf.writeByte(Integer.parseInt(id.substring(9)) | ((firstDigit & 1) << 7)); + + return buf; + } + + private ByteBuf encodeContent(long deviceId, int type, ByteBuf content) { + ByteBuf buf = Unpooled.buffer(); + + buf.writeByte(0x24); + buf.writeByte(0x24); + buf.writeByte(type); + buf.writeByte(0x00); + + buf.writeByte(4 + 1 + (content != null ? content.readableBytes() : 0) + 1); // length + + ByteBuf pseudoIPAddress = encodeId(deviceId); + buf.writeBytes(pseudoIPAddress); + + if (content != null) { + buf.writeBytes(content); + } + + int checksum = Checksum.xor(buf.nioBuffer()); + buf.writeByte(checksum); + + buf.writeByte(0x0D); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + ByteBuf content = Unpooled.buffer(); + + switch (command.getType()) { + case Command.TYPE_POSITION_SINGLE: + return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_POSITION_REQUEST, null); + case Command.TYPE_ENGINE_STOP: + return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_CLOSE_OIL_DUCT, null); + case Command.TYPE_ENGINE_RESUME: + return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_RESTORE_OIL_DUCT, null); + case Command.TYPE_SET_SPEED_LIMIT: + content.writeByte(command.getInteger(Command.KEY_DATA)); + return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_RESET_MILEAGE, content); + case Command.TYPE_SET_ODOMETER: + content.writeShort(command.getInteger(Command.KEY_DATA)); + return encodeContent(command.getDeviceId(), GatorProtocolDecoder.MSG_OVERSPEED_ALARM, content); + default: + return null; + } + } +} diff --git a/src/main/java/org/traccar/protocol/GenxProtocol.java b/src/main/java/org/traccar/protocol/GenxProtocol.java index 97d8633a0..7e5a6a69e 100644 --- a/src/main/java/org/traccar/protocol/GenxProtocol.java +++ b/src/main/java/org/traccar/protocol/GenxProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GenxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gl100Protocol.java b/src/main/java/org/traccar/protocol/Gl100Protocol.java index e1748c9a0..fdd79648f 100644 --- a/src/main/java/org/traccar/protocol/Gl100Protocol.java +++ b/src/main/java/org/traccar/protocol/Gl100Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gl100Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gl200Protocol.java b/src/main/java/org/traccar/protocol/Gl200Protocol.java index c7b6a8e7c..e3ddbb46e 100644 --- a/src/main/java/org/traccar/protocol/Gl200Protocol.java +++ b/src/main/java/org/traccar/protocol/Gl200Protocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gl200Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java index a9736c9e7..9b4e05c35 100644 --- a/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200ProtocolDecoder.java @@ -22,7 +22,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import org.traccar.Protocol; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.net.SocketAddress; public class Gl200ProtocolDecoder extends BaseProtocolDecoder { diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java index 517499f02..2e5ffa8d6 100644 --- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,15 @@ */ package org.traccar.protocol; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.config.Keys; import org.traccar.helper.BitUtil; +import org.traccar.helper.DataConverter; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; @@ -28,15 +31,14 @@ import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; +import org.traccar.session.DeviceSession; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.TimeZone; @@ -47,8 +49,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { private boolean ignoreFixTime; + private final DateFormat dateFormat; + public Gl200TextProtocolDecoder(Protocol protocol) { super(protocol); + dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); } @Override @@ -56,21 +62,67 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { ignoreFixTime = getConfig().getBoolean(Keys.PROTOCOL_IGNORE_FIX_TIME.withPrefix(getProtocolName())); } - private static final Pattern PATTERN_ACK = new PatternBuilder() - .text("+ACK:GT") - .expression("...,") // type - .number("([0-9A-Z]{2}xxxx),") // protocol version - .number("(d{15}|x{14}),") // imei - .any().text(",") - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); + private String getDeviceModel(DeviceSession deviceSession, String value) { + String model = value.isEmpty() ? getDeviceModel(deviceSession) : value; + return model != null ? model.toUpperCase() : ""; + } + + private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { + if (parser.matches()) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession != null) { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + return position; + } + } + return null; + } + + private void decodeDeviceTime(Position position, Parser parser) { + if (parser.hasNext(6)) { + if (ignoreFixTime) { + position.setTime(parser.nextDateTime()); + } else { + position.setDeviceTime(parser.nextDateTime()); + } + } + } + + private Long parseHours(String hoursString) { + if (hoursString != null && !hoursString.isEmpty()) { + String[] hours = hoursString.split(":"); + return (Integer.parseInt(hours[0]) * 3600L + + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60L : 0) + + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000; + } + return null; + } + + private Position decodeAck(Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[2]); + if (deviceSession == null) { + return null; + } + if (values[0].equals("+ACK:GTHBD")) { + if (channel != null) { + channel.writeAndFlush(new NetworkMessage( + "+SACK:GTHBD," + values[1] + "," + values[values.length - 1] + "$", remoteAddress)); + } + } else { + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + getLastLocation(position, dateFormat.parse(values[values.length - 2])); + position.setValid(false); + position.set(Position.KEY_RESULT, values[0]); + return position; + } + return null; + } private static final Pattern PATTERN_INF = new PatternBuilder() .text("+").expression("(?:RESP|BUFF):GTINF,") - .number("[0-9A-Z]{2}xxxx,") // protocol version + .expression("(?:.{6}|.{10})?,") // protocol version .number("(d{15}|x{14}),") // imei .expression("(?:[0-9A-Z]{17},)?") // vin .expression("(?:[^,]+)?,") // device name @@ -109,374 +161,6 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { .text("$").optional() .compile(); - private static final Pattern PATTERN_VER = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTVER,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .expression("([^,]*),") // device type - .number("(xxxx),") // firmware version - .number("(xxxx),") // hardware version - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd),") // time (hhmmss) - .number("(xxxx)") // counter - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_LOCATION = new PatternBuilder() - .number("(d{1,2}.?d?)?,") // hdop - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3}.?d?)?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(d+)?,") // mcc - .number("(d+)?,") // mnc - .groupBegin() - .number("(d+),") // lac - .number("(d+),") // cid - .or() - .number("(x+)?,") // lac - .number("(x+)?,") // cid - .groupEnd() - .number("(?:d+|(d+.d))?,") // rssi / odometer - .compile(); - - private static final Pattern PATTERN_OBD = new PatternBuilder() - .text("+RESP:GTOBD,") - .number("[0-9A-Z]{2}xxxx,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:[0-9A-Z]{17})?,") // vin - .expression("[^,]{0,20},") // device name - .expression("[01],") // report type - .number("x{1,8},") // report mask - .expression("(?:[0-9A-Z]{17})?,") // vin - .number("[01],") // obd connect - .number("(?:d{1,5})?,") // obd voltage - .number("(?:x{8})?,") // support pids - .number("(d{1,5})?,") // engine rpm - .number("(d{1,3})?,") // speed - .number("(-?d{1,3})?,") // coolant temp - .number("(d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d{1,5})?,") // dtcs cleared distance - .number("(?:d{1,5})?,") - .expression("([01])?,") // obd connect - .number("(d{1,3})?,") // number of dtcs - .number("(x*),") // dtcs - .number("(d{1,3})?,") // throttle - .number("(?:d{1,3})?,") // engine load - .number("(d{1,3})?,") // fuel level - .expression("(?:[0-9A],)?") // obd protocol - .number("(d+),") // odometer - .expression(PATTERN_LOCATION.pattern()) - .number("(d{1,7}.d)?,") // odometer - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_FRI = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:([0-9A-Z]{17}),)?") // vin - .expression("[^,]*,") // device name - .number("(d+)?,") // power - .number("(d{1,2}),").optional() // report type - .number("d{1,2},").optional() // count - .number("d*,").optional() // reserved - .number("(d+),").optional() // battery - .expression("((?:") - .expression(PATTERN_LOCATION.pattern()) - .expression(")+)") - .groupBegin() - .number("d{1,2},,") - .number("(d{1,3}),") // battery - .number("[01],") // mode - .number("(?:[01])?,") // motion - .number("(?:-?d{1,2}.d)?,") // temperature - .or() - .number("(d{1,7}.d)?,") // odometer - .number("(d{5}:dd:dd)?,") // hour meter - .number("(x+)?,") // adc 1 - .number("(x+)?,") // adc 2 - .number("(d{1,3})?,") // battery - .number("(?:(xx)(xx)(xx))?,") // device status - .number("(d+)?,") // rpm - .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption - .number("(d+)?,") // fuel level - .or() - .number("(-?d),") // rssi - .number("(d{1,3}),") // battery - .or() - .number("(d{1,7}.d)?,").optional() // odometer - .number("(d{1,3})?,") // battery - .groupEnd() - .any() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_ERI = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTERI,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(x{8}),") // mask - .number("(d+)?,") // power - .number("d{1,2},") // report type - .number("d{1,2},") // count - .expression("((?:") - .expression(PATTERN_LOCATION.pattern()) - .expression(")+)") - .groupBegin() - .number("(d{1,7}.d)?,") // odometer - .number("(d{5}:dd:dd)?,") // hour meter - .number("(x+)?,") // adc 1 - .number("(x+)?,").optional() // adc 2 - .groupBegin() - .number("(x+)?,") // adc 3 - .number("(xx),") // inputs - .number("(xx),") // outputs - .or() - .number("(d{1,3})?,") // battery - .number("(?:(xx)(xx)(xx))?,") // device status - .groupEnd() - .expression("(.*)") // additional data - .or() - .number("d*,,") - .number("(d+),") // battery - .any() - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_IGN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GTIG[NF],") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("d+,") // ignition off duration - .expression(PATTERN_LOCATION.pattern()) - .number("(d{5}:dd:dd)?,") // hour meter - .number("(d{1,7}.d)?,") // odometer - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_LSW = new PatternBuilder() - .text("+RESP:").expression("GT[LT]SW,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("[01],") // type - .number("([01]),") // state - .expression(PATTERN_LOCATION.pattern()) - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_IDA = new PatternBuilder() - .text("+RESP:GTIDA,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,,") // device name - .number("([^,]+),") // rfid - .expression("[01],") // report type - .number("1,") // count - .expression(PATTERN_LOCATION.pattern()) - .number("(d+.d),") // odometer - .text(",,,,") - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_WIF = new PatternBuilder() - .text("+RESP:GTWIF,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(d+),") // count - .number("((?:x{12},-?d+,,,,)+),,,,") // wifi - .number("(d{1,3}),") // battery - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_GSM = new PatternBuilder() - .text("+RESP:GTGSM,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("(?:STR|CTN|NMR|RTL),") // fix type - .expression("(.*)") // cells - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_PNA = new PatternBuilder() - .text("+RESP:GT").expression("P[NF]A,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_DAR = new PatternBuilder() - .text("+RESP:GTDAR,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("(d),") // warning type - .number("(d{1,2}),,,") // fatigue degree - .expression(PATTERN_LOCATION.pattern()) - .any() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF):GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version - .number("(d{15}|x{14}),") // imei - .expression("[^,]*,") // device name - .number("d*,") - .number("(x{1,2}),") // report type - .number("d{1,2},") // count - .number("d*,").optional() // reserved - .expression(PATTERN_LOCATION.pattern()) - .groupBegin() - .number("(?:(d{1,7}.d)|0)?,").optional() // odometer - .number("(d{1,3})?,") // battery - .or() - .number("(d{1,7}.d)?,") // odometer - .groupEnd() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)") // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private static final Pattern PATTERN_BASIC = new PatternBuilder() - .text("+").expression("(?:RESP|BUFF)").text(":") - .expression("GT...,") - .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version - .number("(d{15}|x{14}),") // imei - .any() - .text(",") - .number("(d{1,2})?,") // hdop - .number("(d{1,3}.d)?,") // speed - .number("(d{1,3})?,") // course - .number("(-?d{1,5}.d)?,") // altitude - .number("(-?d{1,3}.d{6})?,") // longitude - .number("(-?d{1,2}.d{6})?,") // latitude - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(d+),") // mcc - .number("(d+),") // mnc - .number("(x+),") // lac - .number("(x+),").optional(4) // cell - .any() - .number("(dddd)(dd)(dd)") // date (yyyymmdd) - .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) - .text(",") - .number("(xxxx)") // count number - .text("$").optional() - .compile(); - - private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) { - Parser parser = new Parser(PATTERN_ACK, sentence); - if (parser.matches()) { - String protocolVersion = parser.next(); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - if (type.equals("HBD")) { - if (channel != null) { - parser.skip(6); - channel.writeAndFlush(new NetworkMessage( - "+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress)); - } - } else { - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - getLastLocation(position, parser.nextDateTime()); - position.setValid(false); - position.set(Position.KEY_RESULT, "Command " + type + " accepted"); - return position; - } - } - return null; - } - - private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) { - if (parser.matches()) { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession != null) { - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - return position; - } - } - return null; - } - - private void decodeDeviceTime(Position position, Parser parser) { - if (parser.hasNext(6)) { - if (ignoreFixTime) { - position.setTime(parser.nextDateTime()); - } else { - position.setDeviceTime(parser.nextDateTime()); - } - } - } - - private Long parseHours(String hoursString) { - if (hoursString != null) { - String[] hours = hoursString.split(":"); - return (long) (Integer.parseInt(hours[0]) * 3600 - + (hours.length > 1 ? Integer.parseInt(hours[1]) * 60 : 0) - + (hours.length > 2 ? Integer.parseInt(hours[2]) : 0)) * 1000; - } - return null; - } - private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_INF, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -537,6 +221,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_VER = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTVER,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .expression("([^,]*),") // device type + .number("(xxxx),") // firmware version + .number("(xxxx),") // hardware version + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(xxxx)") // counter + .text("$").optional() + .compile(); + private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_VER, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -554,9 +252,35 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } private void skipLocation(Parser parser) { - parser.skip(19); + parser.skip(20); } + private static final Pattern PATTERN_LOCATION = new PatternBuilder() + .number("(d{1,2}.?d?)?,") // hdop + .number("(d{1,3}.d)?,") // speed + .number("(d{1,3}.?d?)?,") // course + .number("(-?d{1,5}.d)?,") // altitude + .number("(-?d{1,3}.d{6})?,") // longitude + .number("(-?d{1,2}.d{6})?,") // latitude + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .groupBegin() + .number(",d+") // wifi count + .number("((?:,x{12},-d+,,,)+)") // wifi + .groupEnd("?") + .text(",") + .number("(d+)?,") // mcc + .number("(d+)?,") // mnc + .groupBegin() + .number("(d+),") // lac + .number("(d+),") // cid + .or() + .number("(x+)?,") // lac + .number("(x+)?,") // cid + .groupEnd() + .number("(?:d+|(d+.d))?,") // rssi / odometer + .compile(); + private void decodeLocation(Position position, Parser parser) { Double hdop = parser.nextDouble(); position.setValid(hdop == null || hdop > 0); @@ -575,22 +299,125 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); } + Network network = new Network(); + + if (parser.hasNext()) { + String[] values = parser.next().split(","); + for (int i = 0; i < values.length; i += 5) { + String mac = values[i + 1].replaceAll("(..)", "$1:"); + network.addWifiAccessPoint(WifiAccessPoint.from( + mac.substring(0, mac.length() - 1), Integer.parseInt(values[i + 2]))); + } + } + if (parser.hasNext(6)) { int mcc = parser.nextInt(); int mnc = parser.nextInt(); if (parser.hasNext(2)) { - position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt()))); + network.addCellTower(CellTower.from(mcc, mnc, parser.nextInt(), parser.nextInt())); } if (parser.hasNext(2)) { - position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt()))); + network.addCellTower(CellTower.from(mcc, mnc, parser.nextHexInt(), parser.nextHexInt())); } } + if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) { + position.setNetwork(network); + } + if (parser.hasNext()) { position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); } } + private int decodeLocation(Position position, String model, String[] values, int index) throws ParseException { + double hdop = values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]); + position.set(Position.KEY_HDOP, hdop); + + position.setSpeed(UnitsConverter.knotsFromKph( + values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1]))); + position.setCourse(values[index++].isEmpty() ? 0 : Integer.parseInt(values[index - 1])); + position.setAltitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1])); + + if (!values[index].isEmpty()) { + position.setValid(true); + position.setLongitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1])); + position.setLatitude(values[index++].isEmpty() ? 0 : Double.parseDouble(values[index - 1])); + position.setTime(dateFormat.parse(values[index++])); + } else { + index += 3; + getLastLocation(position, null); + } + + Network network = new Network(); + + if (!values[index].isEmpty()) { + network.addCellTower(CellTower.from( + Integer.parseInt(values[index++]), + Integer.parseInt(values[index++]), + Integer.parseInt(values[index++], 16), + Long.parseLong(values[index++], 16))); + } else { + index += 4; + } + + if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) { + position.setNetwork(network); + } + + if (model.startsWith("GL5")) { + index += 1; // csq rssi + index += 1; // csq ber + } + + if (!values[index++].isEmpty()) { + int appendMask = Integer.parseInt(values[index - 1]); + if (BitUtil.check(appendMask, 0)) { + position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + } + if (BitUtil.check(appendMask, 1)) { + index += 1; // trigger type + } + } + + return index; + } + + private static final Pattern PATTERN_OBD = new PatternBuilder() + .text("+RESP:GTOBD,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:[0-9A-Z]{17})?,") // vin + .expression("[^,]{0,20},") // device name + .expression("[01],") // report type + .number("x{1,8},") // report mask + .expression("(?:[0-9A-Z]{17})?,") // vin + .number("[01],") // obd connect + .number("(?:d{1,5})?,") // obd voltage + .number("(?:x{8})?,") // support pids + .number("(d{1,5})?,") // engine rpm + .number("(d{1,3})?,") // speed + .number("(-?d{1,3})?,") // coolant temp + .number("(d+.?d*|Inf|NaN)?,") // fuel consumption + .number("(d{1,5})?,") // dtcs cleared distance + .number("(?:d{1,5})?,") + .expression("([01])?,") // obd connect + .number("(d{1,3})?,") // number of dtcs + .number("(x*),") // dtcs + .number("(d{1,3})?,") // throttle + .number("(?:d{1,3})?,") // engine load + .number("(d{1,3})?,") // fuel level + .expression("(?:[0-9A],)?") // obd protocol + .number("(d+),") // odometer + .expression(PATTERN_LOCATION.pattern()) + .number("(d{1,7}.d)?,") // odometer + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_OBD, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -625,104 +452,140 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } - private Object decodeCan(Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException { - Position position = new Position(getProtocolName()); - + private Object decodeCan(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException { int index = 0; - String[] values = sentence.split(","); - index += 1; // header index += 1; // protocol version + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]); + if (deviceSession == null) { + return null; + } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]); + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - index += 1; // device name + String model = getDeviceModel(deviceSession, v[index++]); index += 1; // report type - index += 1; // canbus state - long reportMask = Long.parseLong(values[index++], 16); + index += 1; // can bus state + long reportMask = Long.parseLong(v[index++], 16); long reportMaskExt = 0; if (BitUtil.check(reportMask, 0)) { - position.set(Position.KEY_VIN, values[index++]); + position.set(Position.KEY_VIN, v[index++]); } - if (BitUtil.check(reportMask, 1)) { - position.set(Position.KEY_IGNITION, Integer.parseInt(values[index++]) > 0); + if (BitUtil.check(reportMask, 1) && !v[index++].isEmpty()) { + position.set(Position.KEY_IGNITION, Integer.parseInt(v[index - 1]) > 0); } - if (BitUtil.check(reportMask, 2)) { - position.set(Position.KEY_OBD_ODOMETER, values[index++]); + if (BitUtil.check(reportMask, 2) && !v[index++].isEmpty()) { + position.set(Position.KEY_OBD_ODOMETER, Integer.parseInt(v[index - 1].substring(1))); } - if (BitUtil.check(reportMask, 3) && !values[index++].isEmpty()) { - position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index - 1])); + if (BitUtil.check(reportMask, 3) && !v[index++].isEmpty()) { + position.set(Position.KEY_FUEL_USED, Double.parseDouble(v[index - 1])); } - if (BitUtil.check(reportMask, 5) && !values[index++].isEmpty()) { - position.set(Position.KEY_RPM, Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMask, 5) && !v[index++].isEmpty()) { + position.set(Position.KEY_RPM, Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMask, 4) && !values[index++].isEmpty()) { - position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1]))); + if (BitUtil.check(reportMask, 4) && !v[index++].isEmpty()) { + position.set(Position.KEY_OBD_SPEED, Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMask, 6) && !values[index++].isEmpty()) { - position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMask, 6) && !v[index++].isEmpty()) { + position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMask, 7) && !values[index++].isEmpty()) { - position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(values[index - 1].substring(1))); + if (BitUtil.check(reportMask, 7) && !v[index++].isEmpty()) { + String value = v[index - 1]; + if (value.startsWith("L/H")) { + position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(value.substring(3))); + } } - if (BitUtil.check(reportMask, 8) && !values[index++].isEmpty()) { - position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index - 1].substring(1))); + if (BitUtil.check(reportMask, 8) && !v[index++].isEmpty()) { + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(v[index - 1].substring(1))); } - if (BitUtil.check(reportMask, 9) && !values[index++].isEmpty()) { - position.set("range", Long.parseLong(values[index - 1]) * 100); + if (BitUtil.check(reportMask, 9) && !v[index++].isEmpty()) { + position.set("range", Long.parseLong(v[index - 1]) * 100); } - if (BitUtil.check(reportMask, 10) && !values[index++].isEmpty()) { - position.set(Position.KEY_THROTTLE, Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMask, 10) && !v[index++].isEmpty()) { + position.set(Position.KEY_THROTTLE, Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMask, 11) && !values[index++].isEmpty()) { - position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(values[index - 1]))); + if (BitUtil.check(reportMask, 11) && !v[index++].isEmpty()) { + position.set(Position.KEY_HOURS, UnitsConverter.msFromHours(Double.parseDouble(v[index - 1]))); } - if (BitUtil.check(reportMask, 12)) { - position.set("drivingHours", Double.parseDouble(values[index++])); + if (BitUtil.check(reportMask, 12) && !v[index++].isEmpty()) { + position.set(Position.KEY_DRIVING_TIME, Double.parseDouble(v[index - 1])); } - if (BitUtil.check(reportMask, 13)) { - position.set("idleHours", Double.parseDouble(values[index++])); + if (BitUtil.check(reportMask, 13) && !v[index++].isEmpty()) { + position.set("idleHours", Double.parseDouble(v[index - 1])); } - if (BitUtil.check(reportMask, 14) && !values[index++].isEmpty()) { - position.set("idleFuelConsumption", Double.parseDouble(values[index - 1])); + if (BitUtil.check(reportMask, 14) && !v[index++].isEmpty()) { + position.set("idleFuelConsumption", Double.parseDouble(v[index - 1])); } - if (BitUtil.check(reportMask, 15) && !values[index++].isEmpty()) { - position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMask, 15) && !v[index++].isEmpty()) { + position.set(Position.KEY_AXLE_WEIGHT, Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMask, 16) && !values[index++].isEmpty()) { - position.set("tachographInfo", Integer.parseInt(values[index - 1], 16)); + if (BitUtil.check(reportMask, 16) && !v[index++].isEmpty()) { + position.set("tachographInfo", Integer.parseInt(v[index - 1], 16)); } - if (BitUtil.check(reportMask, 17) && !values[index++].isEmpty()) { - position.set("indicators", Integer.parseInt(values[index - 1], 16)); + if (BitUtil.check(reportMask, 17) && !v[index++].isEmpty()) { + position.set("indicators", Integer.parseInt(v[index - 1], 16)); } - if (BitUtil.check(reportMask, 18) && !values[index++].isEmpty()) { - position.set("lights", Integer.parseInt(values[index - 1], 16)); + if (BitUtil.check(reportMask, 18) && !v[index++].isEmpty()) { + position.set("lights", Integer.parseInt(v[index - 1], 16)); } - if (BitUtil.check(reportMask, 19) && !values[index++].isEmpty()) { - position.set("doors", Integer.parseInt(values[index - 1], 16)); + if (BitUtil.check(reportMask, 19) && !v[index++].isEmpty()) { + position.set("doors", Integer.parseInt(v[index - 1], 16)); } - if (BitUtil.check(reportMask, 20) && !values[index++].isEmpty()) { - position.set("vehicleOverspeed", Double.parseDouble(values[index - 1])); + if (BitUtil.check(reportMask, 20) && !v[index++].isEmpty()) { + position.set("vehicleOverspeed", Double.parseDouble(v[index - 1])); } - if (BitUtil.check(reportMask, 21) && !values[index++].isEmpty()) { - position.set("engineOverspeed", Double.parseDouble(values[index - 1])); + if (BitUtil.check(reportMask, 21) && !v[index++].isEmpty()) { + position.set("engineOverspeed", Double.parseDouble(v[index - 1])); } - if (BitUtil.check(reportMask, 29)) { - reportMaskExt = Long.parseLong(values[index++], 16); + if ("GV350M".equals(model)) { + if (BitUtil.check(reportMask, 22)) { + index += 1; // impulse distance + } + if (BitUtil.check(reportMask, 23)) { + index += 1; // gross vehicle weight + } + if (BitUtil.check(reportMask, 24)) { + index += 1; // catalyst liquid level + } + } else if ("GV355CEU".equals(model)) { + if (BitUtil.check(reportMask, 22)) { + index += 1; // impulse distance + } + if (BitUtil.check(reportMask, 23)) { + index += 1; // engine cold starts + } + if (BitUtil.check(reportMask, 24)) { + index += 1; // engine all starts + } + if (BitUtil.check(reportMask, 25)) { + index += 1; // engine starts by ignition + } + if (BitUtil.check(reportMask, 26)) { + index += 1; // total engine cold running time + } + if (BitUtil.check(reportMask, 27)) { + index += 1; // handbrake applies during ride + } + if (BitUtil.check(reportMask, 28)) { + index += 1; // electric report mask + } + } + if (BitUtil.check(reportMask, 29) && !v[index++].isEmpty()) { + reportMaskExt = Long.parseLong(v[index - 1], 16); } - if (BitUtil.check(reportMaskExt, 0) && !values[index++].isEmpty()) { - position.set("adBlueLevel", Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMaskExt, 0) && !v[index++].isEmpty()) { + position.set("adBlueLevel", Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMaskExt, 1) && !values[index++].isEmpty()) { - position.set("axleWeight1", Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMaskExt, 1) && !v[index++].isEmpty()) { + position.set("axleWeight1", Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMaskExt, 2) && !values[index++].isEmpty()) { - position.set("axleWeight3", Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMaskExt, 2) && !v[index++].isEmpty()) { + position.set("axleWeight3", Integer.parseInt(v[index - 1])); } - if (BitUtil.check(reportMaskExt, 3) && !values[index++].isEmpty()) { - position.set("axleWeight4", Integer.parseInt(values[index - 1])); + if (BitUtil.check(reportMaskExt, 3) && !v[index++].isEmpty()) { + position.set("axleWeight4", Integer.parseInt(v[index - 1])); } if (BitUtil.check(reportMaskExt, 4)) { index += 1; // tachograph overspeed @@ -733,8 +596,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(reportMaskExt, 6)) { index += 1; // tachograph direction } - if (BitUtil.check(reportMaskExt, 7) && !values[index++].isEmpty()) { - position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[index - 1]) * 0.001); + if (BitUtil.check(reportMaskExt, 7) && !v[index++].isEmpty()) { + position.set(Position.PREFIX_ADC + 1, Integer.parseInt(v[index - 1]) * 0.001); } if (BitUtil.check(reportMaskExt, 8)) { index += 1; // pedal breaking factor @@ -757,20 +620,20 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(reportMaskExt, 14)) { index += 1; // total brake application } - if (BitUtil.check(reportMaskExt, 15) && !values[index++].isEmpty()) { - position.set("driver1Card", values[index - 1]); + if (BitUtil.check(reportMaskExt, 15) && !v[index++].isEmpty()) { + position.set("driver1Card", v[index - 1]); } - if (BitUtil.check(reportMaskExt, 16) && !values[index++].isEmpty()) { - position.set("driver2Card", values[index - 1]); + if (BitUtil.check(reportMaskExt, 16) && !v[index++].isEmpty()) { + position.set("driver2Card", v[index - 1]); } - if (BitUtil.check(reportMaskExt, 17) && !values[index++].isEmpty()) { - position.set("driver1Name", values[index - 1]); + if (BitUtil.check(reportMaskExt, 17) && !v[index++].isEmpty()) { + position.set("driver1Name", v[index - 1]); } - if (BitUtil.check(reportMaskExt, 18) && !values[index++].isEmpty()) { - position.set("driver2Name", values[index - 1]); + if (BitUtil.check(reportMaskExt, 18) && !v[index++].isEmpty()) { + position.set("driver2Name", v[index - 1]); } - if (BitUtil.check(reportMaskExt, 19) && !values[index++].isEmpty()) { - position.set("registration", values[index - 1]); + if (BitUtil.check(reportMaskExt, 19) && !v[index++].isEmpty()) { + position.set("registration", v[index - 1]); } if (BitUtil.check(reportMaskExt, 20)) { index += 1; // expansion information @@ -788,18 +651,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - if (BitUtil.check(reportMask, 30)) { - while (values[index].isEmpty()) { + if (!"GV355CEU".equals(model) && BitUtil.check(reportMask, 30)) { + while (v[index].isEmpty()) { index += 1; } - position.setValid(Integer.parseInt(values[index++]) > 0); - if (!values[index].isEmpty()) { - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++]))); - position.setCourse(Integer.parseInt(values[index++])); - position.setAltitude(Double.parseDouble(values[index++])); - position.setLongitude(Double.parseDouble(values[index++])); - position.setLatitude(Double.parseDouble(values[index++])); - position.setTime(dateFormat.parse(values[index++])); + position.setValid(Integer.parseInt(v[index++]) > 0); + if (!v[index].isEmpty()) { + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(v[index++]))); + position.setCourse(Integer.parseInt(v[index++])); + position.setAltitude(Double.parseDouble(v[index++])); + position.setLongitude(Double.parseDouble(v[index++])); + position.setLatitude(Double.parseDouble(v[index++])); + position.setTime(dateFormat.parse(v[index++])); } else { index += 6; // no location getLastLocation(position, null); @@ -813,34 +676,79 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { index += 1; // reserved } + index = v.length - 2; if (ignoreFixTime) { - position.setTime(dateFormat.parse(values[index])); + position.setTime(dateFormat.parse(v[index])); } else { - position.setDeviceTime(dateFormat.parse(values[index])); + position.setDeviceTime(dateFormat.parse(v[index])); } return position; } - private void decodeStatus(Position position, Parser parser) { - if (parser.hasNext(3)) { - int ignition = parser.nextHexInt(); - if (BitUtil.check(ignition, 4)) { - position.set(Position.KEY_IGNITION, false); - } else if (BitUtil.check(ignition, 5)) { - position.set(Position.KEY_IGNITION, true); - } - int input = parser.nextHexInt(); - int output = parser.nextHexInt(); - position.set(Position.KEY_INPUT, input); - position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1)); - position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2)); - position.set(Position.KEY_OUTPUT, output); - position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0)); - position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1)); - } + private void decodeStatus(Position position, long value) { + long ignition = BitUtil.between(value, 2 * 8, 3 * 8); + if (BitUtil.check(ignition, 4)) { + position.set(Position.KEY_IGNITION, false); + } else if (BitUtil.check(ignition, 5)) { + position.set(Position.KEY_IGNITION, true); + } + long input = BitUtil.between(value, 8, 2 * 8); + long output = BitUtil.to(value, 8); + position.set(Position.KEY_INPUT, input); + position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2)); + position.set(Position.KEY_OUTPUT, output); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1)); } + private static final Pattern PATTERN_FRI = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GT...,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:([0-9A-Z]{17}),)?") // vin + .expression("[^,]*,") // device name + .number("(d+)?,") // power + .number("(d{1,2}),").optional() // report type + .number("d{1,2},").optional() // count + .number("d*,").optional() // reserved + .number("(d+),").optional() // battery + .expression("((?:") + .expression(PATTERN_LOCATION.pattern()) + .expression(")+)") + .groupBegin() + .number("d{1,2},") + .number("(d{1,5})?,") // battery + .number("(d{1,3}),") // battery level + .number("[01],") // mode + .number("(?:[01])?,") // motion + .number("(-?d{1,2}.d)?,") // temperature + .or() + .number("(d{1,7}.d)?,") // odometer + .number("(d{5}:dd:dd)?,") // hour meter + .number("(x+)?,") // adc 1 + .number("(x+)?,") // adc 2 + .number("(d{1,3})?,") // battery + .number("(x{6})?,") // device status + .number("(d+)?,") // rpm + .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption + .number("(d+)?,") // fuel level + .or() + .number("(-?d),") // rssi + .number("(d{1,3}),") // battery + .or() + .number("(d{1,7}.d)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .groupEnd() + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_FRI, sentence); if (!parser.matches()) { @@ -883,8 +791,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } if (parser.hasNext()) { - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001); } + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + position.set(Position.PREFIX_TEMP + 1, parser.nextDouble()); if (parser.hasNext()) { position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); @@ -894,7 +804,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 2, parser.next()); position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - decodeStatus(position, parser); + if (parser.hasNext()) { + decodeStatus(position, parser.nextHexLong()); + } position.set(Position.KEY_RPM, parser.nextInt()); position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); @@ -921,109 +833,112 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return positions; } - private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) { - Parser parser = new Parser(PATTERN_ERI, sentence); - if (!parser.matches()) { - return null; - } - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + private Object decodeEri(Channel channel, SocketAddress remoteAddress, String[] v) throws ParseException { + int index = 0; + index += 1; // header + index += 1; // protocol version + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, v[index++]); if (deviceSession == null) { return null; } - long mask = parser.nextHexLong(); + String model = getDeviceModel(deviceSession, v[index++]); + long mask = Long.parseLong(v[index++], 16); + Double power = v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001; + index += 1; // report type + int count = Integer.parseInt(v[index++]); LinkedList<Position> positions = new LinkedList<>(); - - Integer power = parser.nextInt(); - - Parser itemParser = new Parser(PATTERN_LOCATION, parser.next()); - while (itemParser.find()) { + for (int i = 0; i < count; i++) { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - - decodeLocation(position, itemParser); - + index = decodeLocation(position, model, v, index); positions.add(position); } Position position = positions.getLast(); + position.set(Position.KEY_POWER, power); - skipLocation(parser); - - if (power != null) { - position.set(Position.KEY_POWER, power * 0.001); + if (!model.startsWith("GL5")) { + position.set(Position.KEY_ODOMETER, v[index++].isEmpty() ? null : Double.parseDouble(v[index - 1]) * 1000); + position.set(Position.KEY_HOURS, parseHours(v[index++])); + position.set(Position.PREFIX_ADC + 1, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001); + } + if (model.startsWith("GV") && !model.startsWith("GV6")) { + position.set(Position.PREFIX_ADC + 2, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) * 0.001); } - if (parser.hasNext(12)) { - - position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); - position.set(Position.KEY_HOURS, parseHours(parser.next())); - position.set(Position.PREFIX_ADC + 1, parser.next()); - position.set(Position.PREFIX_ADC + 2, parser.next()); - position.set(Position.PREFIX_ADC + 3, parser.next()); - if (parser.hasNext(2)) { - position.set(Position.KEY_INPUT, parser.nextHexInt()); - position.set(Position.KEY_OUTPUT, parser.nextHexInt()); - } - if (parser.hasNext(4)) { - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - decodeStatus(position, parser); + position.set(Position.KEY_BATTERY_LEVEL, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1])); + if (model.startsWith("GL5")) { + index += 1; // mode selection + position.set(Position.KEY_MOTION, v[index++].isEmpty() ? null : Integer.parseInt(v[index - 1]) > 0); + } else { + if (!v[index++].isEmpty()) { + decodeStatus(position, Long.parseLong(v[index - 1])); } + index += 1; // reserved / uart device type + } - int index = 0; - String[] data = parser.next().split(","); - - index += 1; // device type - - if (BitUtil.check(mask, 0)) { - index += 1; // digital fuel sensor data - } + if (BitUtil.check(mask, 0)) { + position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(v[index++], 16)); + } - if (BitUtil.check(mask, 1)) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index += 1; // id - index += 1; // type - if (!data[index++].isEmpty()) { - position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625); - } + if (BitUtil.check(mask, 1)) { + int deviceCount = Integer.parseInt(v[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // id + index += 1; // type + if (!v[index++].isEmpty()) { + position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(v[index - 1], 16) * 0.0625); } } + } - if (BitUtil.check(mask, 2)) { - index += 1; // can data - } + if (BitUtil.check(mask, 2)) { + index += 1; // can data + } - if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { - int deviceCount = Integer.parseInt(data[index++]); - for (int i = 1; i <= deviceCount; i++) { - index += 1; // type - if (BitUtil.check(mask, 3)) { - position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++])); - } - if (BitUtil.check(mask, 4)) { - index += 1; // volume - } + if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) { + int deviceCount = Integer.parseInt(v[index++]); + for (int i = 1; i <= deviceCount; i++) { + index += 1; // type + if (BitUtil.check(mask, 3)) { + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(v[index++])); + } + if (BitUtil.check(mask, 4)) { + index += 1; // volume } } - } - if (parser.hasNext()) { - position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); - } - - decodeDeviceTime(position, parser); + Date time = dateFormat.parse(v[v.length - 2]); if (ignoreFixTime) { + position.setTime(time); positions.clear(); positions.add(position); + } else { + position.setDeviceTime(time); } return positions; } + private static final Pattern PATTERN_IGN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GTIG[NF],") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d+,") // ignition off duration + .expression(PATTERN_LOCATION.pattern()) + .number("(d{5}:dd:dd)?,") // hour meter + .number("(d{1,7}.d)?,") // odometer + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_IGN, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1042,6 +957,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_LSW = new PatternBuilder() + .text("+RESP:").expression("GT[LT]SW,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("[01],") // type + .number("([01]),") // state + .expression(PATTERN_LOCATION.pattern()) + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeLsw(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_LSW, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1058,6 +988,24 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_IDA = new PatternBuilder() + .text("+RESP:GTIDA,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,,") // device name + .number("([^,]+),") // rfid + .expression("[01],") // report type + .number("1,") // count + .expression(PATTERN_LOCATION.pattern()) + .number("(d+.d),") // odometer + .text(",,,,") + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_IDA, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1076,6 +1024,21 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_WIF = new PatternBuilder() + .text("+RESP:GTWIF,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(d+),") // count + .number("((?:x{12},-?d+,,,,)+),,,,") // wifi + .number("(d{1,3}),") // battery + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_WIF, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1102,6 +1065,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_GSM = new PatternBuilder() + .text("+RESP:GTGSM,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("(?:STR|CTN|NMR|RTL),") // fix type + .expression("(.*)") // cells + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_GSM, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1128,6 +1104,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_PNA = new PatternBuilder() + .text("+RESP:GT").expression("P[NF]A,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodePna(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_PNA, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1142,6 +1130,22 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_DAR = new PatternBuilder() + .text("+RESP:GTDAR,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("(d),") // warning type + .number("(d{1,2}),,,") // fatigue degree + .expression(PATTERN_LOCATION.pattern()) + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeDar(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_DAR, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1165,6 +1169,204 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_DTT = new PatternBuilder() + .text("+RESP:GTDTT,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,,,") // device name + .number("d,") // data type + .number("d+,") // data length + .number("(x+),") // data + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeDtt(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_DTT, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + getLastLocation(position, null); + + String data = Unpooled.wrappedBuffer(DataConverter.parseHex(parser.next())) + .toString(StandardCharsets.US_ASCII); + if (data.contains("COMB")) { + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data.split(",")[2])); + } else { + position.set(Position.KEY_RESULT, data); + } + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN_BAA = new PatternBuilder() + .text("+RESP:GTBAA,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("x+,") // index + .number("d,") // accessory type + .number("d,") // accessory model + .number("x+,") // alarm type + .number("(x{4}),") // append mask + .expression("((?:[^,]+,){0,6})") // accessory optionals + .expression(PATTERN_LOCATION.pattern()) + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeBaa(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_BAA, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int mask = parser.nextHexInt(); + String[] values = parser.next().split(","); + int index = 0; + if (BitUtil.check(mask, 0)) { + position.set("accessoryName", values[index++]); + } + if (BitUtil.check(mask, 1)) { + position.set("accessoryMac", values[index++]); + } + if (BitUtil.check(mask, 2)) { + position.set("accessoryStatus", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 3)) { + position.set("accessoryVoltage", Integer.parseInt(values[index++]) * 0.001); + } + if (BitUtil.check(mask, 4)) { + position.set("accessoryTemp", Integer.parseInt(values[index++])); + } + if (BitUtil.check(mask, 5)) { + position.set("accessoryHumidity", Integer.parseInt(values[index])); + } + + decodeLocation(position, parser); + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN_BID = new PatternBuilder() + .text("+RESP:GTBID,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d,") // count + .number("d,") // accessory model + .number("(x{4}),") // append mask + .expression("((?:[^,]+,){0,2})") // accessory optionals + .expression(PATTERN_LOCATION.pattern()) + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeBid(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_BID, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + int mask = parser.nextHexInt(); + String[] values = parser.next().split(","); + int index = 0; + if (BitUtil.check(mask, 1)) { + position.set("accessoryMac", values[index++]); + } + if (BitUtil.check(mask, 3)) { + position.set("accessoryVoltage", Integer.parseInt(values[index]) * 0.001); + } + + decodeLocation(position, parser); + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN_LSA = new PatternBuilder() + .text("+RESP:GTLSA,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d,") // event state 1 + .number("d,") // event state 2 + .number("d+,") // number + .expression(PATTERN_LOCATION.pattern()) + .number("d+,") // bit error rate + .number("(d),") // light level + .number("(d+),") // battery level + .number("[01],") // mode selection + .number("[01]?,") // movement status + .number("(-?d+.d)?,") // temperature + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + + private Object decodeLsa(Channel channel, SocketAddress remoteAddress, String sentence) { + Parser parser = new Parser(PATTERN_LSA, sentence); + Position position = initPosition(parser, channel, remoteAddress); + if (position == null) { + return null; + } + + decodeLocation(position, parser); + + position.set("lightLevel", parser.nextInt()); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt()); + position.set(Position.PREFIX_TEMP + 1, parser.nextDouble()); + + decodeDeviceTime(position, parser); + + return position; + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF):GT...,") + .expression("(?:.{6}|.{10})?,") // protocol version + .number("(d{15}|x{14}),") // imei + .expression("[^,]*,") // device name + .number("d*,") + .number("(x{1,2}),") // report type + .number("d{1,2},") // count + .number("d*,").optional() // reserved + .expression(PATTERN_LOCATION.pattern()) + .groupBegin() + .number("(?:(d{1,7}.d)|0)?,").optional() // odometer + .number("(d{1,3})?,") // battery + .or() + .number("(d{1,7}.d)?,") // odometer + .groupEnd() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)") // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) { Parser parser = new Parser(PATTERN, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1215,6 +1417,38 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN_BASIC = new PatternBuilder() + .text("+").expression("(?:RESP|BUFF)").text(":") + .expression("GT...,") + .expression("[^,]+,").optional() // protocol version + .number("(d{15}|x{14}),") // imei + .any() + .text(",") + .number("(d{1,2}),") // hdop + .groupBegin() + .number("(d{1,3}.d),") // speed + .number("(d{1,3}),") // course + .number("(-?d{1,5}.d),") // altitude + .number("(-?d{1,3}.d{6}),") // longitude + .number("(-?d{1,2}.d{6}),") // latitude + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)") // time (hhmmss) + .text(",") + .or() + .text(",,,,,,") + .groupEnd() + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(x+),") // lac + .number("(x+),").optional(4) // cell + .any() + .number("(dddd)(dd)(dd)") // date (yyyymmdd) + .number("(dd)(dd)(dd)").optional(2) // time (hhmmss) + .text(",") + .number("(xxxx)") // count number + .text("$").optional() + .compile(); + private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) { Parser parser = new Parser(PATTERN_BASIC, sentence); Position position = initPosition(parser, channel, remoteAddress); @@ -1299,17 +1533,19 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = ((ByteBuf) msg).toString(StandardCharsets.US_ASCII); + String sentence = ((ByteBuf) msg).toString(StandardCharsets.US_ASCII).replaceAll("\\$$", ""); int typeIndex = sentence.indexOf(":GT"); if (typeIndex < 0) { return null; } + String[] values = sentence.split(","); + Object result; String type = sentence.substring(typeIndex + 3, typeIndex + 6); if (sentence.startsWith("+ACK")) { - result = decodeAck(channel, remoteAddress, sentence, type); + result = decodeAck(channel, remoteAddress, values); } else { switch (type) { case "INF": @@ -1319,7 +1555,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { result = decodeObd(channel, remoteAddress, sentence); break; case "CAN": - result = decodeCan(channel, remoteAddress, sentence); + result = decodeCan(channel, remoteAddress, values); break; case "CTN": case "FRI": @@ -1330,7 +1566,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { result = decodeFri(channel, remoteAddress, sentence); break; case "ERI": - result = decodeEri(channel, remoteAddress, sentence); + result = decodeEri(channel, remoteAddress, values); break; case "IGN": case "IGF": @@ -1359,6 +1595,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { case "DAR": result = decodeDar(channel, remoteAddress, sentence); break; + case "DTT": + result = decodeDtt(channel, remoteAddress, sentence); + break; + case "BAA": + result = decodeBaa(channel, remoteAddress, sentence); + break; + case "BID": + result = decodeBid(channel, remoteAddress, sentence); + break; + case "LSA": + result = decodeLsa(channel, remoteAddress, sentence); + break; default: result = decodeOther(channel, remoteAddress, sentence, type); break; @@ -1380,13 +1628,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder { } if (channel != null && getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) { - String checksum; - if (sentence.endsWith("$")) { - checksum = sentence.substring(sentence.length() - 1 - 4, sentence.length() - 1); - } else { - checksum = sentence.substring(sentence.length() - 4); - } - channel.writeAndFlush(new NetworkMessage("+SACK:" + checksum + "$", remoteAddress)); + channel.writeAndFlush(new NetworkMessage("+SACK:" + values[values.length - 1] + "$", remoteAddress)); } return result; diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java index 16b99f426..13f4f2646 100644 --- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java +++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GlobalSatProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java index 293f5fda5..1d9b6b6dd 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocol.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GlobalstarProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java index 0ddb95c14..b75e612b8 100644 --- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.config.Keys; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -64,6 +65,12 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { private final XPath xPath; private final XPathExpression messageExpression; + private boolean alternative; + + public void setAlternative(boolean alternative) { + this.alternative = alternative; + } + public GlobalstarProtocolDecoder(Protocol protocol) { super(protocol); try { @@ -82,6 +89,11 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { } } + @Override + protected void init() { + this.alternative = getConfig().getBoolean(Keys.PROTOCOL_ALTERNATIVE.withPrefix(getProtocolName())); + } + private void sendResponse(Channel channel, String messageId) throws TransformerException { Document document = documentBuilder.newDocument(); @@ -135,32 +147,50 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - position.setValid(true); position.setTime(new Date(Long.parseLong(xPath.evaluate("unixTime", node)) * 1000)); ByteBuf buf = Unpooled.wrappedBuffer( DataConverter.parseHex(xPath.evaluate("payload", node).substring(2))); int flags = buf.readUnsignedByte(); - position.set(Position.PREFIX_IN + 1, !BitUtil.check(flags, 1)); - position.set(Position.PREFIX_IN + 2, !BitUtil.check(flags, 2)); - position.set(Position.KEY_CHARGE, !BitUtil.check(flags, 3)); - if (BitUtil.check(flags, 4)) { - position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); + int type; + if (alternative) { + type = BitUtil.to(flags, 1); + position.setValid(true); + position.set(Position.PREFIX_IN + 1, !BitUtil.check(flags, 1)); + position.set(Position.PREFIX_IN + 2, !BitUtil.check(flags, 2)); + position.set(Position.KEY_CHARGE, !BitUtil.check(flags, 3)); + if (BitUtil.check(flags, 4)) { + position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); + } + position.setCourse(BitUtil.from(flags, 5) * 45); + } else { + type = BitUtil.to(flags, 2); + if (BitUtil.check(flags, 2)) { + position.set("batteryReplace", true); + } + position.setValid(!BitUtil.check(flags, 3)); } - position.setCourse(BitUtil.from(flags, 5) * 45); - double latitude = buf.readUnsignedMedium() * 90.0 / (1 << 23); position.setLatitude(latitude > 90 ? latitude - 180 : latitude); double longitude = buf.readUnsignedMedium() * 180.0 / (1 << 23); position.setLongitude(longitude > 180 ? longitude - 360 : longitude); - int speed = buf.readUnsignedByte(); - position.setSpeed(UnitsConverter.knotsFromKph(speed)); - - position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7)); + int speed = 0; + if (alternative) { + speed = buf.readUnsignedByte(); + position.setSpeed(UnitsConverter.knotsFromKph(speed)); + position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7)); + } else if (type == 0) { + position.set(Position.KEY_INPUT, BitUtil.to(buf.readUnsignedByte(), 4)); + int other = buf.readUnsignedByte(); + if (BitUtil.check(other, 4)) { + position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); + } + position.set(Position.KEY_MOTION, BitUtil.check(other, 6)); + } if (speed != 0xff) { positions.add(position); diff --git a/src/main/java/org/traccar/protocol/GnxProtocol.java b/src/main/java/org/traccar/protocol/GnxProtocol.java index 32d642688..cfa496009 100644 --- a/src/main/java/org/traccar/protocol/GnxProtocol.java +++ b/src/main/java/org/traccar/protocol/GnxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GnxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocol.java b/src/main/java/org/traccar/protocol/GoSafeProtocol.java index 607931500..c9c0456a0 100644 --- a/src/main/java/org/traccar/protocol/GoSafeProtocol.java +++ b/src/main/java/org/traccar/protocol/GoSafeProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GoSafeProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java index 77649a041..f17ea0e08 100644 --- a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java @@ -93,14 +93,14 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(values[index - 1]))); } position.setCourse(Integer.parseInt(values[index++])); - if (index < values.length) { - position.setAltitude(Integer.parseInt(values[index++])); + if (index < values.length && !values[index++].isEmpty()) { + position.setAltitude(Integer.parseInt(values[index - 1])); } - if (index < values.length) { - position.set(Position.KEY_HDOP, Double.parseDouble(values[index++])); + if (index < values.length && !values[index++].isEmpty()) { + position.set(Position.KEY_HDOP, Double.parseDouble(values[index - 1])); } - if (index < values.length) { - position.set(Position.KEY_VDOP, Double.parseDouble(values[index++])); + if (index < values.length && !values[index++].isEmpty()) { + position.set(Position.KEY_VDOP, Double.parseDouble(values[index - 1])); } break; case "GSM": diff --git a/src/main/java/org/traccar/protocol/GotopProtocol.java b/src/main/java/org/traccar/protocol/GotopProtocol.java index 53fcea0d0..21fbbae99 100644 --- a/src/main/java/org/traccar/protocol/GotopProtocol.java +++ b/src/main/java/org/traccar/protocol/GotopProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GotopProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gps056Protocol.java b/src/main/java/org/traccar/protocol/Gps056Protocol.java index dbffbfdbb..44fc392be 100644 --- a/src/main/java/org/traccar/protocol/Gps056Protocol.java +++ b/src/main/java/org/traccar/protocol/Gps056Protocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gps056Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gps103Protocol.java b/src/main/java/org/traccar/protocol/Gps103Protocol.java index 2725494c5..8424abfe5 100644 --- a/src/main/java/org/traccar/protocol/Gps103Protocol.java +++ b/src/main/java/org/traccar/protocol/Gps103Protocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gps103Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java index 28efa3c30..d1c35b478 100644 --- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -225,7 +225,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { getConfig(), parser.nextHexInt(0), parser.nextHexInt(0)))); } - if (parser.hasNext(20)) { + if (parser.hasNextAny(20)) { String utcHours = parser.next(); String utcMinutes = parser.next(); diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocol.java b/src/main/java/org/traccar/protocol/GpsGateProtocol.java index a6a73ae6b..db1e8554a 100644 --- a/src/main/java/org/traccar/protocol/GpsGateProtocol.java +++ b/src/main/java/org/traccar/protocol/GpsGateProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GpsGateProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java index 12b53342c..f50088b2b 100644 --- a/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java +++ b/src/main/java/org/traccar/protocol/GpsMarkerProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GpsMarkerProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java index a474b1e53..e146a816d 100644 --- a/src/main/java/org/traccar/protocol/GpsmtaProtocol.java +++ b/src/main/java/org/traccar/protocol/GpsmtaProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GpsmtaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/GranitProtocol.java b/src/main/java/org/traccar/protocol/GranitProtocol.java index bb66501e2..9ca0fe25e 100644 --- a/src/main/java/org/traccar/protocol/GranitProtocol.java +++ b/src/main/java/org/traccar/protocol/GranitProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class GranitProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gs100Protocol.java b/src/main/java/org/traccar/protocol/Gs100Protocol.java index 425ca9330..715d48fc4 100644 --- a/src/main/java/org/traccar/protocol/Gs100Protocol.java +++ b/src/main/java/org/traccar/protocol/Gs100Protocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gs100Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gt02Protocol.java b/src/main/java/org/traccar/protocol/Gt02Protocol.java index fa05761f6..f448feacc 100644 --- a/src/main/java/org/traccar/protocol/Gt02Protocol.java +++ b/src/main/java/org/traccar/protocol/Gt02Protocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gt02Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gt06Protocol.java b/src/main/java/org/traccar/protocol/Gt06Protocol.java index 38278121c..945ec3831 100644 --- a/src/main/java/org/traccar/protocol/Gt06Protocol.java +++ b/src/main/java/org/traccar/protocol/Gt06Protocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gt06Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 5b639ddfc..6c0380278 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BufferUtil; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -77,11 +78,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_HEARTBEAT = 0x23; // GK310 public static final int MSG_ADDRESS_REQUEST = 0x2A; // GK310 public static final int MSG_ADDRESS_RESPONSE = 0x97; // GK310 - public static final int MSG_GPS_LBS_5 = 0x31; // AZ735 - public static final int MSG_GPS_LBS_STATUS_4 = 0x32; // AZ735 - public static final int MSG_WIFI_5 = 0x33; // AZ735 - public static final int MSG_AZ735_GPS = 0x32; // AZ735 / only extended - public static final int MSG_AZ735_ALARM = 0x33; // AZ735 / only extended + public static final int MSG_GPS_LBS_5 = 0x31; // AZ735 & SL4X + public static final int MSG_GPS_LBS_STATUS_4 = 0x32; // AZ735 & SL4X + public static final int MSG_WIFI_5 = 0x33; // AZ735 & SL4X + public static final int MSG_LBS_3 = 0x34; // SL4X + public static final int MSG_AZ735_GPS = 0x32; // AZ735 (extended) + public static final int MSG_AZ735_ALARM = 0x33; // AZ735 (only extended) public static final int MSG_X1_GPS = 0x34; public static final int MSG_X1_PHOTO_INFO = 0x35; public static final int MSG_X1_PHOTO_DATA = 0x36; @@ -95,7 +97,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_INFO = 0x94; public static final int MSG_SERIAL = 0x9B; public static final int MSG_STRING_INFO = 0x21; - public static final int MSG_GPS_2 = 0xA0; // GK310 + public static final int MSG_GPS_LBS_7 = 0xA0; // GK310 & JM-VL03 public static final int MSG_LBS_2 = 0xA1; // GK310 public static final int MSG_WIFI_3 = 0xA2; // GK310 public static final int MSG_FENCE_SINGLE = 0xA3; // GK310 @@ -119,6 +121,10 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { SPACE10X, STANDARD, OBD6, + WETRUST, + JC400, + SL4X, + SEEWORLD, } private Variant variant; @@ -168,7 +174,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS_LBS_STATUS_4: case MSG_GPS_PHONE: case MSG_GPS_LBS_EXTEND: - case MSG_GPS_2: + case MSG_GPS_LBS_7: case MSG_FENCE_SINGLE: case MSG_FENCE_MULTI: return true; @@ -190,7 +196,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case MSG_GPS_LBS_STATUS_2: case MSG_GPS_LBS_STATUS_3: case MSG_GPS_LBS_STATUS_4: - case MSG_GPS_2: + case MSG_GPS_LBS_7: case MSG_FENCE_SINGLE: case MSG_FENCE_MULTI: case MSG_LBS_ALARM: @@ -267,12 +273,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } public static boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) { - return decodeGps(position, buf, hasLength, true, true, timezone); + return decodeGps(position, buf, hasLength, true, true, false, timezone); } public static boolean decodeGps( Position position, ByteBuf buf, boolean hasLength, boolean hasSatellites, - boolean hasSpeed, TimeZone timezone) { + boolean hasSpeed, boolean longSpeed, TimeZone timezone) { DateBuilder dateBuilder = new DateBuilder(timezone) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) @@ -291,7 +297,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { double longitude = buf.readUnsignedInt() / 60.0 / 30000.0; if (hasSpeed) { - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + position.setSpeed(UnitsConverter.knotsFromKph( + longSpeed ? buf.readUnsignedShort() : buf.readUnsignedByte())); } int flags = buf.readUnsignedShort(); @@ -337,21 +344,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { int mcc = buf.readUnsignedShort(); int mnc; - if (BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6) { + if (BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6 || variant == Variant.SL4X) { mnc = buf.readUnsignedShort(); } else { mnc = buf.readUnsignedByte(); } int lac; - if (type == MSG_LBS_ALARM) { + if (type == MSG_LBS_ALARM || type == MSG_GPS_LBS_7) { lac = buf.readInt(); } else { lac = buf.readUnsignedShort(); } long cid; - if (type == MSG_LBS_ALARM) { + if (type == MSG_LBS_ALARM || type == MSG_GPS_LBS_7) { cid = buf.readLong(); - } else if (type == MSG_GPS_LBS_6) { + } else if (type == MSG_GPS_LBS_6 || variant == Variant.SEEWORLD) { cid = buf.readUnsignedInt(); } else { cid = buf.readUnsignedMedium(); @@ -434,15 +441,18 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_REMOVING; case 0x23: return Position.ALARM_FALL_DOWN; + case 0x28: + return Position.ALARM_BRAKING; case 0x29: return Position.ALARM_ACCELERATION; - case 0x30: - return Position.ALARM_BRAKING; case 0x2A: case 0x2B: + case 0x2E: return Position.ALARM_CORNERING; case 0x2C: return Position.ALARM_ACCIDENT; + case 0x30: + return Position.ALARM_JAMMING; default: return null; } @@ -473,28 +483,21 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // type deviceSession = getDeviceSession(channel, remoteAddress, imei); - if (deviceSession != null && !deviceSession.contains(DeviceSession.KEY_TIMEZONE)) { - deviceSession.set(DeviceSession.KEY_TIMEZONE, getTimeZone(deviceSession.getDeviceId())); - } - - if (dataLength > 10) { - int extensionBits = buf.readUnsignedShort(); - int hours = (extensionBits >> 4) / 100; - int minutes = (extensionBits >> 4) % 100; - int offset = (hours * 60 + minutes) * 60; - if ((extensionBits & 0x8) != 0) { - offset = -offset; - } - if (deviceSession != null) { - TimeZone timeZone = deviceSession.get(DeviceSession.KEY_TIMEZONE); - if (timeZone.getRawOffset() == 0) { - timeZone.setRawOffset(offset * 1000); - deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone); + if (deviceSession != null) { + TimeZone timeZone = getTimeZone(deviceSession.getDeviceId(), null); + if (timeZone == null && dataLength > 10) { + int extensionBits = buf.readUnsignedShort(); + int hours = (extensionBits >> 4) / 100; + int minutes = (extensionBits >> 4) % 100; + int offset = (hours * 60 + minutes) * 60; + if ((extensionBits & 0x8) != 0) { + offset = -offset; } + timeZone = TimeZone.getTimeZone("UTC"); + timeZone.setRawOffset(offset * 1000); } - } + deviceSession.set(DeviceSession.KEY_TIMEZONE, timeZone); - if (deviceSession != null) { sendResponse(channel, false, type, buf.getShort(buf.writerIndex() - 6), null); } @@ -545,7 +548,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return null; - } else if (type == MSG_X1_GPS) { + } else if (type == MSG_X1_GPS && variant != Variant.SL4X) { buf.readUnsignedInt(); // data and alarm @@ -678,41 +681,50 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; } else if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_MULTIPLE_3 - || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI || type == MSG_LBS_2 + || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI || type == MSG_LBS_2 || type == MSG_LBS_3 || type == MSG_WIFI_3 || type == MSG_WIFI_5) { - boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5; - DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); getLastLocation(position, dateBuilder.getDate()); - if (variant == Variant.WANWAY_S20) { + if (variant == Variant.WANWAY_S20 || variant == Variant.SL4X) { buf.readUnsignedByte(); // ta } int mcc = buf.readUnsignedShort(); - int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte(); + int mnc = BitUtil.check(mcc, 15) || variant == Variant.SL4X + ? buf.readUnsignedShort() : buf.readUnsignedByte(); Network network = new Network(); int cellCount = variant == Variant.WANWAY_S20 ? buf.readUnsignedByte() : type == MSG_WIFI_5 ? 6 : 7; for (int i = 0; i < cellCount; i++) { - int lac = longFormat ? buf.readInt() : buf.readUnsignedShort(); - int cid = longFormat ? (int) buf.readLong() : buf.readUnsignedMedium(); + int lac; + int cid; + if (type == MSG_LBS_2 || type == MSG_WIFI_3) { + lac = buf.readInt(); + cid = (int) buf.readLong(); + } else if (type == MSG_WIFI_5 || type == MSG_LBS_3) { + lac = buf.readUnsignedShort(); + cid = (int) buf.readUnsignedInt(); + } else { + lac = buf.readUnsignedShort(); + cid = buf.readUnsignedMedium(); + } int rssi = -buf.readUnsignedByte(); if (lac > 0) { network.addCellTower(CellTower.from(BitUtil.to(mcc, 15), mnc, lac, cid, rssi)); } } - if (variant != Variant.WANWAY_S20) { + if (variant != Variant.WANWAY_S20 && variant != Variant.SL4X) { buf.readUnsignedByte(); // ta } if (type != MSG_LBS_MULTIPLE_1 && type != MSG_LBS_MULTIPLE_2 && type != MSG_LBS_MULTIPLE_3 - && type != MSG_LBS_2) { + && type != MSG_LBS_2 && type != MSG_LBS_3) { int wifiCount = buf.readUnsignedByte(); for (int i = 0; i < wifiCount; i++) { String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:"); @@ -805,7 +817,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } if (hasLbs(type) && buf.readableBytes() > 6) { - decodeLbs(position, buf, type, hasStatus(type) && type != MSG_LBS_ALARM && type != MSG_LBS_STATUS); + boolean hasLength = hasStatus(type) + && type != MSG_LBS_STATUS + && type != MSG_LBS_ALARM + && (type != MSG_GPS_LBS_STATUS_1 || variant != Variant.VXT01); + decodeLbs(position, buf, type, hasLength); } if (hasStatus(type)) { @@ -821,9 +837,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // working mode position.set(Position.KEY_POWER, buf.readUnsignedShort() / 100.0); } else { - position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 100 / 6); + int battery = buf.readUnsignedByte(); + position.set(Position.KEY_BATTERY_LEVEL, battery <= 6 ? battery * 100 / 6 : battery); position.set(Position.KEY_RSSI, buf.readUnsignedByte()); - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + short alarmExtension = buf.readUnsignedByte(); + if (variant != Variant.VXT01) { + position.set(Position.KEY_ALARM, decodeAlarm(alarmExtension)); + } } } @@ -833,7 +853,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); buf.readUnsignedByte(); // alarm buf.readUnsignedByte(); // swiped - position.set("driverLicense", data.trim()); + position.set(Position.KEY_CARD, data.trim()); } else if (variant == Variant.BENWAY) { int mask = buf.readUnsignedShort(); position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); @@ -869,9 +889,30 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } position.set(Position.PREFIX_TEMP + 1, temperature); position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10); + } else if (variant == Variant.WETRUST) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + position.set(Position.KEY_CARD, buf.readCharSequence( + buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_ALARM, buf.readUnsignedByte() > 0 ? Position.ALARM_GENERAL : null); + position.set("cardStatus", buf.readUnsignedByte()); + position.set(Position.KEY_DRIVING_TIME, buf.readUnsignedShort()); } } + if (type == MSG_GPS_LBS_2 && variant == Variant.SEEWORLD) { + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + buf.readUnsignedByte(); // reporting mode + buf.readUnsignedByte(); // supplementary transmission + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + buf.readUnsignedInt(); // travel time + int temperature = buf.readUnsignedShort(); + if (BitUtil.check(temperature, 15)) { + temperature = -BitUtil.to(temperature, 15); + } + position.set(Position.PREFIX_TEMP + 1, temperature * 0.01); + position.set("humidity", buf.readUnsignedShort() * 0.01); + } + if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3 || type == MSG_GPS_LBS_4) && buf.readableBytes() >= 3 + 6) { position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); @@ -898,6 +939,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } } + if (buf.readableBytes() == 3 + 6 || buf.readableBytes() == 3 + 4 + 6) { + position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0); + buf.readUnsignedByte(); // upload mode + position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0 ? true : null); + } + if (buf.readableBytes() == 4 + 6) { position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); } @@ -906,24 +953,44 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { boolean extendedAlarm = dataLength > 7; if (extendedAlarm) { - decodeGps(position, buf, false, false, false, deviceSession.get(DeviceSession.KEY_TIMEZONE)); + if (variant == Variant.JC400) { + buf.readUnsignedShort(); // marker + buf.readUnsignedByte(); // version + } + decodeGps( + position, buf, false, + variant == Variant.JC400, variant == Variant.JC400, variant == Variant.JC400, + deviceSession.get(DeviceSession.KEY_TIMEZONE)); } else { DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); getLastLocation(position, dateBuilder.getDate()); } - short alarmType = buf.readUnsignedByte(); - switch (alarmType) { + if (variant == Variant.JC400) { + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); + } + short event = buf.readUnsignedByte(); + position.set(Position.KEY_EVENT, event); + switch (event) { case 0x01: position.set(Position.KEY_ALARM, extendedAlarm ? Position.ALARM_SOS : Position.ALARM_GENERAL); break; + case 0x0E: + position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); + break; + case 0x76: + position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE); + break; case 0x80: position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION); break; case 0x87: position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); break; + case 0x88: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; case 0x90: position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); break; @@ -937,7 +1004,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); break; default: - position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); break; } @@ -1020,6 +1086,29 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.01); return position; + } else if (subType == 0x04) { + + CharSequence content = buf.readCharSequence(buf.readableBytes() - 4 - 2, StandardCharsets.US_ASCII); + String[] values = content.toString().split(";"); + for (String value : values) { + String[] pair = value.split("="); + switch (pair[0]) { + case "ALM1": + case "ALM2": + case "ALM3": + position.set("alarm" + pair[0].charAt(3) + "Status", Integer.parseInt(pair[1], 16)); + case "STA1": + position.set("otherStatus", Integer.parseInt(pair[1], 16)); + break; + case "DYD": + position.set("engineStatus", Integer.parseInt(pair[1], 16)); + break; + default: + break; + } + } + return position; + } else if (subType == 0x05) { if (buf.readableBytes() >= 6 + 1 + 6) { @@ -1339,19 +1428,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); buf.readUnsignedByte(); // external device type code - int length = buf.readableBytes() - 9; // line break + checksum + index + checksum + footer - if (length <= 0) { - return null; - } else if (length < 8) { - position.set( - Position.PREFIX_TEMP + 1, - Double.parseDouble(buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString())); + ByteBuf data = buf.readSlice(buf.readableBytes() - 6); // index + checksum + footer + if (BufferUtil.isPrintable(data, data.readableBytes())) { + String value = data.readCharSequence(data.readableBytes(), StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_RESULT, value.trim()); } else { - buf.readUnsignedByte(); // card type - position.set( - Position.KEY_DRIVER_UNIQUE_ID, - buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(data)); } return position; @@ -1391,6 +1474,18 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { variant = Variant.SPACE10X; } else if (header == 0x7878 && type == MSG_STATUS && length == 0x13) { variant = Variant.OBD6; + } else if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x29) { + variant = Variant.WETRUST; + } else if (header == 0x7878 && type == MSG_ALARM && buf.getUnsignedShort(buf.readerIndex() + 4) == 0xffff) { + variant = Variant.JC400; + } else if (header == 0x7878 && type == MSG_LBS_3 && length == 0x37) { + variant = Variant.SL4X; + } else if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_4 && length == 0x27) { + variant = Variant.SL4X; + } else if (header == 0x7878 && type == MSG_GPS_LBS_2 && length == 0x2f) { + variant = Variant.SEEWORLD; + } else if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_1 && length == 0x26) { + variant = Variant.SEEWORLD; } else { variant = Variant.STANDARD; } diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java index dc5dd446f..fd6bb8451 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolEncoder.java @@ -23,7 +23,6 @@ import org.traccar.config.Keys; import org.traccar.helper.Checksum; import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.model.Device; import java.nio.charset.StandardCharsets; @@ -74,11 +73,11 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder { String password = AttributeUtil.getDevicePassword( getCacheManager(), command.getDeviceId(), getProtocolName(), "123456"); - Device device = getCacheManager().getObject(Device.class, command.getDeviceId()); + String model = getDeviceModel(command.getDeviceId()); switch (command.getType()) { case Command.TYPE_ENGINE_STOP: - if ("G109".equals(device.getModel())) { + if ("G109".equals(model)) { return encodeContent(command.getDeviceId(), "DYD#"); } else if (alternative) { return encodeContent(command.getDeviceId(), "DYD," + password + "#"); @@ -86,7 +85,7 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder { return encodeContent(command.getDeviceId(), "Relay,1#"); } case Command.TYPE_ENGINE_RESUME: - if ("G109".equals(device.getModel())) { + if ("G109".equals(model)) { return encodeContent(command.getDeviceId(), "HFYD#"); } else if (alternative) { return encodeContent(command.getDeviceId(), "HFYD," + password + "#"); diff --git a/src/main/java/org/traccar/protocol/Gt30Protocol.java b/src/main/java/org/traccar/protocol/Gt30Protocol.java index 6b79ba58b..fdfc80502 100644 --- a/src/main/java/org/traccar/protocol/Gt30Protocol.java +++ b/src/main/java/org/traccar/protocol/Gt30Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Gt30Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/H02Protocol.java b/src/main/java/org/traccar/protocol/H02Protocol.java index 4e5f8c96a..ba5aeaa26 100644 --- a/src/main/java/org/traccar/protocol/H02Protocol.java +++ b/src/main/java/org/traccar/protocol/H02Protocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class H02Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/HaicomProtocol.java b/src/main/java/org/traccar/protocol/HaicomProtocol.java index f56c605f0..bcc491ada 100644 --- a/src/main/java/org/traccar/protocol/HaicomProtocol.java +++ b/src/main/java/org/traccar/protocol/HaicomProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class HaicomProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/HomtecsProtocol.java b/src/main/java/org/traccar/protocol/HomtecsProtocol.java index aa2d7d852..c04efb945 100644 --- a/src/main/java/org/traccar/protocol/HomtecsProtocol.java +++ b/src/main/java/org/traccar/protocol/HomtecsProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class HomtecsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/HoopoProtocol.java b/src/main/java/org/traccar/protocol/HoopoProtocol.java index 02d8e5a8e..3fc0887d8 100644 --- a/src/main/java/org/traccar/protocol/HoopoProtocol.java +++ b/src/main/java/org/traccar/protocol/HoopoProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class HoopoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java index 708c74f2a..7433e7fce 100644 --- a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java @@ -21,8 +21,8 @@ import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.time.OffsetDateTime; diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocol.java b/src/main/java/org/traccar/protocol/HuaShengProtocol.java index b1b61e977..7246e97e6 100644 --- a/src/main/java/org/traccar/protocol/HuaShengProtocol.java +++ b/src/main/java/org/traccar/protocol/HuaShengProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,17 +19,25 @@ import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; +import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class HuaShengProtocol extends BaseProtocol { @Inject public HuaShengProtocol(Config config) { + setSupportedDataCommands( + Command.TYPE_POSITION_PERIODIC, + Command.TYPE_OUTPUT_CONTROL, + Command.TYPE_ALARM_ARM, + Command.TYPE_ALARM_DISARM, + Command.TYPE_SET_SPEED_LIMIT); addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new HuaShengFrameDecoder()); + pipeline.addLast(new HuaShengProtocolEncoder(HuaShengProtocol.this)); pipeline.addLast(new HuaShengProtocolDecoder(HuaShengProtocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java index 371691d82..7d634b0f2 100644 --- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,10 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_UPFAULT_RSP = 0xFF13; public static final int MSG_HSO_REQ = 0x0002; public static final int MSG_HSO_RSP = 0x0003; + public static final int MSG_SET_REQ = 0xAA04; + public static final int MSG_SET_RSP = 0xFF05; + public static final int MSG_CTRL_REQ = 0xAA16; + public static final int MSG_CTRL_RSP = 0xFF17; private void sendResponse(Channel channel, int type, int index, ByteBuf content) { if (channel != null) { @@ -225,13 +229,14 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { position.setCourse(buf.readUnsignedShort()); position.setAltitude(buf.readUnsignedShort()); - position.set(Position.KEY_ODOMETER, buf.readUnsignedShort() * 1000); + buf.readUnsignedShort(); // odometer speed Network network = new Network(); while (buf.readableBytes() > 4) { int subtype = buf.readUnsignedShort(); int length = buf.readUnsignedShort() - 4; + int endIndex = buf.readerIndex() + length; switch (subtype) { case 0x0001: int coolantTemperature = buf.readUnsignedByte() - 40; @@ -249,6 +254,9 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4); buf.readUnsignedInt(); // trip id + if (buf.readerIndex() < endIndex) { + position.set("adBlueLevel", buf.readUnsignedByte() * 0.4); + } break; case 0x0005: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); @@ -256,8 +264,11 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedInt(); // run time break; case 0x0009: - position.set( - Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + position.set(Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + break; + case 0x0010: + position.set(Position.KEY_ODOMETER, Double.parseDouble( + buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()) * 1000); break; case 0x0011: position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05); @@ -276,7 +287,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { String[] values = cell.split("@"); network.addCellTower(CellTower.from( Integer.parseInt(values[0]), Integer.parseInt(values[1]), - Integer.parseInt(values[2], 16), Integer.parseInt(values[3], 16))); + Integer.parseInt(values[2], 16), Long.parseLong(values[3], 16))); } break; case 0x0021: @@ -291,6 +302,7 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder { buf.skipBytes(length); break; } + buf.readerIndex(endIndex); } if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java new file mode 100644 index 000000000..dc34f7b4e --- /dev/null +++ b/src/main/java/org/traccar/protocol/HuaShengProtocolEncoder.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.model.Command; + +public class HuaShengProtocolEncoder extends BaseProtocolEncoder { + + public HuaShengProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private ByteBuf encodeContent(int type, ByteBuf content) { + + ByteBuf buf = Unpooled.buffer(); + buf.writeByte(0xC0); + buf.writeShort(0x0000); // flag and version + buf.writeShort(12 + content.readableBytes()); + buf.writeShort(type); + buf.writeShort(0); // checksum + buf.writeInt(1); // index + buf.writeBytes(content); + content.release(); + buf.writeByte(0xC0); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + ByteBuf content = Unpooled.buffer(0); + switch (command.getType()) { + case Command.TYPE_POSITION_PERIODIC: + content.writeShort(0x0002); + content.writeShort(6); // length + content.writeShort(command.getInteger(Command.KEY_FREQUENCY)); + return encodeContent(HuaShengProtocolDecoder.MSG_SET_REQ, content); + case Command.TYPE_OUTPUT_CONTROL: + /* +0x01: Lock the relay1; //relay on +0x02: Unlock the relay1; //relay off +0x03: Lock the relay2; //relay2 on +0x04: Unlock the relay2; //relay2 off +0x05: Lock the relay3; //relay3 on +0x06: Unlock the relay3; //realy3 off + */ + content.writeByte( + (command.getInteger(Command.KEY_INDEX) - 1) * 2 + + (2 - command.getInteger(Command.KEY_DATA))); + return encodeContent(HuaShengProtocolDecoder.MSG_CTRL_REQ, content); + case Command.TYPE_ALARM_ARM: + case Command.TYPE_ALARM_DISARM: + content.writeShort(0x0001); + content.writeShort(5); // length + content.writeByte(command.getType().equals(Command.TYPE_ALARM_ARM) ? 1 : 0); + return encodeContent(HuaShengProtocolDecoder.MSG_SET_REQ, content); + case Command.TYPE_SET_SPEED_LIMIT: + content.writeShort(0x0004); + content.writeShort(6); // length + content.writeShort(command.getInteger(Command.KEY_DATA)); + return encodeContent(HuaShengProtocolDecoder.MSG_SET_REQ, content); + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocol.java b/src/main/java/org/traccar/protocol/HuabaoProtocol.java index c37918b0e..fc12d7d71 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocol.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class HuabaoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index d0bbeebb5..0a8540543 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -131,7 +131,10 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(value, 8)) { return Position.ALARM_POWER_OFF; } - if (BitUtil.check(value, 17)) { + if (BitUtil.check(value, 15)) { + return Position.ALARM_VIBRATION; + } + if (BitUtil.check(value, 16) || BitUtil.check(value, 17)) { return Position.ALARM_TAMPERING; } if (BitUtil.check(value, 20)) { @@ -140,7 +143,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(value, 28)) { return Position.ALARM_MOVEMENT; } - if (BitUtil.check(value, 29)) { + if (BitUtil.check(value, 29) || BitUtil.check(value, 30)) { return Position.ALARM_ACCIDENT; } return null; @@ -169,7 +172,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } else { long imei = id.getUnsignedShort(0); imei = (imei << 32) + id.getUnsignedInt(2); - return String.valueOf(imei); + return String.valueOf(imei) + Checksum.luhn(imei); } } @@ -294,6 +297,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_TRANSPARENT) { + sendGeneralResponse(channel, remoteAddress, id, type, index); + return decodeTransparent(deviceSession, buf); } @@ -327,6 +332,16 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x03: position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1); break; + case 0x56: + buf.readUnsignedByte(); // power level + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; + case 0x61: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + break; + case 0x69: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; case 0x80: position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte()); break; @@ -388,6 +403,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { int status = buf.readInt(); position.set(Position.KEY_IGNITION, BitUtil.check(status, 0)); + position.set(Position.KEY_MOTION, BitUtil.check(status, 4)); position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10)); position.set(Position.KEY_CHARGE, BitUtil.check(status, 26)); @@ -448,6 +464,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { int subtype = buf.readUnsignedByte(); int length = buf.readUnsignedByte(); int endIndex = buf.readerIndex() + length; + String stringValue; switch (subtype) { case 0x01: position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100); @@ -455,8 +472,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x02: position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1); break; + case 0x25: + position.set(Position.KEY_INPUT, buf.readUnsignedInt()); + break; case 0x2b: - position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt()); + position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort()); + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort()); break; case 0x30: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); @@ -465,12 +486,75 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); break; case 0x33: - String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); - if (sentence.startsWith("*M00")) { - String lockStatus = sentence.substring(8, 8 + 7); + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + if (stringValue.startsWith("*M00")) { + String lockStatus = stringValue.substring(8, 8 + 7); position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01); } break; + case 0x56: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 10); + buf.readUnsignedByte(); // reserved + break; + case 0x57: + int alarm = buf.readUnsignedShort(); + position.set(Position.KEY_ALARM, BitUtil.check(alarm, 8) ? Position.ALARM_ACCELERATION : null); + position.set(Position.KEY_ALARM, BitUtil.check(alarm, 9) ? Position.ALARM_BRAKING : null); + position.set(Position.KEY_ALARM, BitUtil.check(alarm, 10) ? Position.ALARM_CORNERING : null); + buf.readUnsignedShort(); // external switch state + buf.skipBytes(4); // reserved + break; + case 0x60: + int event = buf.readUnsignedShort(); + position.set(Position.KEY_EVENT, event); + if (event >= 0x0061 && event <= 0x0066) { + buf.skipBytes(6); // lock id + stringValue = buf.readCharSequence(8, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_DRIVER_UNIQUE_ID, stringValue); + } + break; + case 0x63: + for (int i = 1; i <= length / 11; i++) { + position.set("lock" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(6))); + position.set("lock" + i + "Battery", buf.readUnsignedShort() * 0.001); + position.set("lock" + i + "Seal", buf.readUnsignedByte() == 0x31); + buf.readUnsignedByte(); // physical state + buf.readUnsignedByte(); // rssi + } + break; + case 0x64: + buf.readUnsignedInt(); // alarm serial number + buf.readUnsignedByte(); // alarm status + position.set("adasAlarm", buf.readUnsignedByte()); + break; + case 0x65: + buf.readUnsignedInt(); // alarm serial number + buf.readUnsignedByte(); // alarm status + position.set("dmsAlarm", buf.readUnsignedByte()); + break; + case 0x70: + buf.readUnsignedInt(); // alarm serial number + buf.readUnsignedByte(); // alarm status + switch (buf.readUnsignedByte()) { + case 0x01: + position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); + break; + case 0x02: + position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); + break; + case 0x03: + position.set(Position.KEY_ALARM, Position.ALARM_CORNERING); + break; + case 0x16: + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + break; + default: + break; + } + break; + case 0x69: + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + break; case 0x80: buf.readUnsignedByte(); // content endIndex = buf.writerIndex() - 2; @@ -492,8 +576,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { break; case 0x94: if (length > 0) { - position.set( - Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_VIN, stringValue); } break; case 0xA7: @@ -503,6 +587,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0xAC: position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); break; + case 0xBC: + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set("driver", stringValue.trim()); + break; + case 0xBD: + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_DRIVER_UNIQUE_ID, stringValue); + break; case 0xD0: long userStatus = buf.readUnsignedInt(); if (BitUtil.check(userStatus, 3)) { @@ -513,7 +605,12 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); break; case 0xD4: - position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + case 0xE1: + if (length == 1) { + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + } else { + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedInt())); + } break; case 0xD5: if (length == 2) { @@ -536,6 +633,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2)); position.set("cover", BitUtil.check(deviceStatus, 3)); break; + case 0xE2: + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedInt() * 0.1); + break; case 0xE6: while (buf.readerIndex() < endIndex) { int sensorIndex = buf.readUnsignedByte(); @@ -589,8 +689,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } break; case 0xED: - String license = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim(); - position.set("driverLicense", license); + stringValue = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString(); + position.set(Position.KEY_CARD, stringValue.trim()); break; case 0xEE: position.set(Position.KEY_RSSI, buf.readUnsignedByte()); @@ -663,6 +763,8 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0xFE: if (length == 1) { position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + } else if (length == 2) { + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1); } else { int mark = buf.readUnsignedByte(); if (mark == 0x7C) { @@ -721,12 +823,15 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { int battery = buf.readUnsignedByte(); if (battery <= 100) { position.set(Position.KEY_BATTERY_LEVEL, battery); - } else if (battery == 0xAA) { + } else if (battery == 0xAA || battery == 0xAB) { position.set(Position.KEY_CHARGE, true); } - position.setNetwork(new Network(CellTower.fromCidLac( - getConfig(), buf.readUnsignedInt(), buf.readUnsignedShort()))); + long cid = buf.readUnsignedInt(); + int lac = buf.readUnsignedShort(); + if (cid > 0 && lac > 0) { + position.setNetwork(new Network(CellTower.fromCidLac(getConfig(), cid, lac))); + } int product = buf.readUnsignedByte(); int status = buf.readUnsignedShort(); @@ -738,6 +843,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } } else if (product == 3) { position.set(Position.KEY_BLOCKED, BitUtil.check(status, 5)); + if (BitUtil.check(alarm, 0)) { + position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); + } if (BitUtil.check(alarm, 1)) { position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER); } @@ -747,10 +855,69 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { if (BitUtil.check(alarm, 3)) { position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); } + if (BitUtil.check(alarm, 5)) { + position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_ENTER); + } + if (BitUtil.check(alarm, 6)) { + position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE_EXIT); + } } position.set(Position.KEY_STATUS, status); + while (buf.readableBytes() > 2) { + int id = buf.readUnsignedByte(); + int length = buf.readUnsignedByte(); + switch (id) { + case 0x02: + position.setAltitude(buf.readShort()); + break; + case 0x10: + position.set("wakeSource", buf.readUnsignedByte()); + break; + case 0x0A: + if (length == 3) { + buf.readUnsignedShort(); // mcc + buf.readUnsignedByte(); // mnc + } else { + buf.skipBytes(length); + } + break; + case 0x0B: + position.set("lockCommand", buf.readUnsignedByte()); + if (length >= 5 && length <= 6) { + position.set("lockCard", buf.readUnsignedInt()); + } else if (length >= 7) { + position.set("lockPassword", buf.readCharSequence(6, StandardCharsets.US_ASCII).toString()); + } + if (length % 2 == 0) { + position.set("unlockResult", buf.readUnsignedByte()); + } + break; + case 0x0C: + int x = buf.readUnsignedShort(); + if (x > 0x8000) { + x -= 0x10000; + } + int y = buf.readUnsignedShort(); + if (y > 0x8000) { + y -= 0x10000; + } + int z = buf.readUnsignedShort(); + if (z > 0x8000) { + z -= 0x10000; + } + position.set("tilt", String.format("[%d,%d,%d]", x, y, z)); + break; + case 0xFC: + position.set(Position.KEY_GEOFENCE, buf.readUnsignedByte()); + break; + default: + buf.skipBytes(length); + break; + } + } + return position; } @@ -782,6 +949,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { int type = buf.readUnsignedByte(); if (type == 0xF0) { + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -823,6 +991,34 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x0539: position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01); break; + case 0x052B: + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte()); + break; + case 0x052D: + position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40); + break; + case 0x052E: + position.set("airTemp", buf.readUnsignedByte() - 40); + break; + case 0x0530: + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001); + break; + case 0x0535: + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1); + break; + case 0x0536: + position.set(Position.KEY_RPM, buf.readUnsignedShort()); + break; + case 0x053D: + position.set("intakePressure", buf.readUnsignedShort() * 0.1); + break; + case 0x0544: + position.set("liquidLevel", buf.readUnsignedByte()); + break; + case 0x0547: + case 0x0548: + position.set(Position.KEY_THROTTLE, buf.readUnsignedByte()); + break; default: switch (length) { case 1: @@ -841,15 +1037,40 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { break; } } + getLastLocation(position, time); + decodeCoordinates(position, buf); + position.setTime(time); + break; + case 0x02: + List<String> codes = new LinkedList<>(); + count = buf.readUnsignedShort(); + for (int i = 0; i < count; i++) { + buf.readUnsignedInt(); // system id + int codeCount = buf.readUnsignedShort(); + for (int j = 0; j < codeCount; j++) { + buf.readUnsignedInt(); // dtc + buf.readUnsignedInt(); // status + codes.add(buf.readCharSequence( + buf.readUnsignedShort(), StandardCharsets.US_ASCII).toString().trim()); + } + } + position.set(Position.KEY_DTCS, String.join(" ", codes)); + getLastLocation(position, time); decodeCoordinates(position, buf); position.setTime(time); break; case 0x03: count = buf.readUnsignedByte(); for (int i = 0; i < count; i++) { - int id = buf.readUnsignedShort(); + int id = buf.readUnsignedByte(); int length = buf.readUnsignedByte(); switch (id) { + case 0x01: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED); + break; + case 0x02: + position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT); + break; case 0x1A: position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); break; @@ -867,11 +1088,21 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { case 0x23: position.set(Position.KEY_ALARM, Position.ALARM_FATIGUE_DRIVING); break; + case 0x26: + case 0x27: + case 0x28: + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + break; + case 0x31: + case 0x32: + position.set(Position.KEY_ALARM, Position.ALARM_DOOR); + break; default: break; } buf.skipBytes(length); } + getLastLocation(position, time); decodeCoordinates(position, buf); position.setTime(time); break; @@ -886,6 +1117,24 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { } return position; + + } else if (type == 0xFF) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(readDate(buf, deviceSession.get(DeviceSession.KEY_TIMEZONE))); + position.setLatitude(buf.readInt() * 0.000001); + position.setLongitude(buf.readInt() * 0.000001); + position.setAltitude(buf.readShort()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1)); + position.setCourse(buf.readUnsignedShort()); + + // TODO more positions and g sensor data + + return position; + } return null; diff --git a/src/main/java/org/traccar/protocol/HunterProProtocol.java b/src/main/java/org/traccar/protocol/HunterProProtocol.java index ed4289d73..64dab33b1 100644 --- a/src/main/java/org/traccar/protocol/HunterProProtocol.java +++ b/src/main/java/org/traccar/protocol/HunterProProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class HunterProProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/IdplProtocol.java b/src/main/java/org/traccar/protocol/IdplProtocol.java index aa1f4ff5b..1e44ad74c 100644 --- a/src/main/java/org/traccar/protocol/IdplProtocol.java +++ b/src/main/java/org/traccar/protocol/IdplProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class IdplProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocol.java b/src/main/java/org/traccar/protocol/IntellitracProtocol.java index b1a91cca9..a82e6a5db 100644 --- a/src/main/java/org/traccar/protocol/IntellitracProtocol.java +++ b/src/main/java/org/traccar/protocol/IntellitracProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class IntellitracProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/IotmProtocol.java b/src/main/java/org/traccar/protocol/IotmProtocol.java index 0d288f4bf..1631b67d8 100644 --- a/src/main/java/org/traccar/protocol/IotmProtocol.java +++ b/src/main/java/org/traccar/protocol/IotmProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class IotmProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java index 7bbe6c8de..d9e6670c6 100644 --- a/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/IotmProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,27 +17,19 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; -import io.netty.channel.Channel; -import io.netty.handler.codec.mqtt.MqttConnectMessage; -import io.netty.handler.codec.mqtt.MqttConnectReturnCode; -import io.netty.handler.codec.mqtt.MqttMessage; -import io.netty.handler.codec.mqtt.MqttMessageBuilders; import io.netty.handler.codec.mqtt.MqttPublishMessage; -import io.netty.handler.codec.mqtt.MqttSubscribeMessage; -import org.traccar.BaseProtocolDecoder; -import org.traccar.session.DeviceSession; -import org.traccar.NetworkMessage; +import org.traccar.BaseMqttProtocolDecoder; import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; +import org.traccar.session.DeviceSession; -import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.LinkedList; import java.util.List; -public class IotmProtocolDecoder extends BaseProtocolDecoder { +public class IotmProtocolDecoder extends BaseMqttProtocolDecoder { public IotmProtocolDecoder(Protocol protocol) { super(protocol); @@ -236,121 +228,72 @@ public class IotmProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - - if (msg instanceof MqttConnectMessage) { - - MqttConnectMessage message = (MqttConnectMessage) msg; + DeviceSession deviceSession, MqttPublishMessage message) throws Exception { - DeviceSession deviceSession = getDeviceSession( - channel, remoteAddress, message.payload().clientIdentifier()); - - MqttConnectReturnCode returnCode = deviceSession != null - ? MqttConnectReturnCode.CONNECTION_ACCEPTED - : MqttConnectReturnCode.CONNECTION_REFUSED_IDENTIFIER_REJECTED; - - MqttMessage response = MqttMessageBuilders.connAck().returnCode(returnCode).build(); - - if (channel != null) { - channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); - } + List<Position> positions = new LinkedList<>(); - } else if (msg instanceof MqttSubscribeMessage) { + ByteBuf buf = message.payload(); - MqttSubscribeMessage message = (MqttSubscribeMessage) msg; - - MqttMessage response = MqttMessageBuilders.subAck() - .packetId(message.variableHeader().messageId()) - .build(); - - if (channel != null) { - channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); - } - - } else if (msg instanceof MqttPublishMessage) { - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - if (deviceSession == null) { - return null; - } + buf.readUnsignedByte(); // structure version - List<Position> positions = new LinkedList<>(); + while (buf.readableBytes() > 1) { + int type = buf.readUnsignedByte(); + int length = buf.readUnsignedShortLE(); + ByteBuf record = buf.readSlice(length); + if (type == 1) { - MqttPublishMessage message = (MqttPublishMessage) msg; - ByteBuf buf = message.payload(); + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.setTime(new Date(record.readUnsignedIntLE() * 1000)); - buf.readUnsignedByte(); // structure version + while (record.readableBytes() > 0) { + int sensorType = record.readUnsignedByte(); + int sensorId = record.readUnsignedShortLE(); + if (sensorType == 14) { - while (buf.readableBytes() > 1) { - int type = buf.readUnsignedByte(); - int length = buf.readUnsignedShortLE(); - ByteBuf record = buf.readSlice(length); - if (type == 1) { + position.setValid(true); + position.setLatitude(record.readFloatLE()); + position.setLongitude(record.readFloatLE()); + position.setSpeed(UnitsConverter.knotsFromKph(record.readUnsignedShortLE())); - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - position.setTime(new Date(record.readUnsignedIntLE() * 1000)); + position.set(Position.KEY_HDOP, record.readUnsignedByte()); + position.set(Position.KEY_SATELLITES, record.readUnsignedByte()); - while (record.readableBytes() > 0) { - int sensorType = record.readUnsignedByte(); - int sensorId = record.readUnsignedShortLE(); - if (sensorType == 14) { + position.setCourse(record.readUnsignedShortLE()); + position.setAltitude(record.readShortLE()); - position.setValid(true); - position.setLatitude(record.readFloatLE()); - position.setLongitude(record.readFloatLE()); - position.setSpeed(UnitsConverter.knotsFromKph(record.readUnsignedShortLE())); - - position.set(Position.KEY_HDOP, record.readUnsignedByte()); - position.set(Position.KEY_SATELLITES, record.readUnsignedByte()); - - position.setCourse(record.readUnsignedShortLE()); - position.setAltitude(record.readShortLE()); - - } else { - - if (sensorType == 3) { - continue; - } - - decodeSensor(position, record, sensorType, sensorId); + } else { + if (sensorType == 3) { + continue; } - } - - positions.add(position); - } else if (type == 3) { + decodeSensor(position, record, sensorType, sensorId); - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); + } + } - getLastLocation(position, new Date(record.readUnsignedIntLE() * 1000)); + positions.add(position); - record.readUnsignedByte(); // function identifier + } else if (type == 3) { - position.set(Position.KEY_EVENT, record.readUnsignedByte()); + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); - positions.add(position); + getLastLocation(position, new Date(record.readUnsignedIntLE() * 1000)); - } - } + record.readUnsignedByte(); // function identifier - buf.readUnsignedByte(); // checksum + position.set(Position.KEY_EVENT, record.readUnsignedByte()); - MqttMessage response = MqttMessageBuilders.pubAck() - .packetId(message.variableHeader().packetId()) - .build(); + positions.add(position); - if (channel != null) { - channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } - - return positions.isEmpty() ? null : positions; - } - return null; + buf.readUnsignedByte(); // checksum + + return positions.isEmpty() ? null : positions; } } diff --git a/src/main/java/org/traccar/protocol/ItsProtocol.java b/src/main/java/org/traccar/protocol/ItsProtocol.java index 5148e8ab0..7d59ea60c 100644 --- a/src/main/java/org/traccar/protocol/ItsProtocol.java +++ b/src/main/java/org/traccar/protocol/ItsProtocol.java @@ -23,7 +23,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ItsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Ivt401Protocol.java b/src/main/java/org/traccar/protocol/Ivt401Protocol.java index 763457641..5132c7467 100644 --- a/src/main/java/org/traccar/protocol/Ivt401Protocol.java +++ b/src/main/java/org/traccar/protocol/Ivt401Protocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Ivt401Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/JidoProtocol.java b/src/main/java/org/traccar/protocol/JidoProtocol.java index 78aa6c81c..b30cc586a 100644 --- a/src/main/java/org/traccar/protocol/JidoProtocol.java +++ b/src/main/java/org/traccar/protocol/JidoProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class JidoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java index 30c8e9977..ae312ea3e 100644 --- a/src/main/java/org/traccar/protocol/JpKorjarProtocol.java +++ b/src/main/java/org/traccar/protocol/JpKorjarProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class JpKorjarProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java index bfefb94a7..f7890f814 100644 --- a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java +++ b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java @@ -35,7 +35,7 @@ public class Jt600FrameDecoder extends BaseFrameDecoder { char type = (char) buf.getByte(buf.readerIndex()); if (type == '$') { - boolean longFormat = Jt600ProtocolDecoder.isLongFormat(buf, buf.readerIndex() + 1); + boolean longFormat = Jt600ProtocolDecoder.isLongFormat(buf); int length = buf.getUnsignedShort(buf.readerIndex() + (longFormat ? 8 : 7)) + 10; if (length <= buf.readableBytes()) { return buf.readRetainedSlice(length); diff --git a/src/main/java/org/traccar/protocol/Jt600Protocol.java b/src/main/java/org/traccar/protocol/Jt600Protocol.java index bf0b3379e..9dc62662f 100644 --- a/src/main/java/org/traccar/protocol/Jt600Protocol.java +++ b/src/main/java/org/traccar/protocol/Jt600Protocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Jt600Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java index 9ed44f565..eca7e2d11 100644 --- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java @@ -86,8 +86,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { } - static boolean isLongFormat(ByteBuf buf, int flagIndex) { - return buf.getUnsignedByte(flagIndex) >> 4 >= 7; + static boolean isLongFormat(ByteBuf buf) { + return buf.getUnsignedByte(buf.readerIndex() + 8) == 0; } static void decodeBinaryLocation(ByteBuf buf, Position position) { @@ -105,15 +105,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9)); byte flags = buf.readByte(); - position.setValid((flags & 0x1) == 0x1); - if ((flags & 0x2) == 0) { - latitude = -latitude; - } - position.setLatitude(latitude); - if ((flags & 0x4) == 0) { - longitude = -longitude; - } - position.setLongitude(longitude); + position.setValid(BitUtil.check(flags, 0)); + position.setLatitude(BitUtil.check(flags, 1) ? latitude : -latitude); + position.setLongitude(BitUtil.check(flags, 2) ? longitude : -longitude); position.setSpeed(BcdUtil.readInteger(buf, 2)); position.setCourse(buf.readUnsignedByte() * 2.0); @@ -123,9 +117,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { List<Position> positions = new LinkedList<>(); - buf.readByte(); // header + boolean longFormat = isLongFormat(buf); - boolean longFormat = isLongFormat(buf, buf.readerIndex()); + buf.readByte(); // header String id = String.valueOf(Long.parseLong(ByteBufUtil.hexDump(buf.readSlice(5)))); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); @@ -386,7 +380,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { .expression("([AV]),") // validity .number("(d+),") // speed .number("(d+),") // course - .number("d+,") // event source + .number("(d+),") // event source .number("d+,") // unlock verification .number("(d+),") // rfid .number("d+,") // password verification @@ -419,6 +413,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble())); position.setCourse(parser.nextDouble()); + position.set("eventSource", parser.nextInt()); + String rfid = parser.next(); if (!rfid.equals("0000000000")) { position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid); diff --git a/src/main/java/org/traccar/protocol/KenjiProtocol.java b/src/main/java/org/traccar/protocol/KenjiProtocol.java index 8d78c8c56..b4e610cbd 100644 --- a/src/main/java/org/traccar/protocol/KenjiProtocol.java +++ b/src/main/java/org/traccar/protocol/KenjiProtocol.java @@ -24,7 +24,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class KenjiProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/KhdProtocol.java b/src/main/java/org/traccar/protocol/KhdProtocol.java index 521274de5..add13ef16 100644 --- a/src/main/java/org/traccar/protocol/KhdProtocol.java +++ b/src/main/java/org/traccar/protocol/KhdProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class KhdProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java index d7c236c4f..e88b34478 100644 --- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BufferUtil; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -169,7 +170,9 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_FUEL_LEVEL, BitUtil.from(odometer, 16)); } - position.set(Position.KEY_STATUS, buf.readUnsignedInt()); + long status = buf.readUnsignedInt(); + position.set(Position.KEY_IGNITION, BitUtil.check(status, 7 + 3 * 8)); + position.set(Position.KEY_STATUS, status); buf.readUnsignedShort(); buf.readUnsignedByte(); @@ -185,8 +188,7 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // data length int dataType = buf.readUnsignedByte(); - - buf.readUnsignedByte(); // content length + int dataLength = buf.readUnsignedByte(); switch (dataType) { case 0x01: @@ -197,6 +199,20 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte() * 100 + buf.readUnsignedByte()); break; + case 0x05: + int sign = buf.readUnsignedByte(); + switch (sign) { + case 1: + position.set("sign", true); + break; + case 2: + position.set("sign", false); + break; + default: + break; + } + position.set(Position.KEY_DRIVER_UNIQUE_ID, BufferUtil.readString(buf, dataLength - 1)); + break; case 0x18: for (int i = 1; i <= 4; i++) { double value = buf.readUnsignedShort(); @@ -205,6 +221,9 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { } } break; + case 0x20: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + break; case 0x23: Network network = new Network(); int count = buf.readUnsignedByte(); diff --git a/src/main/java/org/traccar/protocol/L100Protocol.java b/src/main/java/org/traccar/protocol/L100Protocol.java index 0edea6095..fa6d1b07e 100644 --- a/src/main/java/org/traccar/protocol/L100Protocol.java +++ b/src/main/java/org/traccar/protocol/L100Protocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class L100Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/LacakProtocol.java b/src/main/java/org/traccar/protocol/LacakProtocol.java index bbebd51ed..ddaf5078d 100644 --- a/src/main/java/org/traccar/protocol/LacakProtocol.java +++ b/src/main/java/org/traccar/protocol/LacakProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class LacakProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java index 809fafc90..66aab3490 100644 --- a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java @@ -24,8 +24,8 @@ import org.traccar.Protocol; import org.traccar.helper.DateUtil; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/LaipacProtocol.java b/src/main/java/org/traccar/protocol/LaipacProtocol.java index 249d3bcbe..65b1a57e9 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocol.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class LaipacProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java index e9570ee11..343d42e09 100644 --- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java @@ -28,6 +28,7 @@ import org.traccar.helper.PatternBuilder; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.helper.BitUtil; import java.net.SocketAddress; import java.util.regex.Pattern; @@ -108,19 +109,31 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { } } - private String decodeEvent(String event, Position position) { + private String decodeEvent(String event, Position position, String model) { if (event.length() == 1) { char inputStatus = event.charAt(0); if (inputStatus >= 'A' && inputStatus <= 'D') { int inputStatusInt = inputStatus - 'A'; - position.set(Position.PREFIX_IN + 1, inputStatusInt & 1); - position.set(Position.PREFIX_IN + 2, inputStatusInt & 2); + position.set(Position.PREFIX_IN + 1, (boolean) BitUtil.check(inputStatusInt, 0)); + position.set(Position.PREFIX_IN + 2, (boolean) BitUtil.check(inputStatusInt, 1)); + if ("SF-Lite".equals(model)) { + position.set(Position.PREFIX_IN + 3, false); + } + return null; + } else if (inputStatus >= 'O' && inputStatus <= 'R') { + int inputStatusInt = inputStatus - 'O'; + position.set(Position.PREFIX_IN + 1, (boolean) BitUtil.check(inputStatusInt, 0)); + position.set(Position.PREFIX_IN + 2, (boolean) BitUtil.check(inputStatusInt, 1)); + if ("SF-Lite".equals(model)) { + position.set(Position.PREFIX_IN + 3, true); + } return null; } } return event; + } private void sendEventResponse( @@ -132,6 +145,9 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { case "3": responseCode = "d"; break; + case "M": + responseCode = "m"; + break; case "S": case "T": responseCode = "t"; @@ -209,6 +225,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { return null; } + String model = getDeviceModel(deviceSession); + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -230,12 +248,17 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder { String event = parser.next(); position.set(Position.KEY_ALARM, decodeAlarm(event)); - position.set(Position.KEY_EVENT, decodeEvent(event, position)); + position.set(Position.KEY_EVENT, decodeEvent(event, position, model)); position.set(Position.KEY_BATTERY, Double.parseDouble(parser.next().replaceAll("\\.", "")) * 0.001); position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000); position.set(Position.KEY_GPS, parser.nextInt()); position.set(Position.PREFIX_ADC + 1, parser.nextDouble() * 0.001); - position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001); + + if ("AVL110".equals(model) || "AVL120".equals(model)) { + position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001); + } else { + parser.next(); + } Integer lac = parser.nextHexInt(); Integer cid = parser.nextHexInt(); diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java index 7e13e23d0..9e167e7ba 100644 --- a/src/main/java/org/traccar/protocol/LeafSpyProtocol.java +++ b/src/main/java/org/traccar/protocol/LeafSpyProtocol.java @@ -24,7 +24,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class LeafSpyProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/M2cProtocol.java b/src/main/java/org/traccar/protocol/M2cProtocol.java index a23ea0f57..8abc30f60 100644 --- a/src/main/java/org/traccar/protocol/M2cProtocol.java +++ b/src/main/java/org/traccar/protocol/M2cProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class M2cProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/M2mProtocol.java b/src/main/java/org/traccar/protocol/M2mProtocol.java index 6809d800c..03a069d66 100644 --- a/src/main/java/org/traccar/protocol/M2mProtocol.java +++ b/src/main/java/org/traccar/protocol/M2mProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class M2mProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MaestroProtocol.java b/src/main/java/org/traccar/protocol/MaestroProtocol.java index 38a67f9a4..29f0b8897 100644 --- a/src/main/java/org/traccar/protocol/MaestroProtocol.java +++ b/src/main/java/org/traccar/protocol/MaestroProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MaestroProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ManPowerProtocol.java b/src/main/java/org/traccar/protocol/ManPowerProtocol.java index 492e86605..ba2414ca7 100644 --- a/src/main/java/org/traccar/protocol/ManPowerProtocol.java +++ b/src/main/java/org/traccar/protocol/ManPowerProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ManPowerProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java index cf65a2db3..916fb7467 100644 --- a/src/main/java/org/traccar/protocol/Mavlink2Protocol.java +++ b/src/main/java/org/traccar/protocol/Mavlink2Protocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Mavlink2Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MegastekProtocol.java b/src/main/java/org/traccar/protocol/MegastekProtocol.java index 10215eb7c..9f8937f01 100644 --- a/src/main/java/org/traccar/protocol/MegastekProtocol.java +++ b/src/main/java/org/traccar/protocol/MegastekProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MegastekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java index 492094ce3..d86a00fb3 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocol.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MeiligaoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java index f3b56973a..1f8c4d2da 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java @@ -282,7 +282,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_RSSI, parser.nextHexInt()); position.set(Position.KEY_ODOMETER, parser.nextHexLong()); position.set(Position.KEY_SATELLITES, parser.nextHexInt()); - position.set("driverLicense", parser.next()); + position.set(Position.KEY_CARD, parser.next()); position.set(Position.KEY_ODOMETER, parser.nextLong()); position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java index 5859d91ce..6d3b4f7e9 100644 --- a/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolEncoder.java @@ -24,7 +24,6 @@ import org.traccar.helper.Checksum; import org.traccar.helper.DataConverter; import org.traccar.helper.model.AttributeUtil; import org.traccar.model.Command; -import org.traccar.model.Device; import java.nio.charset.StandardCharsets; import java.util.Set; @@ -64,7 +63,7 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { int outputCount; int outputType; - String model = getCacheManager().getObject(Device.class, deviceId).getModel(); + String model = getDeviceModel(deviceId); if (model != null && Set.of("TK510", "GT08", "TK208", "TK228", "MT05").contains(model)) { outputCount = 5; diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocol.java b/src/main/java/org/traccar/protocol/MeitrackProtocol.java index c6eba8fe1..4109b22c9 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocol.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MeitrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java index 343141dca..88b6380a5 100644 --- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,10 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.model.Device; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -30,12 +30,14 @@ import org.traccar.helper.UnitsConverter; import org.traccar.model.CellTower; import org.traccar.model.Network; import org.traccar.model.Position; +import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.regex.Pattern; public class MeitrackProtocolDecoder extends BaseProtocolDecoder { @@ -204,11 +206,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_ADC + i, parser.nextHexInt()); } - String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel(); - if (model == null) { - model = ""; - } - switch (model.toUpperCase()) { + switch (Objects.requireNonNullElse(getDeviceModel(deviceSession), "").toUpperCase()) { case "MVT340": case "MVT380": position.set(Position.KEY_BATTERY, parser.nextHexInt() * 3.0 * 2.0 / 1024.0); @@ -394,6 +392,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); + Network network = new Network(); + buf.readUnsignedShortLE(); // length buf.readUnsignedShortLE(); // index @@ -420,6 +420,12 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x15: position.set(Position.KEY_INPUT, buf.readUnsignedByte()); break; + case 0x47: + int lockState = buf.readUnsignedByte(); + if (lockState > 0) { + position.set(Position.KEY_LOCK, lockState == 2); + } + break; case 0x97: position.set(Position.KEY_THROTTLE, buf.readUnsignedByte()); break; @@ -455,12 +461,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { case 0x16: position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE() * 0.01); break; + case 0x17: + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE() * 0.01); + break; case 0x19: position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01); break; case 0x1A: position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01); break; + case 0x29: + position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShortLE() * 0.01); + break; case 0x40: position.set(Position.KEY_EVENT, buf.readUnsignedShortLE()); break; @@ -504,18 +516,26 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 2000-01-01 break; case 0x0C: - case 0x9B: position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); break; case 0x0D: position.set("runtime", buf.readUnsignedIntLE()); break; + case 0x25: + position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedIntLE())); + break; + case 0x9B: + position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedIntLE()); + break; case 0xA0: position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE() * 0.001); break; case 0xA2: position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE() * 0.01); break; + case 0xFEF4: + position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 60000); + break; default: buf.readUnsignedIntLE(); break; @@ -528,6 +548,28 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { int id = extension ? buf.readUnsignedShort() : buf.readUnsignedByte(); int length = buf.readUnsignedByte(); switch (id) { + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + String wifiMac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:"); + network.addWifiAccessPoint(WifiAccessPoint.from( + wifiMac.substring(0, wifiMac.length() - 1), buf.readShortLE())); + break; + case 0x0E: + case 0x0F: + case 0x10: + case 0x12: + case 0x13: + network.addCellTower(CellTower.from( + buf.readUnsignedShortLE(), buf.readUnsignedShortLE(), + buf.readUnsignedShortLE(), buf.readUnsignedIntLE(), buf.readShortLE())); + break; case 0x2A: case 0x2B: case 0x2C: @@ -539,17 +581,48 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // label position.set(Position.PREFIX_TEMP + (id - 0x2A), buf.readShortLE() * 0.01); break; + case 0x4B: + buf.skipBytes(length); // network information + break; case 0xFE31: buf.readUnsignedByte(); // alarm protocol buf.readUnsignedByte(); // alarm type buf.skipBytes(length - 2); break; + case 0xFE73: + buf.readUnsignedByte(); // version + position.set( + "tagName", + buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString()); + buf.skipBytes(6); // mac + position.set("tagBattery", buf.readUnsignedByte()); + position.set("tagTemp", buf.readUnsignedShortLE() / 256.0); + position.set("tagHumidity", buf.readUnsignedShortLE() / 256.0); + buf.readUnsignedShortLE(); // high temperature threshold + buf.readUnsignedShortLE(); // low temperature threshold + buf.readUnsignedShortLE(); // high humidity threshold + buf.readUnsignedShortLE(); // low humidity threshold + break; + case 0xFEA8: + for (int k = 1; k <= 3; k++) { + if (buf.readUnsignedByte() > 0) { + String key = k == 1 ? Position.KEY_BATTERY_LEVEL : "battery" + k + "Level"; + position.set(key, buf.readUnsignedByte()); + } else { + buf.readUnsignedByte(); + } + } + buf.readUnsignedByte(); // battery alert + break; default: buf.skipBytes(length); break; } } + if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) { + position.setNetwork(network); + } positions.add(position); } @@ -624,6 +697,13 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { photo = Unpooled.buffer(); requestPhotoPacket(channel, remoteAddress, imei, "camera_picture.jpg", 0); return null; + case "D82": + Position position = new Position(getProtocolName()); + position.setDeviceId(getDeviceSession(channel, remoteAddress, imei).getDeviceId()); + getLastLocation(position, null); + String result = buf.toString(index + 1, buf.writerIndex() - index - 4, StandardCharsets.US_ASCII); + position.set(Position.KEY_RESULT, result); + return position; case "CCC": return decodeBinaryC(channel, remoteAddress, buf); case "CCE": diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java index ccbc4db4c..08bbe0c82 100644 --- a/src/main/java/org/traccar/protocol/MictrackProtocol.java +++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MictrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MilesmateProtocol.java b/src/main/java/org/traccar/protocol/MilesmateProtocol.java index 59212e791..607dfc5bf 100644 --- a/src/main/java/org/traccar/protocol/MilesmateProtocol.java +++ b/src/main/java/org/traccar/protocol/MilesmateProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MilesmateProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java index 44599accc..1cb2a0007 100644 --- a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java +++ b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MiniFinderProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java index f2e5eb905..1fdb1ece0 100644 --- a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java @@ -143,7 +143,7 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { } DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); - if (deviceSession == null || !sentence.matches("![35A-D],.*")) { + if (deviceSession == null || !sentence.matches("![345A-D],.*")) { return null; } @@ -161,6 +161,20 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type.equals("4")) { + + String[] values = sentence.split(","); + + getLastLocation(position, null); + + for (int i = 1; i <= 3; i++) { + if (!values[i + 1].isEmpty()) { + position.set("phone" + i, values[i + 1]); + } + } + + return position; + } else if (type.equals("5")) { String[] values = sentence.split(","); diff --git a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java index 5499a274e..082b9146d 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2Protocol.java +++ b/src/main/java/org/traccar/protocol/Minifinder2Protocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,19 +20,24 @@ import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; +import org.traccar.model.Command; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Minifinder2Protocol extends BaseProtocol { @Inject public Minifinder2Protocol(Config config) { + setSupportedDataCommands( + Command.TYPE_FIRMWARE_UPDATE, + Command.TYPE_CONFIGURATION); addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { - pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1200, 2, 2, 4, 0, true)); + pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 2048, 2, 2, 4, 0, true)); + pipeline.addLast(new Minifinder2ProtocolEncoder(Minifinder2Protocol.this)); pipeline.addLast(new Minifinder2ProtocolDecoder(Minifinder2Protocol.this)); } }); diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java index 0b08badb8..64373e344 100644 --- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BufferUtil; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -48,6 +49,8 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_DATA = 0x01; public static final int MSG_CONFIGURATION = 0x02; public static final int MSG_SERVICES = 0x03; + public static final int MSG_SYSTEM_CONTROL = 0x04; + public static final int MSG_FIRMWARE = 0x7E; public static final int MSG_RESPONSE = 0x7F; private String decodeAlarm(long code) { @@ -146,14 +149,13 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { int type = buf.readUnsignedByte(); if (BitUtil.check(flags, 4)) { - sendResponse(channel, remoteAddress, index, type, buf); + sendResponse(channel, remoteAddress, index, type, buf.slice()); } - if (type == MSG_DATA) { + if (type == MSG_DATA || type == MSG_SERVICES) { List<Position> positions = new LinkedList<>(); Set<Integer> keys = new HashSet<>(); - boolean hasLocation = false; Position position = new Position(getProtocolName()); DeviceSession deviceSession = null; @@ -163,12 +165,8 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { int key = buf.readUnsignedByte(); if (keys.contains(key)) { - if (!hasLocation) { - getLastLocation(position, null); - } positions.add(position); keys.clear(); - hasLocation = false; position = new Position(getProtocolName()); } keys.add(key); @@ -177,8 +175,9 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { case 0x01: deviceSession = getDeviceSession( channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString()); - - position.setDeviceId(deviceSession.getDeviceId()); + if (deviceSession == null) { + return null; + } break; case 0x02: long alarm = buf.readUnsignedIntLE(); @@ -192,7 +191,6 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001); break; case 0x20: - hasLocation = true; position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); @@ -232,11 +230,18 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); position.setValid(true); - hasLocation = true; break; case 0x24: position.setTime(new Date(buf.readUnsignedIntLE() * 1000)); long status = buf.readUnsignedIntLE(); + if (BitUtil.check(status, 4)) { + position.set(Position.KEY_CHARGE, true); + } + if (BitUtil.check(status, 7)) { + position.set(Position.KEY_ARCHIVE, true); + } + position.set(Position.KEY_MOTION, BitUtil.check(status, 9)); + position.set(Position.KEY_RSSI, BitUtil.between(status, 19, 24)); position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24)); position.set(Position.KEY_STATUS, status); break; @@ -249,7 +254,6 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); position.setValid(true); - hasLocation = true; } if (BitUtil.check(beaconFlags, 6)) { position.set("description", buf.readCharSequence( @@ -263,7 +267,6 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); position.setValid(true); - hasLocation = true; break; case 0x30: buf.readUnsignedIntLE(); // timestamp @@ -277,6 +280,14 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { i += 1; } break; + case 0x37: + buf.readUnsignedIntLE(); // timestamp + long barking = buf.readUnsignedIntLE(); + if (BitUtil.check(barking, 31)) { + position.set("barkStop", true); + } + position.set("barkCount", BitUtil.to(barking, 31)); + break; case 0x40: buf.readUnsignedIntLE(); // timestamp int heartRate = buf.readUnsignedByte(); @@ -290,14 +301,14 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { buf.readerIndex(endIndex); } - if (!hasLocation) { - getLastLocation(position, null); - } positions.add(position); if (deviceSession != null) { for (Position p : positions) { p.setDeviceId(deviceSession.getDeviceId()); + if (!p.getValid() && !p.hasAttribute(Position.KEY_HDOP)) { + getLastLocation(p, null); + } } } else { return null; @@ -305,9 +316,195 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder { return positions; + } else if (type == MSG_CONFIGURATION) { + + return decodeConfiguration(channel, remoteAddress, buf); + + } else if (type == MSG_RESPONSE) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + buf.readUnsignedByte(); // length + position.set(Position.KEY_RESULT, String.valueOf(buf.readUnsignedByte())); + + return position; + } return null; } + private Position decodeConfiguration(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + while (buf.isReadable()) { + int length = buf.readUnsignedByte() - 1; + int endIndex = buf.readerIndex() + length + 1; + int key = buf.readUnsignedByte(); + + switch (key) { + case 0x01: + position.set("moduleNumber", buf.readUnsignedInt()); + break; + case 0x02: + position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedInt())); + break; + case 0x03: + position.set("imei", buf.readCharSequence(length, StandardCharsets.US_ASCII).toString()); + break; + case 0x04: + position.set(Position.KEY_ICCID, BufferUtil.readString(buf, length)); + break; + case 0x05: + position.set("bleMac", ByteBufUtil.hexDump(buf.readSlice(length))); + break; + case 0x06: + position.set("settingTime", buf.readUnsignedInt()); + break; + case 0x07: + position.set("runTimes", buf.readUnsignedInt()); + break; + case 0x0A: + position.set("interval", buf.readUnsignedMedium()); + position.set("petMode", buf.readUnsignedByte()); + break; + case 0x0D: + position.set("passwordProtect", buf.readUnsignedInt()); + break; + case 0x0E: + position.set("timeZone", (int) buf.readByte()); + break; + case 0x0F: + position.set("enableControl", buf.readUnsignedInt()); + break; + case 0x13: + position.set("deviceName", BufferUtil.readString(buf, length)); + break; + case 0x14: + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001); + break; + case 0x15: + position.set("bleLatitude", buf.readIntLE() * 0.0000001); + position.set("bleLongitude", buf.readIntLE() * 0.0000001); + position.set("bleLocation", BufferUtil.readString(buf, length - 8)); + break; + case 0x17: + position.set("gpsUrl", BufferUtil.readString(buf, length)); + break; + case 0x18: + position.set("lbsUrl", BufferUtil.readString(buf, length)); + break; + case 0x1A: + position.set("firmware", BufferUtil.readString(buf, length)); + break; + case 0x1B: + position.set("gsmModule", BufferUtil.readString(buf, length)); + break; + case 0x1D: + position.set("agpsUpdate", buf.readUnsignedByte()); + position.set("agpsLatitude", buf.readIntLE() * 0.0000001); + position.set("agpsLongitude", buf.readIntLE() * 0.0000001); + break; + case 0x30: + position.set("numberFlag", buf.readUnsignedByte()); + position.set("number", BufferUtil.readString(buf, length - 1)); + break; + case 0x31: + position.set("prefixFlag", buf.readUnsignedByte()); + position.set("prefix", BufferUtil.readString(buf, length - 1)); + break; + case 0x33: + position.set("phoneSwitches", buf.readUnsignedByte()); + break; + case 0x40: + position.set("apn", BufferUtil.readString(buf, length)); + break; + case 0x41: + position.set("apnUser", BufferUtil.readString(buf, length)); + break; + case 0x42: + position.set("apnPassword", BufferUtil.readString(buf, length)); + break; + case 0x43: + buf.readUnsignedByte(); // flag + position.set("port", buf.readUnsignedShort()); + position.set("server", BufferUtil.readString(buf, length - 3)); + break; + case 0x44: + position.set("heartbeatInterval", buf.readUnsignedInt()); + position.set("uploadInterval", buf.readUnsignedInt()); + position.set("uploadLazyInterval", buf.readUnsignedInt()); + break; + case 0x47: + position.set("deviceId", BufferUtil.readString(buf, length)); + break; + case 0x4E: + position.set("gsmBand", buf.readUnsignedByte()); + break; + case 0x50: + position.set("powerAlert", buf.readUnsignedInt()); + break; + case 0x51: + position.set("geoAlert", buf.readUnsignedInt()); + break; + case 0x53: + position.set("motionAlert", buf.readUnsignedInt()); + break; + case 0x5C: + position.set("barkLevel", buf.readUnsignedByte()); + position.set("barkInterval", buf.readUnsignedInt()); + break; + case 0x61: + position.set("msisdn", BufferUtil.readString(buf, length)); + break; + case 0x62: + position.set("wifiWhitelist", buf.readUnsignedByte()); + position.set("wifiWhitelistMac", ByteBufUtil.hexDump(buf.readSlice(6))); + break; + case 0x64: + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + position.set("networkBand", buf.readUnsignedInt()); + position.set(Position.KEY_OPERATOR, BufferUtil.readString(buf, length - 5)); + break; + case 0x65: + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + position.set("networkStatus", buf.readUnsignedByte()); + position.set("serverStatus", buf.readUnsignedByte()); + position.set("networkPlmn", ByteBufUtil.hexDump(buf.readSlice(6))); + position.set("homePlmn", ByteBufUtil.hexDump(buf.readSlice(6))); + break; + case 0x66: + position.set("imsi", BufferUtil.readString(buf, length)); + break; + case 0x75: + position.set("extraEnableControl", buf.readUnsignedInt()); + break; + default: + break; + } + + buf.readerIndex(endIndex); + } + + return position; + } + } diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java new file mode 100644 index 000000000..6e330a4dd --- /dev/null +++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolEncoder.java @@ -0,0 +1,72 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.helper.Checksum; +import org.traccar.model.Command; + +import java.nio.charset.StandardCharsets; + +public class Minifinder2ProtocolEncoder extends BaseProtocolEncoder { + + public Minifinder2ProtocolEncoder(Protocol protocol) { + super(protocol); + } + + private ByteBuf encodeContent(ByteBuf content) { + + ByteBuf buf = Unpooled.buffer(); + + buf.writeByte(0xAB); // header + buf.writeByte(0x00); // properties + buf.writeShortLE(content.readableBytes()); + buf.writeShortLE(Checksum.crc16(Checksum.CRC16_XMODEM, content.nioBuffer())); + buf.writeShortLE(1); // index + buf.writeBytes(content); + + return buf; + } + + @Override + protected Object encodeCommand(Command command) { + + if (command.getType().equals(Command.TYPE_CONFIGURATION)) { + ByteBuf content = Unpooled.buffer(); + content.writeByte(Minifinder2ProtocolDecoder.MSG_CONFIGURATION); + content.writeByte(1); // length + content.writeByte(0xF0); // type + } + + if ("Nano".equalsIgnoreCase(getDeviceModel(command.getDeviceId()))) { + ByteBuf content = Unpooled.buffer(); + if (command.getType().equals(Command.TYPE_FIRMWARE_UPDATE)) { + String url = command.getString(Command.KEY_DATA); + content.writeByte(Minifinder2ProtocolDecoder.MSG_SYSTEM_CONTROL); + content.writeByte(1 + url.length()); + content.writeByte(0x30); // type + content.writeCharSequence(url, StandardCharsets.US_ASCII); + return encodeContent(content); + } + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocol.java b/src/main/java/org/traccar/protocol/MobilogixProtocol.java index 1b06c2249..36d6b5ed2 100644 --- a/src/main/java/org/traccar/protocol/MobilogixProtocol.java +++ b/src/main/java/org/traccar/protocol/MobilogixProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MobilogixProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java index 16438e122..af853fe67 100644 --- a/src/main/java/org/traccar/protocol/MoovboxProtocol.java +++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MoovboxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MotorProtocol.java b/src/main/java/org/traccar/protocol/MotorProtocol.java index 3101c9b75..f17886577 100644 --- a/src/main/java/org/traccar/protocol/MotorProtocol.java +++ b/src/main/java/org/traccar/protocol/MotorProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MotorProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Mta6Protocol.java b/src/main/java/org/traccar/protocol/Mta6Protocol.java index 019fe4fa9..c1c6eb829 100644 --- a/src/main/java/org/traccar/protocol/Mta6Protocol.java +++ b/src/main/java/org/traccar/protocol/Mta6Protocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Mta6Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java index 896c7a2d2..9704cf099 100644 --- a/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Mta6ProtocolDecoder.java @@ -96,7 +96,7 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder { } - private static class TimeReader extends FloatReader { + private static final class TimeReader extends FloatReader { private long weekNumber; diff --git a/src/main/java/org/traccar/protocol/MtxProtocol.java b/src/main/java/org/traccar/protocol/MtxProtocol.java index e085b6221..12d324019 100644 --- a/src/main/java/org/traccar/protocol/MtxProtocol.java +++ b/src/main/java/org/traccar/protocol/MtxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MtxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/MxtProtocol.java b/src/main/java/org/traccar/protocol/MxtProtocol.java index 1190bf527..2f0cc1658 100644 --- a/src/main/java/org/traccar/protocol/MxtProtocol.java +++ b/src/main/java/org/traccar/protocol/MxtProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class MxtProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NavigilProtocol.java b/src/main/java/org/traccar/protocol/NavigilProtocol.java index 46a6c33a5..a309235c5 100644 --- a/src/main/java/org/traccar/protocol/NavigilProtocol.java +++ b/src/main/java/org/traccar/protocol/NavigilProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NavigilProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NavisProtocol.java b/src/main/java/org/traccar/protocol/NavisProtocol.java index 640a77803..96b5b0de0 100644 --- a/src/main/java/org/traccar/protocol/NavisProtocol.java +++ b/src/main/java/org/traccar/protocol/NavisProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NavisProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NavisetProtocol.java b/src/main/java/org/traccar/protocol/NavisetProtocol.java index 388f141f8..6df0b0436 100644 --- a/src/main/java/org/traccar/protocol/NavisetProtocol.java +++ b/src/main/java/org/traccar/protocol/NavisetProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NavisetProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java index 50013d1a4..de5f93df1 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NavtelecomProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java index 08b1a8d0f..cd7ffa0e1 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java @@ -193,12 +193,12 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); - for (int j = 0; j < bits.length(); j++) { - if (bits.get(j)) { + for (int j = 1; j <= bits.length(); j++) { + if (bits.get(j - 1)) { int value; - switch (j + 1) { + switch (j) { case 1: position.set(Position.KEY_INDEX, buf.readUnsignedIntLE()); break; @@ -208,6 +208,18 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 3: position.setDeviceTime(new Date(buf.readUnsignedIntLE() * 1000)); break; + case 4: + value = buf.readUnsignedByte(); + position.set( + Position.KEY_ALARM, + BitUtil.check(value, 2) ? Position.ALARM_GENERAL : null); + int guardMode = BitUtil.between(value, 3, 4); + position.set(Position.KEY_ARMED, (0 < guardMode) && (guardMode < 3)); + break; + case 5: + value = buf.readUnsignedByte(); + position.set(Position.KEY_ROAMING, BitUtil.check(value, 6) ? true : null); + break; case 8: value = buf.readUnsignedByte(); position.setValid(BitUtil.check(value, 1)); @@ -232,7 +244,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { position.setCourse(buf.readUnsignedShortLE()); break; case 15: - position.set(Position.KEY_ODOMETER, buf.readFloatLE()); + position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000); break; case 19: position.set(Position.KEY_POWER, buf.readShortLE() * 0.001); @@ -246,7 +258,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 24: case 25: case 26: - position.set(Position.PREFIX_ADC + (j + 2 - 21), buf.readUnsignedShortLE() * 0.001); + position.set(Position.PREFIX_ADC + (j + 1 - 21), buf.readUnsignedShortLE() * 0.001); break; case 29: value = buf.readUnsignedByte(); @@ -262,14 +274,14 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { break; case 33: case 34: - position.set(Position.PREFIX_COUNT + (j + 2 - 33), buf.readUnsignedIntLE()); + position.set(Position.PREFIX_COUNT + (j + 1 - 33), buf.readUnsignedIntLE()); break; case 35: case 36: - position.set("freq" + (j + 2 - 35), buf.readUnsignedShortLE()); + position.set("freq" + (j + 1 - 35), buf.readUnsignedShortLE()); break; case 37: - position.set(Position.KEY_HOURS, buf.readUnsignedIntLE()); + position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000); break; case 38: case 39: @@ -278,7 +290,8 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 42: case 43: value = buf.readUnsignedShortLE(); - position.set("fuel" + (j + 2 - 38), (value < 65500) ? value : null); + position.set( + Position.KEY_FUEL_LEVEL + (j + 1 - 38), (value < 65500) ? value : null); break; case 44: value = buf.readUnsignedShortLE(); @@ -294,8 +307,75 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 52: value = buf.readByte(); position.set( - Position.PREFIX_TEMP + (j + 2 - 45), - (value != (byte) 0x80) ? value : null); + Position.PREFIX_TEMP + (j + 1 - 45), (value != (byte) 0x80) ? value : null); + break; + case 53: + value = buf.readUnsignedShortLE(); + if (value != 0x7FFF) { + if (BitUtil.check(value, 15)) { + position.set("obdFuelLevel", BitUtil.to(value, 14)); + } else { + position.set("obdFuel", BitUtil.to(value, 14) * 0.1); + } + } + break; + case 54: + double fuelUsed = buf.readFloatLE() * 0.5; + position.set(Position.KEY_FUEL_USED, (fuelUsed >= 0) ? fuelUsed : null); + break; + case 55: + value = buf.readUnsignedShortLE(); + position.set(Position.KEY_RPM, (value != 0xFFFF) ? value : null); + break; + case 56: + value = buf.readByte(); + position.set(Position.KEY_COOLANT_TEMP, (value != (byte) 0x80) ? value : null); + break; + case 57: + position.set(Position.KEY_OBD_ODOMETER, buf.readFloatLE() * 1000); + break; + case 58: + case 59: + case 60: + case 61: + case 62: + value = buf.readUnsignedShortLE(); + position.set("axleWeight" + (j + 1 - 58), (value != 0xFFFF) ? value : null); + break; + case 63: + value = buf.readUnsignedByte(); + position.set("acceleratorPosition", (value != 0xFF) ? value : null); + break; + case 64: + value = buf.readUnsignedByte(); + position.set("brakePosition", (value != 0xFF) ? value : null); + break; + case 65: + value = buf.readUnsignedByte(); + position.set(Position.KEY_ENGINE_LOAD, (value != 0xFF) ? value : null); + break; + case 66: + value = buf.readUnsignedShortLE(); + if (value != 0x7FFF) { + if (BitUtil.check(value, 15)) { + position.set("obdAdBlueLevel", BitUtil.to(value, 14)); + } else { + position.set("obdAdBlue", BitUtil.to(value, 14) * 0.1); + } + } + break; + case 67: + position.set("obdHours", buf.readUnsignedIntLE() * 1000); + break; + case 68: + value = buf.readUnsignedShortLE(); + position.set( + Position.KEY_ODOMETER_SERVICE, + (value != 0xFFFF) ? (value * 5000) : null); + break; + case 69: + value = buf.readUnsignedByte(); + position.set(Position.KEY_OBD_SPEED, (value != 0xFF) ? value : null); break; case 78: case 79: @@ -303,10 +383,39 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 81: case 82: case 83: - position.set("fuelTemp" + (j + 2 - 78), (int) buf.readByte()); + position.set("fuelTemp" + (j + 1 - 78), (int) buf.readByte()); + break; + case 163: + case 164: + case 165: + case 166: + value = buf.readShortLE(); + position.set( + Position.PREFIX_TEMP + (j + 1 + 8 - 163), + (value != (short) 0x8000) ? value * 0.05 : null); + break; + case 167: + case 168: + case 169: + case 170: + value = buf.readUnsignedByte(); + position.set("humidity" + (j + 1 - 167), (value != 0xFF) ? value * 0.5 : null); + break; + case 206: + position.set("diagnostic", buf.readUnsignedIntLE()); break; default: - buf.skipBytes(getItemLength(j + 1)); + if ((207 <= j) && (j <= 222)) { + position.set("user1Byte" + (j + 1 - 207), buf.readUnsignedByte()); + } else if ((223 <= j) && (j <= 237)) { + position.set("user2Byte" + (j + 1 - 223), buf.readUnsignedShortLE()); + } else if ((238 <= j) && (j <= 252)) { + position.set("user4Byte" + (j + 1 - 238), buf.readUnsignedIntLE()); + } else if ((253 <= j) && (j <= 255)) { + position.set("user8Byte" + (j + 1 - 253), buf.readLongLE()); + } else { + buf.skipBytes(getItemLength(j)); + } break; } } diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java index ce0dbbef2..9493132f5 100644 --- a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java +++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NdtpV6Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NeosProtocol.java b/src/main/java/org/traccar/protocol/NeosProtocol.java index 0787b6562..16a6ba5a0 100644 --- a/src/main/java/org/traccar/protocol/NeosProtocol.java +++ b/src/main/java/org/traccar/protocol/NeosProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NeosProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java index f27e4afb8..e011660da 100644 --- a/src/main/java/org/traccar/protocol/NetProtocol.java +++ b/src/main/java/org/traccar/protocol/NetProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NetProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NiotProtocol.java b/src/main/java/org/traccar/protocol/NiotProtocol.java index 0fbe0c689..7eacd5ff3 100644 --- a/src/main/java/org/traccar/protocol/NiotProtocol.java +++ b/src/main/java/org/traccar/protocol/NiotProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NiotProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NoranProtocol.java b/src/main/java/org/traccar/protocol/NoranProtocol.java index 626991029..d03e52be5 100644 --- a/src/main/java/org/traccar/protocol/NoranProtocol.java +++ b/src/main/java/org/traccar/protocol/NoranProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NoranProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NtoProtocol.java b/src/main/java/org/traccar/protocol/NtoProtocol.java new file mode 100644 index 000000000..d3596e287 --- /dev/null +++ b/src/main/java/org/traccar/protocol/NtoProtocol.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.CharacterDelimiterFrameDecoder; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class NtoProtocol extends BaseProtocol { + + @Inject + public NtoProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '&')); + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new StringDecoder()); + pipeline.addLast(new NtoProtocolDecoder(NtoProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/NtoProtocolDecoder.java b/src/main/java/org/traccar/protocol/NtoProtocolDecoder.java new file mode 100644 index 000000000..ba9ebd95d --- /dev/null +++ b/src/main/java/org/traccar/protocol/NtoProtocolDecoder.java @@ -0,0 +1,92 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class NtoProtocolDecoder extends BaseProtocolDecoder { + + public NtoProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("^NB,") // manufacturer + .number("(d+),") // imei + .expression("(...),") // type + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time (hhmmss) + .expression("([AVM]),") // validity + .number("([NS]),(dd)(dd.d+),") // latitude + .number("([EW]),(ddd)(dd.d+),") // longitude + .number("(d+.?d*),") // speed + .number("(d+),") // course + .number("(x+),") // status + .any() + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_TYPE, parser.next()); + + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextInt()); + + long status = parser.nextHexLong(); + position.set(Position.KEY_STATUS, status); + position.set(Position.KEY_ALARM, BitUtil.check(status, 1) ? Position.ALARM_JAMMING : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 1) ? Position.ALARM_POWER_CUT : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 2) ? Position.ALARM_OVERSPEED : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 3) ? Position.ALARM_VIBRATION : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 4) ? Position.ALARM_GEOFENCE_ENTER : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 3 * 8 + 5) ? Position.ALARM_GEOFENCE_EXIT : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 4 * 8) ? Position.ALARM_LOW_BATTERY : null); + position.set(Position.KEY_ALARM, BitUtil.check(status, 4 * 8 + 4) ? Position.ALARM_DOOR : null); + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/NvsProtocol.java b/src/main/java/org/traccar/protocol/NvsProtocol.java index 7ed488e38..8a4ece30d 100644 --- a/src/main/java/org/traccar/protocol/NvsProtocol.java +++ b/src/main/java/org/traccar/protocol/NvsProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NvsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/NyitechProtocol.java b/src/main/java/org/traccar/protocol/NyitechProtocol.java index e7ef10945..225b1bd5a 100644 --- a/src/main/java/org/traccar/protocol/NyitechProtocol.java +++ b/src/main/java/org/traccar/protocol/NyitechProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class NyitechProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java index 94f450426..9fcc35d0d 100644 --- a/src/main/java/org/traccar/protocol/ObdDongleProtocol.java +++ b/src/main/java/org/traccar/protocol/ObdDongleProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ObdDongleProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OigoProtocol.java b/src/main/java/org/traccar/protocol/OigoProtocol.java index 0539bada6..3483f8270 100644 --- a/src/main/java/org/traccar/protocol/OigoProtocol.java +++ b/src/main/java/org/traccar/protocol/OigoProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OigoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OkoProtocol.java b/src/main/java/org/traccar/protocol/OkoProtocol.java index 29c8bc1b9..6ca6c0e93 100644 --- a/src/main/java/org/traccar/protocol/OkoProtocol.java +++ b/src/main/java/org/traccar/protocol/OkoProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OkoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocol.java b/src/main/java/org/traccar/protocol/OmnicommProtocol.java index dd400c779..b59b84132 100644 --- a/src/main/java/org/traccar/protocol/OmnicommProtocol.java +++ b/src/main/java/org/traccar/protocol/OmnicommProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OmnicommProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java index 5443b4ffc..24d6de706 100644 --- a/src/main/java/org/traccar/protocol/OpenGtsProtocol.java +++ b/src/main/java/org/traccar/protocol/OpenGtsProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OpenGtsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocol.java b/src/main/java/org/traccar/protocol/OrbcommProtocol.java index fb09f0abb..06b00619c 100644 --- a/src/main/java/org/traccar/protocol/OrbcommProtocol.java +++ b/src/main/java/org/traccar/protocol/OrbcommProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerClient; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OrbcommProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java index 1164d72a1..7ed13d647 100644 --- a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java @@ -24,10 +24,10 @@ import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/OrionProtocol.java b/src/main/java/org/traccar/protocol/OrionProtocol.java index 2dec7cd06..b78af462b 100644 --- a/src/main/java/org/traccar/protocol/OrionProtocol.java +++ b/src/main/java/org/traccar/protocol/OrionProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OrionProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocol.java b/src/main/java/org/traccar/protocol/OsmAndProtocol.java index a86bc70d7..e06580949 100644 --- a/src/main/java/org/traccar/protocol/OsmAndProtocol.java +++ b/src/main/java/org/traccar/protocol/OsmAndProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OsmAndProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocol.java b/src/main/java/org/traccar/protocol/OutsafeProtocol.java index 0099be456..159534883 100644 --- a/src/main/java/org/traccar/protocol/OutsafeProtocol.java +++ b/src/main/java/org/traccar/protocol/OutsafeProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OutsafeProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java index 62b873be7..f71778412 100644 --- a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java @@ -23,11 +23,11 @@ import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java index 9ad337f19..c509ad282 100644 --- a/src/main/java/org/traccar/protocol/OwnTracksProtocol.java +++ b/src/main/java/org/traccar/protocol/OwnTracksProtocol.java @@ -24,7 +24,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class OwnTracksProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java index 71ac87168..e54d07fa7 100644 --- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java @@ -25,8 +25,8 @@ import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java index 709729ef1..a315d4d9f 100644 --- a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PacificTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PathAwayProtocol.java b/src/main/java/org/traccar/protocol/PathAwayProtocol.java index 1d13eea95..a65740475 100644 --- a/src/main/java/org/traccar/protocol/PathAwayProtocol.java +++ b/src/main/java/org/traccar/protocol/PathAwayProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PathAwayProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocol.java b/src/main/java/org/traccar/protocol/PiligrimProtocol.java index aa45a0def..9dd1bc491 100644 --- a/src/main/java/org/traccar/protocol/PiligrimProtocol.java +++ b/src/main/java/org/traccar/protocol/PiligrimProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PiligrimProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PluginProtocol.java b/src/main/java/org/traccar/protocol/PluginProtocol.java index b2101b18d..fff1830e8 100644 --- a/src/main/java/org/traccar/protocol/PluginProtocol.java +++ b/src/main/java/org/traccar/protocol/PluginProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PluginProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java index 69666cc0e..0fbedfb09 100644 --- a/src/main/java/org/traccar/protocol/PolteProtocol.java +++ b/src/main/java/org/traccar/protocol/PolteProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PolteProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java index 028de5424..8954db491 100644 --- a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java @@ -23,8 +23,8 @@ import org.traccar.session.DeviceSession; import org.traccar.Protocol; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/PortmanProtocol.java b/src/main/java/org/traccar/protocol/PortmanProtocol.java index de78013fa..3a4b49289 100644 --- a/src/main/java/org/traccar/protocol/PortmanProtocol.java +++ b/src/main/java/org/traccar/protocol/PortmanProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PortmanProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java index da9403313..716f2694b 100644 --- a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,11 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder { } private static final Pattern PATTERN_STANDARD = new PatternBuilder() + .groupBegin() .text("$PTMLA,") // header + .or() + .text("%%") // header + .groupEnd() .expression("([^,]+),") // id .expression("([ABCL]),") // validity .number("(dd)(dd)(dd)") // date (yymmdd) @@ -47,12 +51,19 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // course .number("(?:NA|C(-?d+)),") // temperature .number("(x{8}),") // status - .number("(?:NA|(d+)),") // card id + .groupBegin() + .text("NA") + .or() + .number("F(d+)") // fuel + .or() + .number("(d+)") // card id + .groupEnd(",") .number("(d+),") // event .number("(d+),") // satellites .number("(d+.d+),") // odometer - .number("(d+),") // rssi - .number("(?:G(d+)|[^,]*)") // fuel + .number("(d+)") // rssi + .number(",G(d+)").optional() // fuel + .any() .compile(); private Object decodeStandard(Channel channel, SocketAddress remoteAddress, String sentence) { @@ -79,6 +90,9 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, parser.next()); position.set(Position.KEY_STATUS, parser.nextHexLong()); + if (parser.hasNext()) { + position.set(Position.KEY_FUEL_LEVEL, parser.nextInt() * 0.1); + } position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); int event = parser.nextInt(); @@ -159,7 +173,7 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder { Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; - if (sentence.startsWith("$PTMLA")) { + if (sentence.startsWith("%%") || sentence.startsWith("$PTMLA")) { return decodeStandard(channel, remoteAddress, sentence); } else if (sentence.startsWith("$EXT")) { return decodeExtended(channel, remoteAddress, sentence); diff --git a/src/main/java/org/traccar/protocol/PositrexProtocol.java b/src/main/java/org/traccar/protocol/PositrexProtocol.java new file mode 100644 index 000000000..5cf389fbe --- /dev/null +++ b/src/main/java/org/traccar/protocol/PositrexProtocol.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class PositrexProtocol extends BaseProtocol { + + @Inject + public PositrexProtocol(Config config) { + addServer(new TrackerServer(config, getName(), true) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new PositrexProtocolDecoder(PositrexProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java b/src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java new file mode 100644 index 000000000..82ae2c134 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PositrexProtocolDecoder.java @@ -0,0 +1,116 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.Date; + +public class PositrexProtocolDecoder extends BaseProtocolDecoder { + + public PositrexProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int MSG_PING = 0x2E; + + private Date readTime(ByteBuf buf) { + long time = buf.readUnsignedInt(); + DateBuilder dateBuilder = new DateBuilder(); + dateBuilder.setSecond((int) (time % 60)); + time /= 60; + dateBuilder.setMinute((int) (time % 60)); + time /= 60; + dateBuilder.setHour((int) (time % 24)); + time /= 24; + dateBuilder.setDay((int) (time % 32)); + time /= 32; + dateBuilder.setMonth((int) (time % 13)); + dateBuilder.setYear((int) (2000 + time / 13)); + return dateBuilder.getDate(); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + int first = buf.getUnsignedByte(buf.readerIndex()); + long deviceId; + if (BitUtil.check(first, 7)) { + if (BitUtil.check(first, 6)) { + deviceId = 73000000 + BitUtil.to(buf.readUnsignedInt(), 30); + } else if (!BitUtil.check(first, 5) && !BitUtil.check(first, 4)) { + deviceId = 7590000 + BitUtil.to(buf.readUnsignedMedium(), 20); + } else { + deviceId = 70000000 + BitUtil.to(buf.readUnsignedMedium(), 20); + } + } else { + deviceId = 7560000 + buf.readUnsignedShort(); + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId)); + if (deviceSession == null) { + return null; + } + + int service = buf.readUnsignedByte(); + if (service == MSG_PING) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setTime(readTime(buf)); + + int latitude = buf.readMedium(); + int longitude = buf.readMedium(); + + position.setValid(BitUtil.check(latitude, 23)); + position.setLatitude(BitUtil.to(latitude, 23) * 0.000025); + position.setLongitude(longitude * 0.000025); + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + position.setCourse(buf.readUnsignedByte() * 2); + + position.set(Position.PREFIX_IO, buf.readUnsignedByte()); + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + buf.readUnsignedInt(); // report begin + buf.readUnsignedInt(); // report end + buf.readUnsignedInt(); // number of records + + if (buf.isReadable()) { + position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); + position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01); + } + + return position; + + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/PretraceProtocol.java b/src/main/java/org/traccar/protocol/PretraceProtocol.java index b77dd97bf..54a34fc69 100644 --- a/src/main/java/org/traccar/protocol/PretraceProtocol.java +++ b/src/main/java/org/traccar/protocol/PretraceProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PretraceProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PricolProtocol.java b/src/main/java/org/traccar/protocol/PricolProtocol.java index f5e904541..7b0e7386c 100644 --- a/src/main/java/org/traccar/protocol/PricolProtocol.java +++ b/src/main/java/org/traccar/protocol/PricolProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PricolProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ProgressProtocol.java b/src/main/java/org/traccar/protocol/ProgressProtocol.java index 49eb6847f..8d159ef24 100644 --- a/src/main/java/org/traccar/protocol/ProgressProtocol.java +++ b/src/main/java/org/traccar/protocol/ProgressProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ProgressProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java index 73f978cbd..01a83b31f 100644 --- a/src/main/java/org/traccar/protocol/PstProtocol.java +++ b/src/main/java/org/traccar/protocol/PstProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class PstProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Pt215Protocol.java b/src/main/java/org/traccar/protocol/Pt215Protocol.java index b272582a4..fd67b1241 100644 --- a/src/main/java/org/traccar/protocol/Pt215Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt215Protocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Pt215Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Pt3000Protocol.java b/src/main/java/org/traccar/protocol/Pt3000Protocol.java index d72774f47..5f49084ed 100644 --- a/src/main/java/org/traccar/protocol/Pt3000Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt3000Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Pt3000Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Pt502Protocol.java b/src/main/java/org/traccar/protocol/Pt502Protocol.java index d5d30e8e8..0257dd60f 100644 --- a/src/main/java/org/traccar/protocol/Pt502Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt502Protocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Pt502Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Pt60Protocol.java b/src/main/java/org/traccar/protocol/Pt60Protocol.java index 58345f025..83e3bfbeb 100644 --- a/src/main/java/org/traccar/protocol/Pt60Protocol.java +++ b/src/main/java/org/traccar/protocol/Pt60Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Pt60Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/PuiProtocol.java b/src/main/java/org/traccar/protocol/PuiProtocol.java new file mode 100644 index 000000000..ac8291039 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PuiProtocol.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.mqtt.MqttDecoder; +import io.netty.handler.codec.mqtt.MqttEncoder; +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class PuiProtocol extends BaseProtocol { + + @Inject + public PuiProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(MqttEncoder.INSTANCE); + pipeline.addLast(new MqttDecoder()); + pipeline.addLast(new PuiProtocolDecoder(PuiProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/PuiProtocolDecoder.java b/src/main/java/org/traccar/protocol/PuiProtocolDecoder.java new file mode 100644 index 000000000..f10ff3fe7 --- /dev/null +++ b/src/main/java/org/traccar/protocol/PuiProtocolDecoder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.mqtt.MqttPublishMessage; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.apache.kafka.common.utils.ByteBufferInputStream; +import org.traccar.BaseMqttProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +public class PuiProtocolDecoder extends BaseMqttProtocolDecoder { + + public PuiProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode(DeviceSession deviceSession, MqttPublishMessage message) throws Exception { + + JsonObject json; + try (ByteBufferInputStream inputStream = new ByteBufferInputStream(message.payload().nioBuffer())) { + json = Json.createReader(inputStream).readObject(); + } + + String type = json.getString("rpt"); + switch (type) { + case "hf": + case "loc": + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + position.setTime(dateFormat.parse(json.getString("ts"))); + + JsonObject location = json.getJsonObject("location"); + position.setLatitude(location.getJsonNumber("lat").doubleValue()); + position.setLongitude(location.getJsonNumber("lon").doubleValue()); + + position.setCourse(json.getInt("bear")); + position.setSpeed(UnitsConverter.knotsFromCps(json.getInt("spd"))); + + position.set(Position.KEY_IGNITION, json.getString("ign").equals("on")); + + return position; + + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/R12wProtocol.java b/src/main/java/org/traccar/protocol/R12wProtocol.java index a406f6306..b5b3eff81 100644 --- a/src/main/java/org/traccar/protocol/R12wProtocol.java +++ b/src/main/java/org/traccar/protocol/R12wProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class R12wProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java index 63ca3476c..6f7340902 100644 --- a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java +++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RaceDynamicsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RadarProtocol.java b/src/main/java/org/traccar/protocol/RadarProtocol.java index 9d88c6d72..8985e0e83 100644 --- a/src/main/java/org/traccar/protocol/RadarProtocol.java +++ b/src/main/java/org/traccar/protocol/RadarProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RadarProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RamacProtocol.java b/src/main/java/org/traccar/protocol/RamacProtocol.java new file mode 100644 index 000000000..42ce16fe8 --- /dev/null +++ b/src/main/java/org/traccar/protocol/RamacProtocol.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class RamacProtocol extends BaseProtocol { + + @Inject + public RamacProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new RamacProtocolDecoder(RamacProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/RamacProtocolDecoder.java b/src/main/java/org/traccar/protocol/RamacProtocolDecoder.java new file mode 100644 index 000000000..ffdc68474 --- /dev/null +++ b/src/main/java/org/traccar/protocol/RamacProtocolDecoder.java @@ -0,0 +1,112 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +public class RamacProtocolDecoder extends BaseHttpProtocolDecoder { + + private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public RamacProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + String content = request.content().toString(StandardCharsets.UTF_8); + JsonObject json = Json.createReader(new StringReader(content)).readObject(); + + String deviceId = json.getString("DeviceId"); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_TYPE, json.getInt("PacketType")); + position.set(Position.KEY_INDEX, json.getInt("SeqNumber")); + position.setDeviceTime(dateFormat.parse(json.getString("UpdateDate"))); + + int alert = json.getInt("Alert"); + if (alert > 0) { + position.set("alert", alert); + String alertMessage = json.getString("AlertMessage"); + if (!alertMessage.isEmpty()) { + position.set("alertMessage", alertMessage); + } + } + + if (json.containsKey("GpsEvent")) { + position.set("gpsEvent", json.getInt("GpsEvent")); + if (json.containsKey("GpsEventText")) { + position.set("gpsEventText", json.getString("GpsEventText")); + } + } + + if (json.containsKey("Event")) { + position.set(Position.KEY_EVENT, json.getInt("Event")); + } + if (json.containsKey("BatteryPercentage")) { + position.set(Position.KEY_BATTERY_LEVEL, json.getInt("BatteryPercentage")); + } + if (json.containsKey("Battery")) { + position.set(Position.KEY_BATTERY, json.getJsonNumber("Battery").doubleValue()); + } + + position.set("deviceType", json.getString("DeviceTypeText")); + + if (json.containsKey("Latitude") && json.containsKey("Longitude")) { + position.setValid(true); + if (json.containsKey("LocationDateTime")) { + position.setFixTime(dateFormat.parse(json.getString("LocationDateTime"))); + } else { + position.setFixTime(position.getDeviceTime()); + } + position.setLatitude(json.getJsonNumber("Latitude").doubleValue()); + position.setLongitude(json.getJsonNumber("Longitude").doubleValue()); + } else { + getLastLocation(position, position.getDeviceTime()); + } + + sendResponse( + channel, HttpResponseStatus.OK, + Unpooled.copiedBuffer("{\"CaseID\":1,\"EventID\":1}", StandardCharsets.UTF_8)); + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/RaveonProtocol.java b/src/main/java/org/traccar/protocol/RaveonProtocol.java index db70396ee..aa1a79219 100644 --- a/src/main/java/org/traccar/protocol/RaveonProtocol.java +++ b/src/main/java/org/traccar/protocol/RaveonProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RaveonProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RecodaProtocol.java b/src/main/java/org/traccar/protocol/RecodaProtocol.java index 0d50db01e..7d2fadae4 100644 --- a/src/main/java/org/traccar/protocol/RecodaProtocol.java +++ b/src/main/java/org/traccar/protocol/RecodaProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RecodaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java index 1d4b419bb..a349a8191 100644 --- a/src/main/java/org/traccar/protocol/RetranslatorProtocol.java +++ b/src/main/java/org/traccar/protocol/RetranslatorProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RetranslatorProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocol.java b/src/main/java/org/traccar/protocol/RfTrackProtocol.java index d3b41e93e..ac033c348 100644 --- a/src/main/java/org/traccar/protocol/RfTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/RfTrackProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RfTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java index 28a3ac29c..cbb204e3b 100644 --- a/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RfTrackProtocolDecoder.java @@ -29,9 +29,9 @@ import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; import org.traccar.session.DeviceSession; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/org/traccar/protocol/RitiProtocol.java b/src/main/java/org/traccar/protocol/RitiProtocol.java index 9b9c00cb2..9916042a8 100644 --- a/src/main/java/org/traccar/protocol/RitiProtocol.java +++ b/src/main/java/org/traccar/protocol/RitiProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RitiProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java index ab2bc5842..229c343bb 100644 --- a/src/main/java/org/traccar/protocol/RoboTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/RoboTrackProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RoboTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RstProtocol.java b/src/main/java/org/traccar/protocol/RstProtocol.java index 109d91b16..0bb809a49 100644 --- a/src/main/java/org/traccar/protocol/RstProtocol.java +++ b/src/main/java/org/traccar/protocol/RstProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RstProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java index fcc96fbf1..eafa4d3d7 100644 --- a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,8 +69,10 @@ public class RstProtocolDecoder extends BaseProtocolDecoder { .number("x{4};") // sensors .number("(xx);") // status 1 .number("(xx);") // status 2 + .expression("(.*)") // additional data .groupEnd("?") .any() + .text("FIM;") .compile(); @Override @@ -115,9 +117,16 @@ public class RstProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_SATELLITES, parser.nextInt()); position.set(Position.KEY_HDOP, parser.nextInt()); - position.set(Position.PREFIX_IN + 1, parser.nextHexInt()); - position.set(Position.PREFIX_IN + 2, parser.nextHexInt()); - position.set(Position.PREFIX_IN + 3, parser.nextHexInt()); + + int inputs1 = parser.nextHexInt(); + int inputs2 = parser.nextHexInt(); + int inputs3 = parser.nextHexInt(); + position.set(Position.PREFIX_IN + 1, inputs1); + position.set(Position.PREFIX_IN + 2, inputs2); + position.set(Position.PREFIX_IN + 3, inputs3); + + position.set(Position.KEY_IGNITION, BitUtil.check(inputs2, 7)); + position.set(Position.PREFIX_OUT + 1, parser.nextHexInt()); position.set(Position.PREFIX_OUT + 2, parser.nextHexInt()); position.set(Position.KEY_POWER, parser.nextDouble()); @@ -125,10 +134,23 @@ public class RstProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ODOMETER, parser.nextInt()); position.set(Position.KEY_RSSI, parser.nextInt()); position.set(Position.PREFIX_TEMP + 1, (int) parser.nextHexInt().byteValue()); + position.set(Position.KEY_STATUS, (parser.nextHexInt() << 8) + parser.nextHexInt()); + + String[] values = parser.next().split(";"); + if (type == 55) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, values[0]); + } + + return position; + + } else if (type == 134) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); - int status = (parser.nextHexInt() << 8) + parser.nextHexInt(); - position.set(Position.KEY_IGNITION, BitUtil.check(status, 7)); - position.set(Position.KEY_STATUS, status); + position.set(Position.KEY_RESULT, String.valueOf(type)); return position; diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java index 99a9686f6..9f399e299 100644 --- a/src/main/java/org/traccar/protocol/RuptelaProtocol.java +++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class RuptelaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java index 77df0deb7..e1efb5757 100644 --- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.traccar.BaseProtocolDecoder; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; +import org.traccar.helper.BitUtil; import org.traccar.helper.DataConverter; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -50,6 +51,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_SMS_VIA_GPRS = 8; public static final int MSG_DTCS = 9; public static final int MSG_IDENTIFICATION = 15; + public static final int MSG_HEARTBEAT = 16; public static final int MSG_SET_IO = 17; public static final int MSG_FILES = 37; public static final int MSG_EXTENDED_RECORDS = 68; @@ -92,22 +94,55 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { } } + private void decodeDriver(Position position, String part1, String part2) { + Long driverIdPart1 = (Long) position.getAttributes().remove(part1); + Long driverIdPart2 = (Long) position.getAttributes().remove(part2); + if (driverIdPart1 != null && driverIdPart2 != null) { + ByteBuf driverId = Unpooled.copyLong(driverIdPart1, driverIdPart2); + position.set(Position.KEY_DRIVER_UNIQUE_ID, driverId.toString(StandardCharsets.US_ASCII)); + driverId.release(); + } + } + private void decodeParameter(Position position, int id, ByteBuf buf, int length) { switch (id) { case 2: case 3: case 4: - position.set("di" + (id - 1), readValue(buf, length, false)); - break; case 5: - position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1); + position.set(Position.PREFIX_IN + (id - 1), readValue(buf, length, false)); + break; + case 13: + case 173: + position.set(Position.KEY_MOTION, readValue(buf, length, false) > 0); + break; + case 20: + position.set(Position.PREFIX_ADC + 3, readValue(buf, length, false)); + break; + case 21: + position.set(Position.PREFIX_ADC + 4, readValue(buf, length, false)); + break; + case 22: + position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false)); + break; + case 23: + position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false)); break; case 29: - position.set(Position.KEY_POWER, readValue(buf, length, false)); + position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001); break; case 30: position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001); break; + case 32: + position.set(Position.KEY_DEVICE_TEMP, readValue(buf, length, true)); + break; + case 39: + position.set(Position.KEY_ENGINE_LOAD, readValue(buf, length, false)); + break; + case 65: + position.set(Position.KEY_ODOMETER, readValue(buf, length, false)); + break; case 74: position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1); break; @@ -116,6 +151,23 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { case 80: position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1); break; + case 88: + if (readValue(buf, length, false) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_JAMMING); + } + break; + case 94: + position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.25); + break; + case 95: + position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false)); + break; + case 98: + position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 100 / 255.0); + break; + case 100: + position.set(Position.KEY_FUEL_CONSUMPTION, readValue(buf, length, false) / 20.0); + break; case 134: if (readValue(buf, length, false) > 0) { position.set(Position.KEY_ALARM, Position.ALARM_BRAKING); @@ -126,9 +178,61 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION); } break; + case 150: + position.set(Position.KEY_OPERATOR, readValue(buf, length, false)); + break; + case 163: + position.set(Position.KEY_ODOMETER, readValue(buf, length, false) * 5); + break; + case 164: + position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false) * 5); + break; + case 165: + position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false) / 256.0); + break; + case 166: case 197: position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.125); break; + case 170: + position.set(Position.KEY_CHARGE, readValue(buf, length, false) > 0); + break; + case 205: + position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false)); + break; + case 207: + position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 0.4); + break; + case 208: + position.set(Position.KEY_FUEL_USED, readValue(buf, length, false) * 0.5); + break; + case 251: + case 409: + position.set(Position.KEY_IGNITION, readValue(buf, length, false) > 0); + break; + case 410: + if (readValue(buf, length, false) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_TOW); + } + break; + case 411: + if (readValue(buf, length, false) > 0) { + position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT); + } + break; + case 415: + if (readValue(buf, length, false) == 0) { + position.set(Position.KEY_ALARM, Position.ALARM_GPS_ANTENNA_CUT); + } + break; + case 645: + position.set(Position.KEY_OBD_ODOMETER, readValue(buf, length, false) * 1000); + break; + case 758: + if (readValue(buf, length, false) == 1) { + position.set(Position.KEY_ALARM, Position.ALARM_TAMPERING); + } + break; default: position.set(Position.PREFIX_IO + id, readValue(buf, length, false)); break; @@ -166,22 +270,36 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // timestamp extension if (type == MSG_EXTENDED_RECORDS) { - buf.readUnsignedByte(); // record extension + int recordExtension = buf.readUnsignedByte(); + int mergeRecordCount = BitUtil.from(recordExtension, 4); + int currentRecord = BitUtil.to(recordExtension, 4); + + if (currentRecord > 0 && currentRecord <= mergeRecordCount) { + if (positions.size() == 0) { + getLastLocation(position, null); + } else { + position = positions.remove(positions.size() - 1); + } + } } buf.readUnsignedByte(); // priority (reserved) - position.setValid(true); - position.setLongitude(buf.readInt() / 10000000.0); - position.setLatitude(buf.readInt() / 10000000.0); - position.setAltitude(buf.readUnsignedShort() / 10.0); - position.setCourse(buf.readUnsignedShort() / 100.0); - - position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); - - position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); - - position.set(Position.KEY_HDOP, buf.readUnsignedByte() / 10.0); + int longitude = buf.readInt(); + int latitude = buf.readInt(); + if (longitude > Integer.MIN_VALUE && latitude > Integer.MIN_VALUE) { + position.setValid(true); + position.setLongitude(longitude / 10000000.0); + position.setLatitude(latitude / 10000000.0); + position.setAltitude(buf.readUnsignedShort() / 10.0); + position.setCourse(buf.readUnsignedShort() / 100.0); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); + position.set(Position.KEY_HDOP, buf.readUnsignedByte() / 10.0); + } else { + buf.skipBytes(8); + getLastLocation(position, null); + } if (type == MSG_EXTENDED_RECORDS) { position.set(Position.KEY_EVENT, buf.readUnsignedShort()); @@ -217,12 +335,13 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { decodeParameter(position, id, buf, 8); } - Long driverIdPart1 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 126); - Long driverIdPart2 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 127); - if (driverIdPart1 != null && driverIdPart2 != null) { - ByteBuf driverId = Unpooled.copyLong(driverIdPart1, driverIdPart2); - position.set(Position.KEY_DRIVER_UNIQUE_ID, driverId.toString(StandardCharsets.US_ASCII)); - driverId.release(); + decodeDriver(position, Position.PREFIX_IO + 126, Position.PREFIX_IO + 127); // can driver + decodeDriver(position, Position.PREFIX_IO + 155, Position.PREFIX_IO + 156); // tco driver + + Long tagIdPart1 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 760); + Long tagIdPart2 = (Long) position.getAttributes().remove(Position.PREFIX_IO + 761); + if (tagIdPart1 != null && tagIdPart2 != null) { + position.set("tagId", Long.toHexString(tagIdPart1) + Long.toHexString(tagIdPart2)); } positions.add(position); @@ -306,7 +425,7 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { return null; - } else if (type == MSG_IDENTIFICATION) { + } else if (type == MSG_IDENTIFICATION || type == MSG_HEARTBEAT) { ByteBuf content = Unpooled.buffer(); content.writeByte(1); diff --git a/src/main/java/org/traccar/protocol/S168Protocol.java b/src/main/java/org/traccar/protocol/S168Protocol.java index f904ed9ff..5fb0c6e72 100644 --- a/src/main/java/org/traccar/protocol/S168Protocol.java +++ b/src/main/java/org/traccar/protocol/S168Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class S168Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SabertekProtocol.java b/src/main/java/org/traccar/protocol/SabertekProtocol.java index 403243cdc..cb3f2ab32 100644 --- a/src/main/java/org/traccar/protocol/SabertekProtocol.java +++ b/src/main/java/org/traccar/protocol/SabertekProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SabertekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SanavProtocol.java b/src/main/java/org/traccar/protocol/SanavProtocol.java index 1a0e7b0e9..ac1941725 100644 --- a/src/main/java/org/traccar/protocol/SanavProtocol.java +++ b/src/main/java/org/traccar/protocol/SanavProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SanavProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SanulProtocol.java b/src/main/java/org/traccar/protocol/SanulProtocol.java index ea44bf868..cba162296 100644 --- a/src/main/java/org/traccar/protocol/SanulProtocol.java +++ b/src/main/java/org/traccar/protocol/SanulProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SanulProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SatsolProtocol.java b/src/main/java/org/traccar/protocol/SatsolProtocol.java index d90033e38..7252f99f0 100644 --- a/src/main/java/org/traccar/protocol/SatsolProtocol.java +++ b/src/main/java/org/traccar/protocol/SatsolProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SatsolProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocol.java b/src/main/java/org/traccar/protocol/SigfoxProtocol.java index 9a268af62..edd624727 100644 --- a/src/main/java/org/traccar/protocol/SigfoxProtocol.java +++ b/src/main/java/org/traccar/protocol/SigfoxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SigfoxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java index 4ed2bb51d..e0dfab9b2 100644 --- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,11 +32,11 @@ import org.traccar.model.Network; import org.traccar.model.Position; import org.traccar.model.WifiAccessPoint; -import javax.json.Json; -import javax.json.JsonNumber; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonNumber; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.net.URLDecoder; @@ -105,7 +105,7 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder { FullHttpRequest request = (FullHttpRequest) msg; String content = request.content().toString(StandardCharsets.UTF_8); if (!content.startsWith("{")) { - content = URLDecoder.decode(content.split("=")[0], "UTF-8"); + content = URLDecoder.decode(content.split("=")[0], StandardCharsets.UTF_8); } JsonObject json = Json.createReader(new StringReader(content)).readObject(); @@ -156,18 +156,30 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder { ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(json.getString("data"))); try { - int event = buf.readUnsignedByte(); - if (event == 0x0f || event == 0x1f) { + int header = buf.readUnsignedByte(); + if ("Amber".equals(getDeviceModel(deviceSession))) { + + int flags = buf.readUnsignedByte(); + position.set(Position.KEY_MOTION, BitUtil.check(flags, 1)); + + position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.02); + position.set(Position.PREFIX_TEMP + 1, (int) buf.readByte()); + + position.setValid(true); + position.setLatitude(buf.readInt() / 60000.0); + position.setLongitude(buf.readInt() / 60000.0); + + } else if (header == 0x0f || header == 0x1f) { - position.setValid(event >> 4 > 0); + position.setValid(header >> 4 > 0); position.setLatitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001); position.setLongitude(BufferUtil.readSignedMagnitudeInt(buf) * 0.000001); position.set(Position.KEY_BATTERY, (int) buf.readUnsignedByte()); - } else if (event >> 4 <= 3 && buf.writerIndex() == 12) { + } else if (header >> 4 <= 3 && buf.writerIndex() == 12) { - if (BitUtil.to(event, 4) == 0) { + if (BitUtil.to(header, 4) == 0) { position.setValid(true); position.setLatitude(buf.readIntLE() * 0.0000001); position.setLongitude(buf.readIntLE() * 0.0000001); diff --git a/src/main/java/org/traccar/protocol/SiwiProtocol.java b/src/main/java/org/traccar/protocol/SiwiProtocol.java index f12958a50..59b96bf72 100644 --- a/src/main/java/org/traccar/protocol/SiwiProtocol.java +++ b/src/main/java/org/traccar/protocol/SiwiProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SiwiProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java index 7ae26f634..615ef536d 100644 --- a/src/main/java/org/traccar/protocol/SkypatrolProtocol.java +++ b/src/main/java/org/traccar/protocol/SkypatrolProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SkypatrolProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java index cb7efb7ee..e4838581a 100644 --- a/src/main/java/org/traccar/protocol/SmartSoleProtocol.java +++ b/src/main/java/org/traccar/protocol/SmartSoleProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SmartSoleProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SmokeyProtocol.java b/src/main/java/org/traccar/protocol/SmokeyProtocol.java index 22b343537..0aa2bcfa7 100644 --- a/src/main/java/org/traccar/protocol/SmokeyProtocol.java +++ b/src/main/java/org/traccar/protocol/SmokeyProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SmokeyProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java index 0676aa629..e00f27b9b 100644 --- a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java +++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SolarPoweredProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SpotProtocol.java b/src/main/java/org/traccar/protocol/SpotProtocol.java index 6bd802fed..4fc57f177 100644 --- a/src/main/java/org/traccar/protocol/SpotProtocol.java +++ b/src/main/java/org/traccar/protocol/SpotProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SpotProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocol.java b/src/main/java/org/traccar/protocol/StarLinkProtocol.java index d578fa705..6dcd40fbf 100644 --- a/src/main/java/org/traccar/protocol/StarLinkProtocol.java +++ b/src/main/java/org/traccar/protocol/StarLinkProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class StarLinkProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java index aa23bfac5..193005e28 100644 --- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java @@ -61,10 +61,10 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder { @Override protected void init() { setFormat(getConfig().getString( - getProtocolName() + ".format", "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#," + Keys.PROTOCOL_FORMAT.withPrefix(getProtocolName()), "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#," + "#IN1#,#IN2#,#IN3#,#IN4#,#OUT1#,#OUT2#,#OUT3#,#OUT4#,#LAC#,#CID#,#VIN#,#VBAT#,#DEST#,#IGN#,#ENG#")); - setDateFormat(getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss")); + setDateFormat(getConfig().getString(Keys.PROTOCOL_DATE_FORMAT.withPrefix(getProtocolName()), "yyMMddHHmmss")); } public String[] getFormat(long deviceId) { diff --git a/src/main/java/org/traccar/protocol/StarcomProtocol.java b/src/main/java/org/traccar/protocol/StarcomProtocol.java index 33c3a4776..458220e59 100644 --- a/src/main/java/org/traccar/protocol/StarcomProtocol.java +++ b/src/main/java/org/traccar/protocol/StarcomProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class StarcomProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java index 56ab733c8..325847b16 100644 --- a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java @@ -75,7 +75,7 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder { case "eventid": position.set(Position.KEY_EVENT, Integer.parseInt(value)); break; - case "mileage": + case "odometer": position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(value) * 1000)); break; case "satellites": @@ -110,8 +110,8 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder { case "extra1": case "extra2": case "extra3": - position.set(key, value); default: + position.set(key, value); break; } } diff --git a/src/main/java/org/traccar/protocol/StartekFrameDecoder.java b/src/main/java/org/traccar/protocol/StartekFrameDecoder.java new file mode 100644 index 000000000..608b128cd --- /dev/null +++ b/src/main/java/org/traccar/protocol/StartekFrameDecoder.java @@ -0,0 +1,49 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.traccar.BaseFrameDecoder; + +import java.nio.charset.StandardCharsets; + +public class StartekFrameDecoder extends BaseFrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { + + if (buf.readableBytes() < 10) { + return null; + } + + int lengthIndex = buf.readerIndex() + 3; + int dividerIndex = buf.indexOf(lengthIndex, buf.writerIndex(), (byte) ','); + if (dividerIndex > 0) { + int lengthOffset = dividerIndex - buf.readerIndex() + 4; + int length = lengthOffset + Integer.parseInt(buf.getCharSequence( + lengthIndex, dividerIndex - lengthIndex, StandardCharsets.US_ASCII).toString()); + if (buf.readableBytes() >= length) { + return buf.readRetainedSlice(length); + } + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/StartekProtocol.java b/src/main/java/org/traccar/protocol/StartekProtocol.java index d010df858..9c01d24e2 100644 --- a/src/main/java/org/traccar/protocol/StartekProtocol.java +++ b/src/main/java/org/traccar/protocol/StartekProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package org.traccar.protocol; -import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; @@ -24,7 +23,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class StartekProtocol extends BaseProtocol { @@ -38,7 +37,7 @@ public class StartekProtocol extends BaseProtocol { addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { - pipeline.addLast(new LineBasedFrameDecoder(1024)); + pipeline.addLast(new StartekFrameDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new StartekProtocolEncoder(StartekProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java index 8e3624cb5..0eeb5b2aa 100644 --- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,12 +41,13 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { .expression(".") // index .number("d+,") // length .number("(d+),") // imei + .number("(xxx),") // type .expression("(.+)") // content .number("xx") // checksum + .text("\r\n") .compile(); private static final Pattern PATTERN_POSITION = new PatternBuilder() - .number("xxx,") // command .number("(d+),") // event .expression("([^,]+)?,") // event data .number("(dd)(dd)(dd)") // date (yyymmdd) @@ -72,12 +73,10 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { .number("(x+)") // battery .expression("([^,]+)?") // adc .groupBegin() - .text(",") - .number("d,") // extended - .expression("([^,]+)?") // fuel + .number(",d+") // extended + .expression(",([^,]+)?") // fuel .groupBegin() - .text(",") - .expression("([^,]+)?") // temperature + .expression(",([^,]+)?") // temperature .groupBegin() .text(",") .groupBegin() @@ -91,16 +90,26 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { .number("(d+)?|") // instant fuel .number("(d+)[%L]").optional() // fuel level .groupEnd("?") + .number(",(d+)").optional() // hours .groupEnd("?") .groupEnd("?") .groupEnd("?") + .any() .compile(); private String decodeAlarm(int value) { switch (value) { + case 1: + return Position.ALARM_SOS; case 5: case 6: return Position.ALARM_DOOR; + case 17: + return Position.ALARM_LOW_POWER; + case 18: + return Position.ALARM_POWER_CUT; + case 19: + return Position.ALARM_POWER_RESTORED; case 39: return Position.ALARM_ACCELERATION; case 40: @@ -126,26 +135,24 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { return null; } + String type = parser.next(); String content = parser.next(); - if (content.length() < 100) { - - Position position = new Position(getProtocolName()); - position.setDeviceId(deviceSession.getDeviceId()); - - getLastLocation(position, null); - - position.set(Position.KEY_RESULT, content); - - return position; - - } else { - - return decodePosition(deviceSession, content); - + switch (type) { + case "000": + return decodePosition(deviceSession, content); + case "710": + return decodeSerial(deviceSession, content); + default: + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + getLastLocation(position, null); + position.set(Position.KEY_TYPE, type); + position.set(Position.KEY_RESULT, content); + return position; } } - protected Object decodePosition(DeviceSession deviceSession, String content) throws Exception { + private Object decodePosition(DeviceSession deviceSession, String content) { Parser parser = new Parser(PATTERN_POSITION, content); if (!parser.matches()) { @@ -176,7 +183,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { position.setCourse(parser.nextInt()); position.setAltitude(parser.nextInt()); - position.set(Position.KEY_ODOMETER, parser.nextInt()); + position.set(Position.KEY_ODOMETER, parser.nextLong()); position.setNetwork(new Network(CellTower.from( parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), parser.nextInt()))); @@ -186,6 +193,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { int input = parser.nextHexInt(); int output = parser.nextHexInt(); position.set(Position.KEY_IGNITION, BitUtil.check(input, 1)); + position.set(Position.KEY_DOOR, BitUtil.check(input, 2)); position.set(Position.KEY_INPUT, input); position.set(Position.KEY_OUTPUT, output); @@ -221,7 +229,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { } } - if (parser.hasNext(6)) { + if (parser.hasNextAny(9)) { position.set(Position.KEY_RPM, parser.nextInt()); position.set(Position.KEY_ENGINE_LOAD, parser.nextInt()); position.set("airFlow", parser.nextInt()); @@ -239,6 +247,87 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); } + if (parser.hasNext()) { + position.set(Position.KEY_HOURS, parser.nextInt() * 1000L); + } + + return position; + } + + private Object decodeSerial(DeviceSession deviceSession, String content) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + String[] frames = content.split("\r\n"); + + for (String frame : frames) { + String[] values = frame.split(","); + int index = 0; + String type = values[index++]; + switch (type) { + case "T1": + index += 1; // speed + position.set(Position.KEY_RPM, Double.parseDouble(values[index++])); + index += 1; // fuel consumption + position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(values[index++])); + index += 4; // axel weights + index += 1; // turbo pressure + position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index++])); + index += 1; // accelerator pedal + position.set("torque", Integer.parseInt(values[index++])); + index += 1; // firmware version + position.set(Position.KEY_POWER, Double.parseDouble(values[index++])); + index += 1; // coolant level + position.set("oilTemp", Double.parseDouble(values[index++])); + index += 1; // oil level + position.set(Position.KEY_THROTTLE, Double.parseDouble(values[index++])); + index += 1; // air inlet pressure + index += 1; // fuel tank secondary + index += 1; // current gear + index += 1; // seatbelt + position.set("oilPressure", Integer.parseInt(values[index++])); + index += 1; // wet tank air pressure + index += 1; // pto state + int ignition = Integer.parseInt(values[index++]); + if (ignition < 2) { + position.set(Position.KEY_IGNITION, ignition > 0); + } + index += 1; // brake pedal + position.set("catalystLevel", Double.parseDouble(values[index++])); + index += 1; // fuel type + break; + case "T2": + position.set(Position.KEY_ODOMETER, Double.parseDouble(values[index++]) * 1000); + index += 1; // total fuel + index += 1; // fuel used cruise + index += 1; // fuel used drive + index += 1; + index += 1; + index += 1; // total idle time + index += 1; // total time pto + index += 1; // time cruise + index += 1; + index += 1; + index += 1; + index += 1; + index += 1; + index += 1; // brake apps + index += 1; // clutch apps + position.set(Position.KEY_HOURS, Integer.parseInt(values[index++])); + index += 1; // time torque + position.set(Position.KEY_FUEL_CONSUMPTION, Double.parseDouble(values[index++])); + index += 1; // total cruise control distance + position.set(Position.KEY_FUEL_USED, Double.parseDouble(values[index++])); + index += 1; // total drive time + break; + default: + break; + } + } + return position; } diff --git a/src/main/java/org/traccar/protocol/StbProtocol.java b/src/main/java/org/traccar/protocol/StbProtocol.java index af4e0d2c4..0beaed39c 100644 --- a/src/main/java/org/traccar/protocol/StbProtocol.java +++ b/src/main/java/org/traccar/protocol/StbProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class StbProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java index 641359bfd..c52ab485f 100644 --- a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java @@ -22,9 +22,9 @@ import org.traccar.Protocol; import org.traccar.model.Position; import org.traccar.session.DeviceSession; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonValue; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; import java.io.StringReader; import java.net.SocketAddress; import java.util.Date; diff --git a/src/main/java/org/traccar/protocol/Stl060Protocol.java b/src/main/java/org/traccar/protocol/Stl060Protocol.java index 83b5db3bb..ac23ab3ee 100644 --- a/src/main/java/org/traccar/protocol/Stl060Protocol.java +++ b/src/main/java/org/traccar/protocol/Stl060Protocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Stl060Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java index 4253b761b..0cc5fc75c 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocol.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SuntechProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java index 047a1822a..53c4a5d02 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java @@ -271,18 +271,26 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { index += 1; // collaborative network } - DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - position.setTime(dateFormat.parse(values[index++] + values[index++])); + if (values[index].isEmpty()) { - position.setLatitude(Double.parseDouble(values[index++])); - position.setLongitude(Double.parseDouble(values[index++])); - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++]))); - position.setCourse(Double.parseDouble(values[index++])); + getLastLocation(position, null); - position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + } else { - position.setValid(values[index++].equals("1")); + DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + position.setTime(dateFormat.parse(values[index++] + values[index++])); + + position.setLatitude(Double.parseDouble(values[index++])); + position.setLongitude(Double.parseDouble(values[index++])); + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++]))); + position.setCourse(Double.parseDouble(values[index++])); + + position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++])); + + position.setValid(values[index++].equals("1")); + + } return position; } @@ -446,9 +454,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { if (values.length - index >= 2) { String driverUniqueId = values[index++]; - if (values[index++].equals("1") && !driverUniqueId.isEmpty()) { + if (!driverUniqueId.isEmpty()) { position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId); } + index += 1; // registered } if (isIncludeTemp(deviceSession.getDeviceId())) { diff --git a/src/main/java/org/traccar/protocol/SupermateProtocol.java b/src/main/java/org/traccar/protocol/SupermateProtocol.java index 4290b7126..064f12b4b 100644 --- a/src/main/java/org/traccar/protocol/SupermateProtocol.java +++ b/src/main/java/org/traccar/protocol/SupermateProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SupermateProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SviasProtocol.java b/src/main/java/org/traccar/protocol/SviasProtocol.java index 7c6624f7c..a903d503c 100644 --- a/src/main/java/org/traccar/protocol/SviasProtocol.java +++ b/src/main/java/org/traccar/protocol/SviasProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SviasProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/SwiftechProtocol.java b/src/main/java/org/traccar/protocol/SwiftechProtocol.java index 68cf40d84..d5fa5c5d3 100644 --- a/src/main/java/org/traccar/protocol/SwiftechProtocol.java +++ b/src/main/java/org/traccar/protocol/SwiftechProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class SwiftechProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/T55Protocol.java b/src/main/java/org/traccar/protocol/T55Protocol.java index cedac275f..e76959fea 100644 --- a/src/main/java/org/traccar/protocol/T55Protocol.java +++ b/src/main/java/org/traccar/protocol/T55Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class T55Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java index 3be161fb8..9e7518ce5 100644 --- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { } private static final Pattern PATTERN_GPRMC = new PatternBuilder() - .text("$GPRMC,") + .text("$") + .expression("G[PLN]RMC,") .number("(dd)(dd)(dd).?d*,") // time (hhmmss) .expression("([AV]),") // validity .number("(dd)(dd.d+),") // latitude @@ -64,7 +65,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .compile(); private static final Pattern PATTERN_GPGGA = new PatternBuilder() - .text("$GPGGA,") + .text("$") + .expression("G[PLN]GGA,") .number("(dd)(dd)(dd).?d*,") // time (hhmmss) .number("(d+)(dd.d+),") // latitude .expression("([NS]),") @@ -150,6 +152,18 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .number("xx") // checksum .compile(); + private static final Pattern PATTERN_GPTXT = new PatternBuilder() + .text("$GPTXT,") + .text("NET,") + .number("(d+),") // device id + .expression("([^,]+),") // network operator + .number("(-d+),") // rssi + .number("(d+) ") // mcc + .number("(d+)") // mnc + .text("*") + .number("xx") // checksum + .compile(); + private Position position = null; private Position decodeGprmc( @@ -328,6 +342,31 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeGptxt(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_GPTXT, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + position.set(Position.KEY_OPERATOR, parser.next()); + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set("mcc", parser.nextInt()); + position.set("mnc", parser.nextInt()); + + return position; + } + private Position decodePubx(Channel channel, SocketAddress remoteAddress, String sentence) { Parser parser = new Parser(PATTERN_PUBX, sentence); @@ -407,9 +446,9 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { } } else if (sentence.matches("^[0-9A-F]+$")) { getDeviceSession(channel, remoteAddress, sentence); - } else if (sentence.startsWith("$GPRMC")) { + } else if (sentence.startsWith("RMC", 3)) { return decodeGprmc(deviceSession, sentence, remoteAddress, channel); - } else if (sentence.startsWith("$GPGGA") && deviceSession != null) { + } else if (sentence.startsWith("GGA", 3) && deviceSession != null) { return decodeGpgga(deviceSession, sentence); } else if (sentence.startsWith("$GPRMA") && deviceSession != null) { return decodeGprma(deviceSession, sentence); @@ -421,6 +460,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { return decodeQze(channel, remoteAddress, sentence); } else if (sentence.startsWith("$PUBX")) { return decodePubx(channel, remoteAddress, sentence); + } else if (sentence.startsWith("$GPTXT")) { + return decodeGptxt(channel, remoteAddress, sentence); } return null; diff --git a/src/main/java/org/traccar/protocol/T57Protocol.java b/src/main/java/org/traccar/protocol/T57Protocol.java index 4bafe8c6d..e6ef4ccc9 100644 --- a/src/main/java/org/traccar/protocol/T57Protocol.java +++ b/src/main/java/org/traccar/protocol/T57Protocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class T57Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/T622IridiumProtocol.java b/src/main/java/org/traccar/protocol/T622IridiumProtocol.java new file mode 100644 index 000000000..22efa38a8 --- /dev/null +++ b/src/main/java/org/traccar/protocol/T622IridiumProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class T622IridiumProtocol extends BaseProtocol { + + @Inject + public T622IridiumProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2)); + pipeline.addLast(new T622IridiumProtocolDecoder(T622IridiumProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java b/src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java new file mode 100644 index 000000000..9e64ec9be --- /dev/null +++ b/src/main/java/org/traccar/protocol/T622IridiumProtocolDecoder.java @@ -0,0 +1,149 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.config.Keys; +import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public class T622IridiumProtocolDecoder extends BaseProtocolDecoder { + + private String format; + + public T622IridiumProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public List<Integer> getParameters(long deviceId) { + String value = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_FORMAT.withPrefix(getProtocolName()), deviceId); + return Arrays.stream((value != null ? value : format).split(",")) + .map(s -> Integer.parseInt(s, 16)) + .collect(Collectors.toList()); + } + + public void setFormat(String format) { + this.format = format; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.readUnsignedByte(); // protocol revision + buf.readUnsignedShort(); // length + buf.readUnsignedByte(); // header indicator + buf.readUnsignedShort(); // header length + buf.readUnsignedInt(); // reference + + String imei = buf.readCharSequence(15, StandardCharsets.US_ASCII).toString(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + if (deviceSession == null) { + return null; + } + + buf.readUnsignedByte(); // session status + buf.readUnsignedShort(); // originator index + buf.readUnsignedShort(); // transfer index + buf.readUnsignedInt(); // session time + buf.readUnsignedByte(); // payload indicator + buf.readUnsignedShort(); // payload length + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + List<Integer> parameters = getParameters(deviceSession.getDeviceId()); + + for (int parameter : parameters) { + switch (parameter) { + case 0x01: + position.set(Position.KEY_EVENT, buf.readUnsignedByte()); + break; + case 0x02: + position.setLatitude(buf.readIntLE() / 1000000.0); + break; + case 0x03: + position.setLongitude(buf.readIntLE() / 1000000.0); + break; + case 0x04: + position.setTime(new Date((buf.readUnsignedIntLE() + 946684800) * 1000)); + break; + case 0x05: + position.setValid(buf.readUnsignedByte() > 0); + break; + case 0x06: + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + break; + case 0x07: + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + break; + case 0x08: + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); + break; + case 0x09: + position.setCourse(buf.readUnsignedShortLE()); + break; + case 0x0A: + position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1); + break; + case 0x0B: + position.setAltitude(buf.readShortLE()); + break; + case 0x0C: + position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE()); + break; + case 0x0D: + position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000); + break; + case 0x14: + position.set(Position.KEY_OUTPUT, buf.readUnsignedByte()); + break; + case 0x15: + position.set(Position.KEY_INPUT, buf.readUnsignedByte()); + break; + case 0x19: + position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01); + break; + case 0x1A: + position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01); + break; + case 0x1B: + buf.readUnsignedByte(); // geofence + break; + default: + break; + } + } + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/T800xProtocol.java b/src/main/java/org/traccar/protocol/T800xProtocol.java index 253c3cb73..f50f22a18 100644 --- a/src/main/java/org/traccar/protocol/T800xProtocol.java +++ b/src/main/java/org/traccar/protocol/T800xProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class T800xProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java index 6e09e6e3b..23750be8d 100644 --- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -161,7 +163,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { boolean positionType = type == MSG_GPS || type == MSG_GPS_2 || type == MSG_ALARM || type == MSG_ALARM_2; if (!positionType) { - sendResponse(channel, header, type, index, imei, 0); + sendResponse(channel, header, type, header == 0x2323 ? 1 : index, imei, 0); } if (positionType) { @@ -389,7 +391,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { for (int i = 1; i <= adcCount; i++) { String value = ByteBufUtil.hexDump(buf.readSlice(2)); if (!value.equals("ffff")) { - position.set(Position.PREFIX_ADC + i, Integer.parseInt(value) * 0.01); + position.set(Position.PREFIX_ADC + i, Integer.parseInt(value, 16) * 0.01); } } } @@ -398,6 +400,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { int alarm = buf.readUnsignedByte(); position.set(Position.KEY_ALARM, header != 0x2727 ? decodeAlarm1(alarm) : decodeAlarm2(alarm)); + position.set("alarmCode", alarm); if (header != 0x2727) { @@ -468,6 +471,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { int inputStatus = buf.readUnsignedShort(); position.set(Position.KEY_IGNITION, BitUtil.check(inputStatus, 2)); position.set(Position.KEY_RSSI, BitUtil.between(inputStatus, 4, 11)); + position.set(Position.KEY_INPUT, inputStatus); buf.readUnsignedShort(); // ignition on upload interval buf.readUnsignedInt(); // ignition off upload interval @@ -511,8 +515,10 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder { } } - if (type == MSG_ALARM || type == MSG_ALARM_2) { - sendResponse(channel, header, type, index, imei, alarm); + boolean acknowledgement = AttributeUtil.lookup( + getCacheManager(), Keys.PROTOCOL_ACK.withPrefix(getProtocolName()), deviceSession.getDeviceId()); + if (acknowledgement || type == MSG_ALARM || type == MSG_ALARM_2) { + sendResponse(channel, header, type, header == 0x2323 ? 1 : index, imei, alarm); } return position; diff --git a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java index 48419af2a..75fd447d0 100644 --- a/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java +++ b/src/main/java/org/traccar/protocol/TaipPrefixEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,40 +15,20 @@ */ package org.traccar.protocol; -import com.google.inject.Inject; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; -import org.traccar.Protocol; -import org.traccar.config.Config; -import org.traccar.config.Keys; import java.util.List; @ChannelHandler.Sharable public class TaipPrefixEncoder extends MessageToMessageEncoder<ByteBuf> { - private final Protocol protocol; - private Config config; - - public TaipPrefixEncoder(Protocol protocol) { - this.protocol = protocol; - } - - @Inject - public void setConfig(Config config) { - this.config = config; - } - @Override protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) { - if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(protocol.getName()))) { - out.add(Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(new byte[] {0x20, 0x20, 0x06, 0x00}), msg.retain())); - } else { - out.add(msg.retain()); - } + out.add(Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(new byte[] {0x20, 0x20, 0x06, 0x00}), msg.retain())); } } diff --git a/src/main/java/org/traccar/protocol/TaipProtocol.java b/src/main/java/org/traccar/protocol/TaipProtocol.java index 943ec98c5..f57bb296c 100644 --- a/src/main/java/org/traccar/protocol/TaipProtocol.java +++ b/src/main/java/org/traccar/protocol/TaipProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,8 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; +import org.traccar.config.Keys; public class TaipProtocol extends BaseProtocol { @@ -33,7 +34,9 @@ public class TaipProtocol extends BaseProtocol { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '<')); - pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this)); + if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(getName()))) { + pipeline.addLast(new TaipPrefixEncoder()); + } pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this)); @@ -42,7 +45,9 @@ public class TaipProtocol extends BaseProtocol { addServer(new TrackerServer(config, getName(), true) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { - pipeline.addLast(new TaipPrefixEncoder(TaipProtocol.this)); + if (config.getBoolean(Keys.PROTOCOL_PREFIX.withPrefix(getName()))) { + pipeline.addLast(new TaipPrefixEncoder()); + } pipeline.addLast(new StringDecoder()); pipeline.addLast(new StringEncoder()); pipeline.addLast(new TaipProtocolDecoder(TaipProtocol.this)); diff --git a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java index e5e84b7c4..448d7ffca 100644 --- a/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TaipProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import org.traccar.helper.DateBuilder; import org.traccar.helper.DateUtil; import org.traccar.helper.Parser; import org.traccar.helper.PatternBuilder; -import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; import java.net.SocketAddress; @@ -49,7 +48,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { .groupEnd("?") .number("(d{5})") // seconds .or() - .expression("(?:RGP|RCQ|RCV|RBR|RUS00),?") // type + .expression("(?:RGP|RCQ|RCV|RBR|RUS00|RPI),?") // type .number("(dd)?") // event .number("(dd)(dd)(dd)") // date (mmddyy) .number("(dd)(dd)(dd)") // time (hhmmss) @@ -84,7 +83,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { .or() .groupBegin() .number("(xx)") // input - .number("(xx)") // satellites + .number("xx") // satellites / outputs .number("(ddd)") // battery .number("(x{8})") // odometer .number("[01]") // gps power @@ -96,12 +95,14 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { .number("[01]") // modem power .number("[0-5]") // gsm status .number("(dd)") // rssi + .groupBegin() .number("([-+]dddd)") // temperature 1 .number("xx") // seconds from last .number("([-+]dddd)") // temperature 2 .number("xx") // seconds from last .groupEnd("?") .groupEnd("?") + .groupEnd("?") .groupEnd() .any() .compile(); @@ -192,7 +193,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); } - position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0))); + position.setSpeed(convertSpeed(parser.nextDouble(0), "mph")); position.setCourse(parser.nextDouble(0)); if (parser.hasNext(2)) { @@ -217,17 +218,18 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_HDOP, parser.nextInt()); } - if (parser.hasNext(4)) { + if (parser.hasNext(3)) { position.set(Position.KEY_INPUT, parser.nextHexInt(0)); - position.set(Position.KEY_SATELLITES, parser.nextHexInt(0)); position.set(Position.KEY_BATTERY, parser.nextInt(0)); position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0)); } - if (parser.hasNext(4)) { + if (parser.hasNext(3)) { valid = parser.nextInt() > 0; position.set(Position.KEY_PDOP, parser.nextInt()); position.set(Position.KEY_RSSI, parser.nextInt()); + } + if (parser.hasNext(2)) { position.set(Position.PREFIX_TEMP + 1, parser.nextInt() * 0.01); position.set(Position.PREFIX_TEMP + 2, parser.nextInt() * 0.01); } @@ -262,6 +264,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { String uniqueId = null; DeviceSession deviceSession = null; String messageIndex = null; + boolean indexFirst = true; if (attributes != null) { for (String attribute : attributes) { @@ -276,6 +279,9 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { if (deviceSession != null) { position.setDeviceId(deviceSession.getDeviceId()); } + if (messageIndex == null) { + indexFirst = false; + } break; case "io": position.set(Position.KEY_IGNITION, BitUtil.check(value.charAt(0) - '0', 0)); @@ -315,7 +321,11 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { if (messageIndex.startsWith("#IP")) { response = ">SAK;ID=" + uniqueId + ";" + messageIndex + "<"; } else { - response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*"; + if (indexFirst) { + response = ">ACK;" + messageIndex + ";ID=" + uniqueId + ";*"; + } else { + response = ">ACK;ID=" + uniqueId + ";" + messageIndex + ";*"; + } response += String.format("%02X", Checksum.xor(response)) + "<"; } channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); diff --git a/src/main/java/org/traccar/protocol/TechTltProtocol.java b/src/main/java/org/traccar/protocol/TechTltProtocol.java index 191dd9ccc..a4a7460b0 100644 --- a/src/main/java/org/traccar/protocol/TechTltProtocol.java +++ b/src/main/java/org/traccar/protocol/TechTltProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TechTltProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java index 265a3eb64..f0828a99e 100644 --- a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java +++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TechtoCruzProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TekProtocol.java b/src/main/java/org/traccar/protocol/TekProtocol.java index 54e860d79..56714041b 100644 --- a/src/main/java/org/traccar/protocol/TekProtocol.java +++ b/src/main/java/org/traccar/protocol/TekProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TelemaxProtocol.java b/src/main/java/org/traccar/protocol/TelemaxProtocol.java index 9e9cbb50e..792a5b176 100644 --- a/src/main/java/org/traccar/protocol/TelemaxProtocol.java +++ b/src/main/java/org/traccar/protocol/TelemaxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TelemaxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TelicProtocol.java b/src/main/java/org/traccar/protocol/TelicProtocol.java index 9ef7864ca..fc5bdf0d1 100644 --- a/src/main/java/org/traccar/protocol/TelicProtocol.java +++ b/src/main/java/org/traccar/protocol/TelicProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TelicProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java index 3a0962584..aabc24887 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaFrameDecoder.java @@ -29,7 +29,7 @@ public class TeltonikaFrameDecoder extends BaseFrameDecoder { protected Object decode( ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { - while (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) { + if (buf.isReadable() && buf.getByte(buf.readerIndex()) == (byte) 0xff) { return buf.readRetainedSlice(1); } diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java index 38283cb64..f2d610251 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocol.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TeltonikaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java index 929eca8aa..6197c6c13 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.model.Device; +import org.traccar.helper.BufferUtil; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -33,6 +33,7 @@ import org.traccar.model.Network; import org.traccar.model.Position; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.HashMap; @@ -112,18 +113,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - private boolean isPrintable(ByteBuf buf, int length) { - boolean printable = true; - for (int i = 0; i < length; i++) { - byte b = buf.getByte(buf.readerIndex() + i); - if (b < 32 && b != '\r' && b != '\n') { - printable = false; - break; - } - } - return printable; - } - private void decodeSerial( Channel channel, SocketAddress remoteAddress, DeviceSession deviceSession, Position position, ByteBuf buf) { @@ -169,7 +158,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_TYPE, type); int length = buf.readInt(); - if (isPrintable(buf, length)) { + if (BufferUtil.isPrintable(buf, length)) { String data = buf.readSlice(length).toString(StandardCharsets.US_ASCII).trim(); if (data.startsWith("UUUUww") && data.endsWith("SSS")) { String[] values = data.substring(6, data.length() - 4).split(";"); @@ -231,6 +220,8 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { register(26, null, (p, b) -> p.set("bleTemp2", b.readShort() * 0.01)); register(27, null, (p, b) -> p.set("bleTemp3", b.readShort() * 0.01)); register(28, null, (p, b) -> p.set("bleTemp4", b.readShort() * 0.01)); + register(30, fmbXXX, (p, b) -> p.set("faultCount", b.readUnsignedByte())); + register(32, fmbXXX, (p, b) -> p.set(Position.KEY_COOLANT_TEMP, b.readByte())); register(66, null, (p, b) -> p.set(Position.KEY_POWER, b.readUnsignedShort() * 0.001)); register(67, null, (p, b) -> p.set(Position.KEY_BATTERY, b.readUnsignedShort() * 0.001)); register(68, fmbXXX, (p, b) -> p.set("batteryCurrent", b.readUnsignedShort() * 0.001)); @@ -239,28 +230,52 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { register(74, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 3, b.readInt() * 0.1)); register(75, fmbXXX, (p, b) -> p.set(Position.PREFIX_TEMP + 4, b.readInt() * 0.1)); register(78, null, (p, b) -> { - long driverUniqueId = b.readLong(); + long driverUniqueId = b.readLongLE(); if (driverUniqueId > 0) { p.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", driverUniqueId)); } }); register(80, fmbXXX, (p, b) -> p.set("dataMode", b.readUnsignedByte())); + register(81, fmbXXX, (p, b) -> p.set(Position.KEY_OBD_SPEED, b.readUnsignedByte())); + register(82, fmbXXX, (p, b) -> p.set(Position.KEY_THROTTLE, b.readUnsignedByte())); + register(83, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_USED, b.readUnsignedInt() * 0.1)); + register(84, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_LEVEL, b.readUnsignedShort() * 0.1)); + register(85, fmbXXX, (p, b) -> p.set(Position.KEY_RPM, b.readUnsignedShort())); + register(87, fmbXXX, (p, b) -> p.set(Position.KEY_OBD_ODOMETER, b.readUnsignedInt())); + register(89, fmbXXX, (p, b) -> p.set("fuelLevelPercentage", b.readUnsignedByte())); register(90, null, (p, b) -> p.set(Position.KEY_DOOR, b.readUnsignedShort())); - register(115, fmbXXX, (p, b) -> p.set(Position.KEY_COOLANT_TEMP, b.readShort() * 0.1)); + register(110, fmbXXX, (p, b) -> p.set(Position.KEY_FUEL_CONSUMPTION, b.readUnsignedShort() * 0.1)); + register(113, fmbXXX, (p, b) -> p.set(Position.KEY_BATTERY_LEVEL, b.readUnsignedByte())); register(179, null, (p, b) -> p.set(Position.PREFIX_OUT + 1, b.readUnsignedByte() > 0)); register(180, null, (p, b) -> p.set(Position.PREFIX_OUT + 2, b.readUnsignedByte() > 0)); register(181, null, (p, b) -> p.set(Position.KEY_PDOP, b.readUnsignedShort() * 0.1)); register(182, null, (p, b) -> p.set(Position.KEY_HDOP, b.readUnsignedShort() * 0.1)); register(199, null, (p, b) -> p.set(Position.KEY_ODOMETER_TRIP, b.readUnsignedInt())); register(200, fmbXXX, (p, b) -> p.set("sleepMode", b.readUnsignedByte())); - register(205, fmbXXX, (p, b) -> p.set("cid", b.readUnsignedShort())); + register(205, fmbXXX, (p, b) -> p.set("cid2g", b.readUnsignedShort())); register(206, fmbXXX, (p, b) -> p.set("lac", b.readUnsignedShort())); + register(232, fmbXXX, (p, b) -> p.set("cngStatus", b.readUnsignedByte() > 0)); + register(233, fmbXXX, (p, b) -> p.set("cngUsed", b.readUnsignedInt() * 0.1)); + register(234, fmbXXX, (p, b) -> p.set("cngLevel", b.readUnsignedShort())); + register(235, fmbXXX, (p, b) -> p.set("oilLevel", b.readUnsignedByte())); register(236, null, (p, b) -> { p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_GENERAL : null); }); register(239, null, (p, b) -> p.set(Position.KEY_IGNITION, b.readUnsignedByte() > 0)); register(240, null, (p, b) -> p.set(Position.KEY_MOTION, b.readUnsignedByte() > 0)); register(241, null, (p, b) -> p.set(Position.KEY_OPERATOR, b.readUnsignedInt())); + register(246, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_TOW : null); + }); + register(247, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_ACCIDENT : null); + }); + register(249, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_JAMMING : null); + }); + register(252, fmbXXX, (p, b) -> { + p.set(Position.KEY_ALARM, b.readUnsignedByte() > 0 ? Position.ALARM_POWER_CUT : null); + }); register(253, null, (p, b) -> { switch (b.readUnsignedByte()) { case 1: @@ -276,6 +291,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { break; } }); + register(636, fmbXXX, (p, b) -> p.set("cid4g", b.readUnsignedInt())); } private void decodeGh3000Parameter(Position position, int id, ByteBuf buf, int length) { @@ -366,14 +382,23 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(network); } } else { - Integer cid = (Integer) position.getAttributes().remove("cid"); + Integer cid2g = (Integer) position.getAttributes().remove("cid2g"); + Long cid4g = (Long) position.getAttributes().remove("cid4g"); Integer lac = (Integer) position.getAttributes().remove("lac"); - if (cid != null && lac != null) { - CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid); + if (lac != null && (cid2g != null || cid4g != null)) { + Network network = new Network(); + CellTower cellTower; + if (cid2g != null) { + cellTower = CellTower.fromLacCid(getConfig(), lac, cid2g); + } else { + cellTower = CellTower.fromLacCid(getConfig(), lac, cid4g); + network.setRadioType("lte"); + } long operator = position.getInteger(Position.KEY_OPERATOR); if (operator >= 1000) { cellTower.setOperator(operator); } + network.addCellTower(cellTower); position.setNetwork(new Network(cellTower)); } } @@ -563,6 +588,38 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } index += 1; } + } else if (id == 548 || id == 10829 || id == 10831) { + ByteBuf data = buf.readSlice(length); + data.readUnsignedByte(); // header + for (int i = 1; data.isReadable(); i++) { + ByteBuf beacon = data.readSlice(data.readUnsignedByte()); + while (beacon.isReadable()) { + int parameterId = beacon.readUnsignedByte(); + int parameterLength = beacon.readUnsignedByte(); + switch (parameterId) { + case 0: + position.set("tag" + i + "Rssi", (int) beacon.readByte()); + break; + case 1: + String beaconId = ByteBufUtil.hexDump(beacon.readSlice(parameterLength)); + position.set("tag" + i + "Id", beaconId); + break; + case 2: + String beaconData = ByteBufUtil.hexDump(beacon.readSlice(parameterLength)); + position.set("tag" + i + "Data", beaconData); + break; + case 13: + position.set("tag" + i + "LowBattery", beacon.readUnsignedByte()); + break; + case 14: + position.set("tag" + i + "Battery", beacon.readUnsignedShort()); + break; + default: + beacon.skipBytes(parameterLength); + break; + } + } + } } else { position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(length))); } @@ -571,6 +628,14 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { decodeNetwork(position, model); + if (model != null && model.matches("FM.6..")) { + Long driverMsb = (Long) position.getAttributes().get("io195"); + Long driverLsb = (Long) position.getAttributes().get("io196"); + if (driverMsb != null && driverLsb != null) { + String driver = new String(ByteBuffer.allocate(16).putLong(driverMsb).putLong(driverLsb).array()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, driver); + } + } } private List<Position> parseData( @@ -588,7 +653,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (deviceSession == null) { return null; } - String model = getCacheManager().getObject(Device.class, deviceSession.getDeviceId()).getModel(); for (int i = 0; i < count; i++) { Position position = new Position(getProtocolName()); @@ -600,9 +664,13 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // type int length = buf.readInt() - 4; getLastLocation(position, new Date(buf.readUnsignedInt() * 1000)); - if (isPrintable(buf, length)) { - position.set(Position.KEY_RESULT, - buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim()); + if (BufferUtil.isPrintable(buf, length)) { + String data = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim(); + if (data.startsWith("GTSL")) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, data.split("\\|")[4]); + } else { + position.set(Position.KEY_RESULT, data); + } } else { position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(buf.readSlice(length))); @@ -610,7 +678,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } else if (codec == CODEC_12) { decodeSerial(channel, remoteAddress, deviceSession, position, buf); } else { - decodeLocation(position, buf, codec, model); + decodeLocation(position, buf, codec, getDeviceModel(deviceSession)); } if (!position.getOutdated() || !position.getAttributes().isEmpty()) { diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java index 73219cc5e..e872ddf42 100644 --- a/src/main/java/org/traccar/protocol/TeraTrackProtocol.java +++ b/src/main/java/org/traccar/protocol/TeraTrackProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TeraTrackProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java index 313210f63..be4b98e4c 100644 --- a/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeraTrackProtocolDecoder.java @@ -23,8 +23,8 @@ import org.traccar.Protocol; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; import java.io.StringReader; import java.net.SocketAddress; import java.text.DateFormat; @@ -63,7 +63,7 @@ public class TeraTrackProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(json.getString("Speed")))); position.set(Position.KEY_ODOMETER, Integer.parseInt(json.getString("Mileage"))); - position.set(Position.KEY_BLOCKED, json.getString("LockOpen").equals("0")); + position.set(Position.KEY_LOCK, json.getString("LockOpen").equals("0")); position.set(Position.KEY_DRIVER_UNIQUE_ID, json.getString("CardNo")); position.set(Position.KEY_ALARM, json.getString("LowPower").equals("1") ? Position.ALARM_LOW_POWER : null); position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(json.getString("Power"))); diff --git a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java index 38bf078aa..b23dadf08 100644 --- a/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java +++ b/src/main/java/org/traccar/protocol/ThinkPowerProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ThinkPowerProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java index 782b0a352..34b80ba87 100644 --- a/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java +++ b/src/main/java/org/traccar/protocol/ThinkRaceProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ThinkRaceProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocol.java b/src/main/java/org/traccar/protocol/ThurayaProtocol.java index f709a1183..33d486f6b 100644 --- a/src/main/java/org/traccar/protocol/ThurayaProtocol.java +++ b/src/main/java/org/traccar/protocol/ThurayaProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class ThurayaProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Tk102Protocol.java b/src/main/java/org/traccar/protocol/Tk102Protocol.java index 150e83ab3..b6a82981b 100644 --- a/src/main/java/org/traccar/protocol/Tk102Protocol.java +++ b/src/main/java/org/traccar/protocol/Tk102Protocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Tk102Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Tk103Protocol.java b/src/main/java/org/traccar/protocol/Tk103Protocol.java index cf09886f5..b641ef083 100644 --- a/src/main/java/org/traccar/protocol/Tk103Protocol.java +++ b/src/main/java/org/traccar/protocol/Tk103Protocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Tk103Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java index b343c3b33..6c926da90 100644 --- a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,11 @@ */ package org.traccar.protocol; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DataConverter; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -448,6 +451,106 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return position; } + private Position decodeBms(Channel channel, SocketAddress remoteAddress, String sentence) { + String id = sentence.substring(1, 13); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + getLastLocation(position, null); + + String payload = sentence.substring(1 + 12 + 4, sentence.length() - 1); + + if (sentence.startsWith("BS50", 1 + 12)) { + + ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(payload)); + + buf.readUnsignedByte(); + buf.readUnsignedByte(); + buf.readUnsignedByte(); // header + + int batteryCount = buf.readUnsignedByte(); + for (int i = 1; i <= 24; i++) { + int voltage = buf.readUnsignedShortLE(); + if (i <= batteryCount) { + position.set("battery" + i, voltage * 0.001); + } + } + + position.set(Position.KEY_CHARGE, buf.readUnsignedByte() == 0); + position.set("current", buf.readUnsignedShortLE() * 0.1); + position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.01); + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + position.set("batteryOverheat", buf.readUnsignedByte() > 0); + position.set("chargeProtection", buf.readUnsignedByte() > 0); + position.set("dischargeProtection", buf.readUnsignedByte() > 0); + buf.readUnsignedByte(); // drop line + buf.readUnsignedByte(); // balanced + position.set("cycles", buf.readUnsignedShortLE()); + position.set("faultAlarm", buf.readUnsignedByte()); + + buf.skipBytes(6); + + int temperatureCount = buf.readUnsignedByte(); + position.set("powerTemp", buf.readUnsignedByte() - 40); + position.set("equilibriumTemp", buf.readUnsignedByte() - 40); + for (int i = 1; i <= 7; i++) { + int temperature = buf.readUnsignedByte() - 40; + if (i <= temperatureCount) { + position.set("batteryTemp" + i, temperature); + } + } + + position.set("calibrationCapacity", buf.readUnsignedShortLE() * 0.01); + position.set("dischargeCapacity", buf.readUnsignedIntLE()); + + } else { + + String[] values = payload.split(","); + for (String value : values) { + String[] pair = value.split(":"); + int key = Integer.parseInt(pair[0], 16); + ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(pair[1])); + switch (key) { + case 0x90: + position.set("cumulativeVoltage", buf.readUnsignedShortLE() * 0.1); + position.set("gatherVoltage", buf.readUnsignedShortLE() * 0.1); + position.set("current", (buf.readUnsignedShortLE() - 30000) * 0.1); + position.set("soc", buf.readUnsignedShortLE() * 0.1); + break; + case 0x91: + position.set("maxCellVoltage", buf.readUnsignedShortLE() * 0.001); + position.set("maxCellVoltageCount", buf.readUnsignedByte()); + position.set("minCellVoltage", buf.readUnsignedShortLE() * 0.001); + position.set("minCellVoltageCount", buf.readUnsignedByte()); + break; + case 0x92: + position.set("maxTemp", buf.readUnsignedByte() - 40); + position.set("maxTempCount", buf.readUnsignedByte()); + position.set("minTemp", buf.readUnsignedByte() - 40); + position.set("minTempCount", buf.readUnsignedByte()); + break; + case 0x96: + buf.readUnsignedByte(); // frame + while (buf.isReadable()) { + position.set("cellTemp" + buf.readerIndex(), buf.readUnsignedByte() - 40); + } + break; + default: + break; + } + + } + + } + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -477,6 +580,8 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { return decodeLbsWifi(channel, remoteAddress, sentence); } else if (sentence.contains("BV00")) { return decodeVin(channel, remoteAddress, sentence); + } else if (sentence.contains("BS50") || sentence.contains("BS51")) { + return decodeBms(channel, remoteAddress, sentence); } Parser parser = new Parser(PATTERN, sentence); diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java index b10271f7d..6763e9b6b 100644 --- a/src/main/java/org/traccar/protocol/Tlt2hProtocol.java +++ b/src/main/java/org/traccar/protocol/Tlt2hProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Tlt2hProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java index e85bdf9b3..6be3d2dc3 100644 --- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,16 +55,20 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN_POSITION = new PatternBuilder() .text("#") - .number("(?:(dd)|x*)") // cell or voltage + .number("(?:(dd|dddd)|x*)") // cell or voltage .groupBegin() - .number("#(d+),") // mcc + .text("#") + .groupBegin() + .number("(d+),") // mcc .number("(d+),") // mnc .number("(x+),") // lac .number("(x+)") // cell id .groupEnd("?") + .groupEnd("?") .text("$GPRMC,") - .number("(dd)(dd)(dd).d+,") // time (hhmmss.sss) + .number("(?:(dd)(dd)(dd).d+)?,") // time (hhmmss.sss) .expression("([AVL]),") // validity + .groupBegin() .number("(d+)(dd.d+),") // latitude .expression("([NS]),") .number("(d+)(dd.d+),") // longitude @@ -72,14 +76,16 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { .number("(d+.?d*)?,") // speed .number("(d+.?d*)?,") // course .number("(dd)(dd)(dd)") // date (ddmmyy) + .groupEnd("?") .any() .compile(); private static final Pattern PATTERN_WIFI = new PatternBuilder() .text("#") - .number("(?:(dd)|x+)") // cell or voltage + .number("(?:(dd|dddd)|x+)") // cell or voltage + .expression("#?") .groupBegin() - .number("#(d+),") // mcc + .number("(d+),") // mcc .number("(d+),") // mnc .number("(x+),") // lac .number("(x+)") // cell id @@ -178,7 +184,8 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { if (parser.matches()) { if (parser.hasNext()) { - position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1); + int voltage = parser.nextInt(); + position.set(Position.KEY_BATTERY, voltage > 100 ? voltage * 0.001 : voltage * 0.1); } if (parser.hasNext(4)) { @@ -188,17 +195,26 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(network); } - DateBuilder dateBuilder = new DateBuilder() - .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + DateBuilder dateBuilder = new DateBuilder(); + if (parser.hasNext(3)) { + dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + } position.setValid(parser.next().equals("A")); - position.setLatitude(parser.nextCoordinate()); - position.setLongitude(parser.nextCoordinate()); - position.setSpeed(parser.nextDouble(0)); - position.setCourse(parser.nextDouble(0)); - dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); - position.setTime(dateBuilder.getDate()); + if (parser.hasNext()) { + + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble(0)); + position.setCourse(parser.nextDouble(0)); + + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + } else { + getLastLocation(position, null); + } } else { continue; @@ -209,7 +225,10 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { parser = new Parser(PATTERN_WIFI, message); if (parser.matches()) { - position.set(Position.KEY_BATTERY, parser.nextInt() * 0.1); + if (parser.hasNext()) { + int voltage = parser.nextInt(); + position.set(Position.KEY_BATTERY, voltage > 100 ? voltage * 0.001 : voltage * 0.1); + } Network network = new Network(); if (parser.hasNext(4)) { @@ -230,6 +249,8 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder { dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); getLastLocation(position, dateBuilder.getDate()); + } else { + continue; } } else { diff --git a/src/main/java/org/traccar/protocol/TlvProtocol.java b/src/main/java/org/traccar/protocol/TlvProtocol.java index 9d83388c9..f99676d23 100644 --- a/src/main/java/org/traccar/protocol/TlvProtocol.java +++ b/src/main/java/org/traccar/protocol/TlvProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TlvProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TmgProtocol.java b/src/main/java/org/traccar/protocol/TmgProtocol.java index e078c425b..dbba648be 100644 --- a/src/main/java/org/traccar/protocol/TmgProtocol.java +++ b/src/main/java/org/traccar/protocol/TmgProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TmgProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TopflytechProtocol.java b/src/main/java/org/traccar/protocol/TopflytechProtocol.java index 339d2fc8d..a658235ab 100644 --- a/src/main/java/org/traccar/protocol/TopflytechProtocol.java +++ b/src/main/java/org/traccar/protocol/TopflytechProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TopflytechProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java index b15373d71..1a558f617 100644 --- a/src/main/java/org/traccar/protocol/TopinProtocol.java +++ b/src/main/java/org/traccar/protocol/TopinProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2019 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,19 @@ import org.traccar.BaseProtocol; import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TopinProtocol extends BaseProtocol { @Inject public TopinProtocol(Config config) { - setSupportedDataCommands( - Command.TYPE_SOS_NUMBER); + if (!config.getBoolean(Keys.PROTOCOL_DISABLE_COMMANDS.withPrefix(getName()))) { + setSupportedDataCommands( + Command.TYPE_SOS_NUMBER); + } addServer(new TrackerServer(config, getName(), false) { @Override protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java index a1d5481db..c7b816818 100644 --- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java @@ -48,7 +48,12 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_GPS = 0x10; public static final int MSG_GPS_OFFLINE = 0x11; public static final int MSG_STATUS = 0x13; + public static final int MSG_SLEEP = 0x14; + public static final int MSG_FACTORY_RESET = 0x15; public static final int MSG_WIFI_OFFLINE = 0x17; + public static final int MSG_LBS_WIFI = 0x18; + public static final int MSG_LBS_WIFI_OFFLINE = 0x19; + public static final int MSG_LBS_WIFI_2 = 0x1A; public static final int MSG_TIME_UPDATE = 0x30; public static final int MSG_SOS_NUMBER = 0x41; public static final int MSG_WIFI = 0x69; @@ -216,7 +221,8 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder { return position; - } else if (type == MSG_WIFI || type == MSG_WIFI_OFFLINE) { + } else if (type == MSG_WIFI || type == MSG_WIFI_OFFLINE + || type == MSG_LBS_WIFI || type == MSG_LBS_WIFI_2 || type == MSG_LBS_WIFI_OFFLINE) { Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); diff --git a/src/main/java/org/traccar/protocol/TotemProtocol.java b/src/main/java/org/traccar/protocol/TotemProtocol.java index 9ab36fd0b..b02d4f1fc 100644 --- a/src/main/java/org/traccar/protocol/TotemProtocol.java +++ b/src/main/java/org/traccar/protocol/TotemProtocol.java @@ -23,7 +23,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TotemProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java index 9d0d794f8..6f039c324 100644 --- a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -135,7 +135,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN4 = new PatternBuilder() .text("$$") // header .number("dddd") // length - .number("(xx)") // type + .number("xx") // type .number("(d+)|") // imei .number("(x{8})") // status .number("(dd)(dd)(dd)") // date (yymmdd) @@ -176,7 +176,21 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); - private static final Pattern PATTERN_OBD = new PatternBuilder() + private static final Pattern PATTERN_E2 = new PatternBuilder() + .text("$$") // header + .number("dddd") // length + .number("xx") // type + .number("(d+)|") // imei + .number("(dd)(dd)(dd)") // date (yymmdd) + .number("(dd)(dd)(dd),") // time (hhmmss) + .number("(-?d+.d+),") // longitude + .number("(-?d+.d+),") // latitude + .expression("(.+)") // rfid + .number("|xx") // checksum + .any() + .compile(); + + private static final Pattern PATTERN_E5 = new PatternBuilder() .text("$$") // header .number("dddd") // length .number("xx") // type @@ -257,7 +271,20 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { } } - private boolean decode12(Position position, Parser parser, Pattern pattern) { + private Position decode12(Channel channel, SocketAddress remoteAddress, String sentence, Pattern pattern) { + + Parser parser = new Parser(pattern, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); if (parser.hasNext()) { position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16))); @@ -283,7 +310,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { year = parser.nextInt(0); } if (year == 0) { - return false; // ignore invalid data + return null; // ignore invalid data } dateBuilder.setDate(year, month, day); position.setTime(dateBuilder.getDate()); @@ -331,10 +358,23 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.PREFIX_TEMP + 1, parser.next()); position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000); - return true; + return position; } - private boolean decode3(Position position, Parser parser) { + private Position decode3(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN3, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); if (parser.hasNext()) { position.set(Position.KEY_ALARM, decodeAlarm123(Short.parseShort(parser.next(), 16))); @@ -363,10 +403,36 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - return true; + return position; } - private boolean decode4(Position position, Parser parser) { + private Position decode4(Channel channel, SocketAddress remoteAddress, String sentence) { + + int type = Integer.parseInt(sentence.substring(6, 8), 16); + + switch (type) { + case 0xE2: + return decodeE2(channel, remoteAddress, sentence); + case 0xE5: + return decodeE5(channel, remoteAddress, sentence); + default: + break; + } + + Parser parser = new Parser(PATTERN4, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_ALARM, decodeAlarm4(type)); long status = parser.nextHexLong(); @@ -427,10 +493,48 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - return true; + return position; } - private boolean decodeObd(Position position, Parser parser) { + private Position decodeE2(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_E2, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(parser.nextDateTime()); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); + + return position; + } + + private Position decodeE5(Channel channel, SocketAddress remoteAddress, String sentence) { + + Parser parser = new Parser(PATTERN_E5, sentence); + if (!parser.matches()) { + return null; + } + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); position.setValid(true); position.setTime(parser.nextDateTime()); @@ -451,7 +555,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_THROTTLE, parser.nextInt()); position.set(Position.KEY_FUEL_LEVEL, parser.nextInt()); - return true; + return position; } @Override @@ -459,50 +563,23 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; - Pattern pattern = PATTERN3; - if (sentence.contains("$Cloud")) { - pattern = PATTERN_OBD; - } else if (sentence.charAt(2) == '0') { - pattern = PATTERN4; + + Position position; + if (sentence.charAt(2) == '0') { + position = decode4(channel, remoteAddress, sentence); } else if (sentence.contains("$GPRMC")) { - pattern = PATTERN1; + position = decode12(channel, remoteAddress, sentence, PATTERN1); } else { int index = sentence.indexOf('|'); if (index != -1 && sentence.indexOf('|', index + 1) != -1) { - pattern = PATTERN2; + position = decode12(channel, remoteAddress, sentence, PATTERN2); + } else { + position = decode3(channel, remoteAddress, sentence); } } - Parser parser = new Parser(pattern, sentence); - if (!parser.matches()) { - return null; - } - - Position position = new Position(getProtocolName()); - - if (pattern == PATTERN4) { - position.set(Position.KEY_ALARM, decodeAlarm4(parser.nextHexInt())); - } - - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); - - boolean result; - if (pattern == PATTERN1 || pattern == PATTERN2) { - result = decode12(position, parser, pattern); - } else if (pattern == PATTERN3) { - result = decode3(position, parser); - } else if (pattern == PATTERN4) { - result = decode4(position, parser); - } else { - result = decodeObd(position, parser); - } - if (channel != null) { - if (pattern == PATTERN4) { + if (sentence.charAt(2) == '0') { String response = "$$0014AA" + sentence.substring(sentence.length() - 6, sentence.length() - 2); response += String.format("%02X", Checksum.xor(response)).toUpperCase(); channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); @@ -511,7 +588,7 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder { } } - return result ? position : null; + return position; } } diff --git a/src/main/java/org/traccar/protocol/Tr20Protocol.java b/src/main/java/org/traccar/protocol/Tr20Protocol.java index 615fdab28..3b3fc02b6 100644 --- a/src/main/java/org/traccar/protocol/Tr20Protocol.java +++ b/src/main/java/org/traccar/protocol/Tr20Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Tr20Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Tr900Protocol.java b/src/main/java/org/traccar/protocol/Tr900Protocol.java index 162cbe651..c5f357604 100644 --- a/src/main/java/org/traccar/protocol/Tr900Protocol.java +++ b/src/main/java/org/traccar/protocol/Tr900Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Tr900Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TrackboxProtocol.java b/src/main/java/org/traccar/protocol/TrackboxProtocol.java index 4236144a3..eadcd07f9 100644 --- a/src/main/java/org/traccar/protocol/TrackboxProtocol.java +++ b/src/main/java/org/traccar/protocol/TrackboxProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TrackboxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TrakMateProtocol.java b/src/main/java/org/traccar/protocol/TrakMateProtocol.java index b7637e6f3..f4e7c5e60 100644 --- a/src/main/java/org/traccar/protocol/TrakMateProtocol.java +++ b/src/main/java/org/traccar/protocol/TrakMateProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TrakMateProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java b/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java index e4c94dc77..72cadf21a 100644 --- a/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/TramigoFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,13 @@ public class TramigoFrameDecoder extends BaseFrameDecoder { return null; } + int protocol = buf.getUnsignedByte(buf.readerIndex()); + int length; - if (buf.getUnsignedByte(buf.readerIndex()) == 0x80) { + if (protocol == 0x80) { length = buf.getUnsignedShortLE(buf.readerIndex() + 6); + } else if (protocol == 0x02 || protocol == 0x04) { + length = buf.getUnsignedShortLE(buf.readerIndex() + 1); } else { length = buf.getUnsignedShort(buf.readerIndex() + 6); } diff --git a/src/main/java/org/traccar/protocol/TramigoProtocol.java b/src/main/java/org/traccar/protocol/TramigoProtocol.java index 79a59abd3..5d8baf2a9 100644 --- a/src/main/java/org/traccar/protocol/TramigoProtocol.java +++ b/src/main/java/org/traccar/protocol/TramigoProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TramigoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java index 21dd78da3..4a9a9a58f 100644 --- a/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TramigoProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; import org.traccar.session.DeviceSession; import org.traccar.NetworkMessage; import org.traccar.Protocol; @@ -29,6 +31,7 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.text.DateFormat; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -42,9 +45,6 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - public static final int MSG_COMPACT = 0x0100; - public static final int MSG_FULL = 0x00FE; - private static final String[] DIRECTIONS = new String[] {"N", "NE", "E", "SE", "S", "SW", "W", "NW"}; @Override @@ -54,34 +54,47 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { ByteBuf buf = (ByteBuf) msg; int protocol = buf.readUnsignedByte(); - boolean legacy = protocol == 0x80; + + if (protocol == 0x01) { + return decode01(channel, remoteAddress, buf); + } else if (protocol == 0x04) { + return decode04(channel, remoteAddress, buf); + } else if (protocol == 0x80) { + return decode80(channel, remoteAddress, buf); + } + + return null; + } + + private Position decode01(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { buf.readUnsignedByte(); // version id - int index = legacy ? buf.readUnsignedShort() : buf.readUnsignedShortLE(); - int type = legacy ? buf.readUnsignedShort() : buf.readUnsignedShortLE(); - buf.readUnsignedShort(); // length - buf.readUnsignedShort(); // mask - buf.readUnsignedShort(); // checksum - long id = legacy ? buf.readUnsignedInt() : buf.readUnsignedIntLE(); - buf.readUnsignedInt(); // time + int index = buf.readUnsignedShortLE(); + int type = buf.readUnsignedShortLE(); - Position position = new Position(getProtocolName()); - position.set(Position.KEY_INDEX, index); - position.setValid(true); + if (type == 0x0100 || type == 0x00FE) { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); - if (deviceSession == null) { - return null; - } - position.setDeviceId(deviceSession.getDeviceId()); + buf.readUnsignedShort(); // length + buf.readUnsignedShort(); // mask + buf.readUnsignedShort(); // checksum + long id = buf.readUnsignedIntLE(); + buf.readUnsignedInt(); // time - if (protocol == 0x01 && (type == MSG_COMPACT || type == MSG_FULL)) { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_INDEX, index); // need to send ack? buf.readUnsignedShortLE(); // report trigger buf.readUnsignedShortLE(); // state flag + position.setValid(true); position.setLatitude(buf.readUnsignedIntLE() * 0.0000001); position.setLongitude(buf.readUnsignedIntLE() * 0.0000001); @@ -105,56 +118,183 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { return position; - } else if (legacy) { + } - if (channel != null) { - channel.writeAndFlush(new NetworkMessage( - Unpooled.copiedBuffer("gprs,ack," + index, StandardCharsets.US_ASCII), remoteAddress)); - } + return null; - String sentence = buf.toString(StandardCharsets.US_ASCII); + } - Pattern pattern = Pattern.compile("(-?\\d+\\.\\d+), (-?\\d+\\.\\d+)"); - Matcher matcher = pattern.matcher(sentence); - if (!matcher.find()) { - return null; - } - position.setLatitude(Double.parseDouble(matcher.group(1))); - position.setLongitude(Double.parseDouble(matcher.group(2))); - - pattern = Pattern.compile("([NSWE]{1,2}) with speed (\\d+) km/h"); - matcher = pattern.matcher(sentence); - if (matcher.find()) { - for (int i = 0; i < DIRECTIONS.length; i++) { - if (matcher.group(1).equals(DIRECTIONS[i])) { - position.setCourse(i * 45.0); - break; - } - } - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2)))); - } + private Position decode04(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { - pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})"); - matcher = pattern.matcher(sentence); - if (!matcher.find()) { - return null; - } - DateFormat dateFormat = new SimpleDateFormat( - matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH); - position.setTime(DateUtil.correctYear( - dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR)))); - - if (sentence.contains("Ignition on detected")) { - position.set(Position.KEY_IGNITION, true); - } else if (sentence.contains("Ignition off detected")) { - position.set(Position.KEY_IGNITION, false); + buf.readUnsignedShortLE(); // length + buf.readUnsignedShortLE(); // checksum + int index = buf.readUnsignedShortLE(); + long id1 = buf.readUnsignedIntLE(); + long id2 = buf.readUnsignedIntLE(); + long time = buf.readUnsignedIntLE(); + + String id = String.format("%08d%07d", id1, id2); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + return null; + } + + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeByte(0x04); // protocol + response.writeShortLE(24); // length + response.writeShortLE(0); // checksum + response.writeShortLE(index); + response.writeIntLE((int) id1); + response.writeIntLE((int) id2); + response.writeIntLE((int) time); + + response.writeByte(0xff); // acknowledgement + response.writeShortLE(index); + response.writeShortLE(0); // success + + response.setShortLE(3, Checksum.crc16(Checksum.CRC16_CCITT_FALSE, response.nioBuffer())); + + channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_INDEX, index); + + position.setDeviceTime(new Date(time * 1000)); + + while (buf.isReadable()) { + int type = buf.readUnsignedByte(); + switch (type) { + case 0: + position.set(Position.KEY_EVENT, buf.readUnsignedShortLE()); + buf.readUnsignedIntLE(); // event data + + int status = buf.readUnsignedShortLE(); + position.set(Position.KEY_IGNITION, BitUtil.check(status, 5)); + position.set(Position.KEY_STATUS, status); + + position.setValid(true); + position.setLatitude(buf.readIntLE() * 0.00001); + position.setLongitude(buf.readIntLE() * 0.00001); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); + position.setCourse(buf.readUnsignedShortLE()); + + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + position.set(Position.KEY_GPS, buf.readUnsignedByte()); + position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte()); + position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShortLE()); + position.set("maxAcceleration", buf.readUnsignedShortLE() * 0.001); + position.set("maxDeceleration", buf.readUnsignedShortLE() * 0.001); + buf.readUnsignedShortLE(); // bearing to landmark + buf.readUnsignedIntLE(); // distance to landmark + + position.setFixTime(new Date(buf.readUnsignedIntLE() * 1000)); + + buf.readUnsignedByte(); // reserved + break; + case 1: + buf.skipBytes(buf.readUnsignedShortLE() - 3); // landmark + break; + case 4: + buf.skipBytes(53); // trip + break; + case 20: + buf.skipBytes(32); // extended + break; + case 22: + buf.readUnsignedByte(); // zone flag + buf.skipBytes(buf.readUnsignedShortLE()); // zone name + break; + case 30: + buf.skipBytes(79); // system status + break; + case 40: + buf.skipBytes(40); // analog + break; + case 50: + buf.skipBytes(buf.readUnsignedShortLE() - 3); // console + break; + case 255: + buf.skipBytes(4); // acknowledgement + break; + default: + throw new IllegalArgumentException(String.format("Unknown type %d", type)); } + } - return position; + return position.getValid() ? position : null; + + } + private Position decode80(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws ParseException { + + buf.readUnsignedByte(); // version id + int index = buf.readUnsignedShort(); + buf.readUnsignedShort(); // type + + buf.readUnsignedShort(); // length + buf.readUnsignedShort(); // mask + buf.readUnsignedShort(); // checksum + long id = buf.readUnsignedInt(); + buf.readUnsignedInt(); // time + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; } - return null; + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + position.set(Position.KEY_INDEX, index); + + if (channel != null) { + channel.writeAndFlush(new NetworkMessage( + Unpooled.copiedBuffer("gprs,ack," + index, StandardCharsets.US_ASCII), remoteAddress)); + } + + String sentence = buf.toString(StandardCharsets.US_ASCII); + + Pattern pattern = Pattern.compile("(-?\\d+\\.\\d+), (-?\\d+\\.\\d+)"); + Matcher matcher = pattern.matcher(sentence); + if (!matcher.find()) { + return null; + } + position.setLatitude(Double.parseDouble(matcher.group(1))); + position.setLongitude(Double.parseDouble(matcher.group(2))); + position.setValid(true); + + pattern = Pattern.compile("([NSWE]{1,2}) with speed (\\d+) km/h"); + matcher = pattern.matcher(sentence); + if (matcher.find()) { + for (int i = 0; i < DIRECTIONS.length; i++) { + if (matcher.group(1).equals(DIRECTIONS[i])) { + position.setCourse(i * 45.0); + break; + } + } + position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2)))); + } + + pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})"); + matcher = pattern.matcher(sentence); + if (!matcher.find()) { + return null; + } + DateFormat dateFormat = new SimpleDateFormat( + matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH); + position.setTime(DateUtil.correctYear( + dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR)))); + + if (sentence.contains("Ignition on detected")) { + position.set(Position.KEY_IGNITION, true); + } else if (sentence.contains("Ignition off detected")) { + position.set(Position.KEY_IGNITION, false); + } + + return position; + } } diff --git a/src/main/java/org/traccar/protocol/TranSyncProtocol.java b/src/main/java/org/traccar/protocol/TranSyncProtocol.java new file mode 100644 index 000000000..fb37a1ab4 --- /dev/null +++ b/src/main/java/org/traccar/protocol/TranSyncProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class TranSyncProtocol extends BaseProtocol { + + @Inject + public TranSyncProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0)); + pipeline.addLast(new TranSyncProtocolDecoder(TranSyncProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java b/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java new file mode 100644 index 000000000..816b5d2cf --- /dev/null +++ b/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java @@ -0,0 +1,166 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; + +public class TranSyncProtocolDecoder extends BaseProtocolDecoder { + + public TranSyncProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private String decodeAlarm(int value) { + switch (value) { + case 4: + return Position.ALARM_LOW_BATTERY; + case 6: + return Position.ALARM_POWER_RESTORED; + case 10: + return Position.ALARM_SOS; + case 13: + return Position.ALARM_BRAKING; + case 14: + return Position.ALARM_ACCELERATION; + case 17: + return Position.ALARM_OVERSPEED; + case 23: + return Position.ALARM_ACCIDENT; + default: + return null; + } + } + + @Override + protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.readUnsignedShort(); // header + buf.readByte(); // length + + int lac = buf.readUnsignedShort(); + + String deviceId = ByteBufUtil.hexDump(buf.readSlice(8)); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + buf.readUnsignedShort(); // index + buf.readUnsignedByte(); // type + + position.setTime(new DateBuilder() + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .getDate()); + + double latitude = buf.readUnsignedInt() / 1800000.0; + double longitude = buf.readUnsignedInt() / 1800000.0; + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + position.setCourse(buf.readUnsignedShort()); + + int mnc = buf.readUnsignedByte(); + int cid = buf.readUnsignedShort(); + int status0 = buf.readUnsignedByte(); + + position.setValid(BitUtil.check(status0, 0)); + position.setLatitude(BitUtil.check(status0, 1) ? latitude : -latitude); + position.setLongitude(BitUtil.check(status0, 2) ? longitude : -longitude); + + position.set(Position.PREFIX_OUT + 1, BitUtil.check(status0, 7)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(status0, 6)); + position.set(Position.PREFIX_IN + 3, BitUtil.check(status0, 5)); + if (BitUtil.check(status0, 4)) { + position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF); + } + position.set(Position.KEY_IGNITION, BitUtil.check(status0, 3)); + + buf.readUnsignedByte(); // reserved + + int event = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + + int status3 = buf.readUnsignedByte(); + if (BitUtil.check(status3, 7)) { + position.set(Position.KEY_ARCHIVE, true); + } + if (BitUtil.check(status3, 5)) { + position.set(Position.KEY_ALARM, Position.ALARM_GPS_ANTENNA_CUT); + } + + int rssi = buf.readUnsignedByte(); + CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid); + cellTower.setMobileNetworkCode(mnc); + cellTower.setSignalStrength(rssi); + position.setNetwork(new Network(cellTower)); + + position.set(Position.KEY_BATTERY, (double) (buf.readUnsignedByte() / 10)); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_HDOP, buf.readUnsignedByte()); + position.set(Position.PREFIX_ADC + 1, (short) buf.readUnsignedShort()); + + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // odometer id + int length = buf.readUnsignedByte(); + if (length > 0) { + position.set(Position.KEY_ODOMETER, buf.readBytes(length).readInt()); + } + } + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // rfid id + int length = buf.readUnsignedByte(); + if (length > 0) { + position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(length))); + } + } + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // adc2 id + int length = buf.readUnsignedByte(); + if (length > 0) { + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort()); + } + } + if (buf.readableBytes() > 5) { + buf.readUnsignedByte(); // adc3 id + int length = buf.readUnsignedByte(); + if (length > 0 && length <= buf.readableBytes() - 2) { + position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShort()); + } + } + + return position; + } + +} diff --git a/src/main/java/org/traccar/protocol/TrvProtocol.java b/src/main/java/org/traccar/protocol/TrvProtocol.java index e67afbda2..7bdf3d2d0 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocol.java +++ b/src/main/java/org/traccar/protocol/TrvProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TrvProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java index 9df29ae1b..02744f8ab 100644 --- a/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TrvProtocolDecoder.java @@ -64,10 +64,20 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { .number("(d+),") // mnc .number("(d+),") // lac .number("(d+)") // cell + .groupBegin() + .text(",") + .expression("(") + .groupBegin() + .expression("[^\\|]+") // name + .number("|xx-xx-xx-xx-xx-xx") // mac + .number("|d+&?") // signal + .groupEnd("+") + .expression(")") + .groupEnd("?") .any() .compile(); - private static final Pattern PATTERN_HEATRBEAT = new PatternBuilder() + private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder() .expression("[A-Z]{2,3}") .text("CP01,") .number("(ddd)") // gsm @@ -130,6 +140,16 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { } } + private void decodeWifi(Network network, String data) { + for (String wifi : data.split("&")) { + if (!wifi.isEmpty()) { + String[] values = wifi.split("\\|"); + network.addWifiAccessPoint(WifiAccessPoint.from( + values[1].replace('-', ':'), Integer.parseInt(values[2]))); + } + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -163,7 +183,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { if (type.equals("CP01")) { - Parser parser = new Parser(PATTERN_HEATRBEAT, sentence); + Parser parser = new Parser(PATTERN_HEARTBEAT, sentence); if (!parser.matches()) { return null; } @@ -208,8 +228,16 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { decodeCommon(position, parser); - position.setNetwork(new Network(CellTower.from( - parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()))); + Network network = new Network(); + + network.addCellTower(CellTower.from( + parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt())); + + if (parser.hasNext()) { + decodeWifi(network, parser.next()); + } + + position.setNetwork(network); return position; @@ -241,12 +269,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder { } } - for (String wifi : parser.next().split("&")) { - if (!wifi.isEmpty()) { - String[] values = wifi.split("\\|"); - network.addWifiAccessPoint(WifiAccessPoint.from(values[1], Integer.parseInt(values[2]))); - } - } + decodeWifi(network, parser.next()); position.setNetwork(network); diff --git a/src/main/java/org/traccar/protocol/Tt8850Protocol.java b/src/main/java/org/traccar/protocol/Tt8850Protocol.java index ab109e274..8e2800d90 100644 --- a/src/main/java/org/traccar/protocol/Tt8850Protocol.java +++ b/src/main/java/org/traccar/protocol/Tt8850Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Tt8850Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TytanProtocol.java b/src/main/java/org/traccar/protocol/TytanProtocol.java index cc3bc9b52..4fd3c807f 100644 --- a/src/main/java/org/traccar/protocol/TytanProtocol.java +++ b/src/main/java/org/traccar/protocol/TytanProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TytanProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TzoneProtocol.java b/src/main/java/org/traccar/protocol/TzoneProtocol.java index d25757b63..2df721049 100644 --- a/src/main/java/org/traccar/protocol/TzoneProtocol.java +++ b/src/main/java/org/traccar/protocol/TzoneProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class TzoneProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java index 8e84a6781..f0b1e709d 100644 --- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java @@ -204,30 +204,39 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { } - private void decodeTags(Position position, ByteBuf buf) { + private void decodeTags(Position position, ByteBuf buf, int hardware) { int blockLength = buf.readUnsignedShort(); int blockEnd = buf.readerIndex() + blockLength; if (blockLength > 0) { - buf.readUnsignedByte(); // tag type + int type = buf.readUnsignedByte(); - int count = buf.readUnsignedByte(); - int tagLength = buf.readUnsignedByte(); + if (hardware != 0x153 || type >= 2) { - for (int i = 1; i <= count; i++) { - int tagEnd = buf.readerIndex() + tagLength; + int count = buf.readUnsignedByte(); + int tagLength = buf.readUnsignedByte(); + + for (int i = 1; i <= count; i++) { + int tagEnd = buf.readerIndex() + tagLength; + + buf.readUnsignedByte(); // status + buf.readUnsignedShortLE(); // battery voltage - buf.readUnsignedByte(); // status - buf.readUnsignedShortLE(); // battery voltage + position.set(Position.PREFIX_TEMP + i, (buf.readShortLE() & 0x3fff) * 0.1); + + buf.readUnsignedByte(); // humidity + buf.readUnsignedByte(); // rssi + + buf.readerIndex(tagEnd); + } - position.set(Position.PREFIX_TEMP + i, (buf.readShortLE() & 0x3fff) * 0.1); + } else if (type == 1) { - buf.readUnsignedByte(); // humidity - buf.readUnsignedByte(); // rssi + position.set(Position.KEY_CARD, buf.readCharSequence( + blockEnd - buf.readerIndex(), StandardCharsets.UTF_8).toString()); - buf.readerIndex(tagEnd); } } @@ -364,9 +373,9 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder { } - if (hardware == 0x406) { + if (hardware == 0x153 || hardware == 0x406) { - decodeTags(position, buf); + decodeTags(position, buf, hardware); } diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocol.java b/src/main/java/org/traccar/protocol/UlbotechProtocol.java index 57fc47644..f8c4f1960 100644 --- a/src/main/java/org/traccar/protocol/UlbotechProtocol.java +++ b/src/main/java/org/traccar/protocol/UlbotechProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class UlbotechProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/UproProtocol.java b/src/main/java/org/traccar/protocol/UproProtocol.java index e27088594..cbec9777d 100644 --- a/src/main/java/org/traccar/protocol/UproProtocol.java +++ b/src/main/java/org/traccar/protocol/UproProtocol.java @@ -22,7 +22,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class UproProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java index ed714e464..8d2e5de0a 100644 --- a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java @@ -310,6 +310,10 @@ public class UproProtocolDecoder extends BaseProtocolDecoder { position.set("serial", data.toString(StandardCharsets.US_ASCII).substring(3)); } break; + case 'd': + position.set(Position.PREFIX_ADC + 1, + Integer.parseInt(data.toString(StandardCharsets.US_ASCII)) / 100.0); + break; default: break; } diff --git a/src/main/java/org/traccar/protocol/UuxProtocol.java b/src/main/java/org/traccar/protocol/UuxProtocol.java index 3de4a4732..63727cb94 100644 --- a/src/main/java/org/traccar/protocol/UuxProtocol.java +++ b/src/main/java/org/traccar/protocol/UuxProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class UuxProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/V680Protocol.java b/src/main/java/org/traccar/protocol/V680Protocol.java index 53bca849c..587a0c8f7 100644 --- a/src/main/java/org/traccar/protocol/V680Protocol.java +++ b/src/main/java/org/traccar/protocol/V680Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class V680Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/ValtrackProtocol.java b/src/main/java/org/traccar/protocol/ValtrackProtocol.java new file mode 100644 index 000000000..8471a0fd8 --- /dev/null +++ b/src/main/java/org/traccar/protocol/ValtrackProtocol.java @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import jakarta.inject.Inject; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +public class ValtrackProtocol extends BaseProtocol { + + @Inject + public ValtrackProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(16384)); + pipeline.addLast(new ValtrackProtocolDecoder(ValtrackProtocol.this)); + } + }); + } +} diff --git a/src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java new file mode 100644 index 000000000..dc9ca8590 --- /dev/null +++ b/src/main/java/org/traccar/protocol/ValtrackProtocolDecoder.java @@ -0,0 +1,84 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.io.StringReader; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class ValtrackProtocolDecoder extends BaseHttpProtocolDecoder { + + public ValtrackProtocolDecoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + String content = request.content().toString(StandardCharsets.UTF_8); + JsonObject object = Json.createReader(new StringReader(content)).readObject(); + JsonArray messages = object.getJsonArray("resource"); + + List<Position> positions = new LinkedList<>(); + for (int i = 0; i < messages.size(); i++) { + + JsonObject message = messages.getJsonObject(i); + String id = message.getString("devid"); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); + if (deviceSession == null) { + continue; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setValid(true); + position.setTime(new Date()); + position.setLatitude(Double.parseDouble(message.getString("lat"))); + position.setLongitude(Double.parseDouble(message.getString("lon"))); + String speed = message.getString("speed"); + if (!speed.isEmpty()) { + position.setSpeed(Double.parseDouble(speed)); + } + + position.set(Position.KEY_BATTERY, Double.parseDouble(message.getString("vbat"))); + + positions.add(position); + + } + + sendResponse(channel, HttpResponseStatus.OK); + return positions; + } + +} diff --git a/src/main/java/org/traccar/protocol/VisiontekProtocol.java b/src/main/java/org/traccar/protocol/VisiontekProtocol.java index 5296402b4..83bcd37ff 100644 --- a/src/main/java/org/traccar/protocol/VisiontekProtocol.java +++ b/src/main/java/org/traccar/protocol/VisiontekProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class VisiontekProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/VltProtocol.java b/src/main/java/org/traccar/protocol/VltProtocol.java new file mode 100644 index 000000000..005cd8ffb --- /dev/null +++ b/src/main/java/org/traccar/protocol/VltProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import jakarta.inject.Inject; + +public class VltProtocol extends BaseProtocol { + + @Inject + public VltProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new HttpResponseEncoder()); + pipeline.addLast(new HttpRequestDecoder()); + pipeline.addLast(new HttpObjectAggregator(65535)); + pipeline.addLast(new VltProtocolDecoder(VltProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/VltProtocolDecoder.java b/src/main/java/org/traccar/protocol/VltProtocolDecoder.java new file mode 100644 index 000000000..01c0563f5 --- /dev/null +++ b/src/main/java/org/traccar/protocol/VltProtocolDecoder.java @@ -0,0 +1,139 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.QueryStringDecoder; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.model.CellTower; +import org.traccar.model.Network; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +public class VltProtocolDecoder extends BaseHttpProtocolDecoder { + + public VltProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .number("(dd)") // alert id + .expression("([HL])") // history + .number("([01])") // validity + .number("(dd)(dd)(dd)") // date (ddmmyy) + .number("(dd)(dd)(dd)") // time (hhmmss) + .number("(d{3}.d{6})([NS])") // latitude + .number("(d{3}.d{6})([EW])") // longitude + .number("(d{3})") // mcc + .expression("(x*[0-9]+)") // mnc + .number("(x{4})") // lac + .number("(d{9})") // cid + .number("(d{3}.d{2})") // speed + .number("(d{3}.d{2})") // course + .number("(d{2})") // satellites + .number("(d{2})") // hdop + .number("(d{2})") // rssi + .number("([01])") // ignition + .number("([01])") // charging + .expression("([HMS])") // vehicle mode + .compile(); + + private Position decodePosition(DeviceSession deviceSession, String sentence) { + + Parser parser = new Parser(PATTERN, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.set(Position.KEY_EVENT, parser.nextInt()); + position.set(Position.KEY_ARCHIVE, parser.next().equals("H") ? true : null); + + position.setValid(parser.nextInt() > 0); + position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + + int mcc = parser.nextInt(); + int mnc = Integer.parseInt(parser.next().replaceAll("x", "")); + int lac = parser.nextHexInt(); + int cid = parser.nextInt(); + + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + position.set(Position.KEY_SATELLITES, parser.nextInt()); + position.set(Position.KEY_HDOP, parser.nextInt()); + + position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid, parser.nextInt()))); + + position.set(Position.KEY_IGNITION, parser.nextInt() > 0); + position.set(Position.KEY_CHARGE, parser.nextInt() > 0); + position.set(Position.KEY_MOTION, parser.next().equals("M")); + + return position; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + FullHttpRequest request = (FullHttpRequest) msg; + QueryStringDecoder decoder = new QueryStringDecoder( + request.content().toString(StandardCharsets.US_ASCII), false); + String sentence = decoder.parameters().get("vltdata").iterator().next(); + + int index = 0; + String type = sentence.substring(index, index += 3); + String imei = sentence.substring(index, index += 15); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + sendResponse(channel, HttpResponseStatus.OK); + + switch (type) { + case "NRM": + return decodePosition(deviceSession, sentence.substring(3 + 15)); + case "BTH": + List<Position> positions = new LinkedList<>(); + int count = Integer.parseInt(sentence.substring(index, index += 3)); + for (int i = 0; i < count; i++) { + positions.add(decodePosition(deviceSession, sentence.substring(index, index += 78))); + } + return positions; + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/VnetProtocol.java b/src/main/java/org/traccar/protocol/VnetProtocol.java index dd739f0d9..6ccc54483 100644 --- a/src/main/java/org/traccar/protocol/VnetProtocol.java +++ b/src/main/java/org/traccar/protocol/VnetProtocol.java @@ -23,7 +23,7 @@ import org.traccar.config.Config; import java.nio.ByteOrder; -import javax.inject.Inject; +import jakarta.inject.Inject; public class VnetProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Vt200Protocol.java b/src/main/java/org/traccar/protocol/Vt200Protocol.java index efb5fe2fd..97e64b74f 100644 --- a/src/main/java/org/traccar/protocol/Vt200Protocol.java +++ b/src/main/java/org/traccar/protocol/Vt200Protocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Vt200Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java index a8fc801e7..1ad15f39c 100644 --- a/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Vt200ProtocolDecoder.java @@ -123,7 +123,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder { position.set("tripStart", decodeDate(buf).getTime()); position.set("tripEnd", decodeDate(buf).getTime()); - position.set("drivingTime", buf.readUnsignedShort()); + position.set(Position.KEY_DRIVING_TIME, buf.readUnsignedShort()); position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt()); position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt()); diff --git a/src/main/java/org/traccar/protocol/VtfmsProtocol.java b/src/main/java/org/traccar/protocol/VtfmsProtocol.java index 482ab4a37..91453c413 100644 --- a/src/main/java/org/traccar/protocol/VtfmsProtocol.java +++ b/src/main/java/org/traccar/protocol/VtfmsProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class VtfmsProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/WatchFrameDecoder.java b/src/main/java/org/traccar/protocol/WatchFrameDecoder.java index f99bd52e2..9dfae8726 100644 --- a/src/main/java/org/traccar/protocol/WatchFrameDecoder.java +++ b/src/main/java/org/traccar/protocol/WatchFrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,26 @@ public class WatchFrameDecoder extends BaseFrameDecoder { protected Object decode( ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { - int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ']') + 1; + int brackets = 0; + int endIndex = -1; + for (int i = buf.readerIndex(); i < buf.writerIndex(); i++) { + byte b = buf.getByte(i); + switch (b) { + case '[': + brackets += 1; + break; + case ']': + brackets -= 1; + break; + default: + break; + } + if (brackets == 0 && i > buf.readerIndex()) { + endIndex = i + 1; + break; + } + } + if (endIndex > 0) { ByteBuf frame = Unpooled.buffer(); while (buf.readerIndex() < endIndex) { diff --git a/src/main/java/org/traccar/protocol/WatchProtocol.java b/src/main/java/org/traccar/protocol/WatchProtocol.java index 600f81328..aee70b6ec 100644 --- a/src/main/java/org/traccar/protocol/WatchProtocol.java +++ b/src/main/java/org/traccar/protocol/WatchProtocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class WatchProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java index 40d56b130..b586f4e92 100644 --- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java @@ -51,7 +51,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { .number("(dd)(dd)(dd),") // time (hhmmss) .expression("([AV]),") // validity .number(" *(-?d+.d+),") // latitude - .expression("([NS]),") + .expression("([NS])?,") .number(" *(-?d+.d+),") // longitude .expression("([EW])?,") .number("(d+.?d*),") // speed @@ -285,7 +285,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { || type.equalsIgnoreCase("BLOOD") || type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("TEMP") - || type.equalsIgnoreCase("btemp2")) { + || type.equalsIgnoreCase("btemp2") + || type.equalsIgnoreCase("oxygen")) { if (buf.isReadable()) { @@ -303,6 +304,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder { if (Integer.parseInt(values[valueIndex++]) > 0) { position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(values[valueIndex])); } + } else if (type.equalsIgnoreCase("oxygen")) { + position.set("bloodOxygen", Integer.parseInt(values[++valueIndex])); } else { if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) { position.set("pressureHigh", values[valueIndex++]); diff --git a/src/main/java/org/traccar/protocol/WialonProtocol.java b/src/main/java/org/traccar/protocol/WialonProtocol.java index a744349cd..84033132d 100644 --- a/src/main/java/org/traccar/protocol/WialonProtocol.java +++ b/src/main/java/org/traccar/protocol/WialonProtocol.java @@ -27,7 +27,7 @@ import org.traccar.model.Command; import java.nio.charset.StandardCharsets; -import javax.inject.Inject; +import jakarta.inject.Inject; public class WialonProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java index 3d57525b7..4d1b34dba 100644 --- a/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/WialonProtocolDecoder.java @@ -63,8 +63,9 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { .number("(?:NA|(d+));") // outputs .expression("(?:NA|([^;]*));") // adc .expression("(?:NA|([^;]*));") // ibutton - .expression("(?:NA|(.*))") // params + .expression("(?:NA|([^;]*))") // params .groupEnd("?") + .any() .compile(); private void sendResponse(Channel channel, SocketAddress remoteAddress, String type, Integer number) { @@ -101,7 +102,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { position.setTime(new Date()); } - if (parser.hasNext(9)) { + if (parser.hasNextAny(9)) { position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); @@ -135,10 +136,22 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder { for (String param : values) { Matcher paramParser = Pattern.compile("(.*):[1-3]:(.*)").matcher(param); if (paramParser.matches()) { + String key = paramParser.group(1).toLowerCase(); + String value = paramParser.group(2); try { - position.set(paramParser.group(1).toLowerCase(), Double.parseDouble(paramParser.group(2))); + if (key.equals("accuracy")) { + position.setAccuracy(Double.parseDouble(value)); + } else { + position.set(key, Double.parseDouble(value)); + } } catch (NumberFormatException e) { - position.set(paramParser.group(1).toLowerCase(), paramParser.group(2)); + if (value.equalsIgnoreCase("true")) { + position.set(key, true); + } else if (value.equalsIgnoreCase("false")) { + position.set(key, false); + } else { + position.set(key, value); + } } } } diff --git a/src/main/java/org/traccar/protocol/WliProtocol.java b/src/main/java/org/traccar/protocol/WliProtocol.java index f7084e55b..5b9ebb520 100644 --- a/src/main/java/org/traccar/protocol/WliProtocol.java +++ b/src/main/java/org/traccar/protocol/WliProtocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class WliProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/WondexProtocol.java b/src/main/java/org/traccar/protocol/WondexProtocol.java index 5a0401df4..e27b8e2bb 100644 --- a/src/main/java/org/traccar/protocol/WondexProtocol.java +++ b/src/main/java/org/traccar/protocol/WondexProtocol.java @@ -22,7 +22,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class WondexProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/WristbandProtocol.java b/src/main/java/org/traccar/protocol/WristbandProtocol.java index c5d8d4050..117daf8cf 100644 --- a/src/main/java/org/traccar/protocol/WristbandProtocol.java +++ b/src/main/java/org/traccar/protocol/WristbandProtocol.java @@ -21,7 +21,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class WristbandProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Xexun2Protocol.java b/src/main/java/org/traccar/protocol/Xexun2Protocol.java index 52cf731f0..9dd517cfa 100644 --- a/src/main/java/org/traccar/protocol/Xexun2Protocol.java +++ b/src/main/java/org/traccar/protocol/Xexun2Protocol.java @@ -21,7 +21,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Xexun2Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java index 913dfaf28..0e3c44e12 100644 --- a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java @@ -156,7 +156,7 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < wifiCount; j++) { String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:"); network.addWifiAccessPoint(WifiAccessPoint.from( - mac.substring(0, mac.length() - 1), buf.readUnsignedByte())); + mac.substring(0, mac.length() - 1), buf.readByte())); } } if (BitUtil.check(positionMask, 2)) { @@ -164,7 +164,7 @@ public class Xexun2ProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < cellCount; j++) { network.addCellTower(CellTower.from( buf.readUnsignedShort(), buf.readUnsignedShort(), - buf.readInt(), buf.readUnsignedInt(), buf.readUnsignedByte())); + buf.readInt(), buf.readUnsignedInt(), buf.readByte())); } } if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) { diff --git a/src/main/java/org/traccar/protocol/XexunProtocol.java b/src/main/java/org/traccar/protocol/XexunProtocol.java index 5c7329603..e76e47d19 100644 --- a/src/main/java/org/traccar/protocol/XexunProtocol.java +++ b/src/main/java/org/traccar/protocol/XexunProtocol.java @@ -25,7 +25,7 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class XexunProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/XirgoProtocol.java b/src/main/java/org/traccar/protocol/XirgoProtocol.java index 0841d86d5..7e14c6842 100644 --- a/src/main/java/org/traccar/protocol/XirgoProtocol.java +++ b/src/main/java/org/traccar/protocol/XirgoProtocol.java @@ -24,7 +24,7 @@ import org.traccar.TrackerServer; import org.traccar.config.Config; import org.traccar.model.Command; -import javax.inject.Inject; +import jakarta.inject.Inject; public class XirgoProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Xrb28Protocol.java b/src/main/java/org/traccar/protocol/Xrb28Protocol.java index 65c2a1230..135fb0928 100644 --- a/src/main/java/org/traccar/protocol/Xrb28Protocol.java +++ b/src/main/java/org/traccar/protocol/Xrb28Protocol.java @@ -26,7 +26,7 @@ import org.traccar.model.Command; import java.nio.charset.StandardCharsets; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Xrb28Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java index 88f2d07e5..6033293c4 100644 --- a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import org.traccar.model.Command; import org.traccar.model.Position; import java.net.SocketAddress; +import java.util.Arrays; import java.util.regex.Pattern; public class Xrb28ProtocolDecoder extends BaseProtocolDecoder { @@ -46,6 +47,7 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder { .expression("....,") .expression("..,") // vendor .number("d{15},") // imei + .number("d{12},").optional() // time .expression("..,") // type .number("[01],") // reserved .number("(dd)(dd)(dd).d+,") // time (hhmmss) @@ -67,23 +69,44 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder { Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; + String[] values = sentence.replaceAll("#$", "").split(","); - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, sentence.substring(9, 24)); + int index = 0; + String header = values[index++]; + String vendor = values[index++]; + + String imei = values[index++]; + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); if (deviceSession == null) { return null; } - String type = sentence.substring(25, 27); + String time; + if (values[index].length() == 12) { + time = values[index++]; + } else { + time = null; + } + + String type = values[index++]; if (channel != null) { + StringBuilder response = new StringBuilder("\u00ff\u00ff"); + response.append(header.replaceAll("R$", "S")).append(','); + response.append(vendor).append(','); + response.append(imei).append(','); + if (time != null) { + response.append(time).append(','); + } if (type.matches("L0|L1|W0|E1")) { - channel.write(new NetworkMessage( - "\u00ff\u00ff*SCOS" + sentence.substring(5, 27) + "#\n", - remoteAddress)); + response.append(type).append("#\n"); + channel.write(new NetworkMessage(response.toString(), remoteAddress)); } else if (type.equals("R0") && pendingCommand != null) { - String command = pendingCommand.equals(Command.TYPE_ALARM_ARM) ? "L1," : "L0,"; - channel.write(new NetworkMessage( - "\u00ff\u00ff*SCOS" + sentence.substring(5, 25) + command + sentence.substring(30) + "\n", - remoteAddress)); + String command = pendingCommand.equals(Command.TYPE_ALARM_ARM) ? "L1" : "L0"; + response.append(command); + String[] remaining = Arrays.copyOfRange(values, index, values.length); + response.append(String.join(",", remaining)); + response.append("#\n"); + channel.write(new NetworkMessage(response.toString(), remoteAddress)); pendingCommand = null; } } @@ -95,11 +118,6 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder { getLastLocation(position, null); - String payload = sentence.substring(25, sentence.length() - 1); - - int index = 0; - String[] values = payload.substring(3).split(","); - switch (type) { case "Q0": position.set(Position.KEY_BATTERY, Integer.parseInt(values[index++]) * 0.01); @@ -146,7 +164,8 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder { case "K0": case "I0": case "M0": - position.set(Position.KEY_RESULT, payload); + String[] remaining = Arrays.copyOfRange(values, index, values.length); + position.set(Position.KEY_RESULT, String.join(",", remaining)); break; default: break; diff --git a/src/main/java/org/traccar/protocol/Xt013Protocol.java b/src/main/java/org/traccar/protocol/Xt013Protocol.java index 9e9087609..25809463a 100644 --- a/src/main/java/org/traccar/protocol/Xt013Protocol.java +++ b/src/main/java/org/traccar/protocol/Xt013Protocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Xt013Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Xt2400Protocol.java b/src/main/java/org/traccar/protocol/Xt2400Protocol.java index e200adb9f..1b7fc840b 100644 --- a/src/main/java/org/traccar/protocol/Xt2400Protocol.java +++ b/src/main/java/org/traccar/protocol/Xt2400Protocol.java @@ -20,7 +20,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class Xt2400Protocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java index edcb3f535..11f9e0654 100644 --- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java @@ -177,7 +177,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1); break; case 0x57: - position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(buf.readUnsignedShort())); + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort()); break; case 0x65: position.set(Position.KEY_VIN, buf.readSlice(17).toString(StandardCharsets.US_ASCII)); diff --git a/src/main/java/org/traccar/protocol/YwtProtocol.java b/src/main/java/org/traccar/protocol/YwtProtocol.java index fb44e2360..27c71cfa8 100644 --- a/src/main/java/org/traccar/protocol/YwtProtocol.java +++ b/src/main/java/org/traccar/protocol/YwtProtocol.java @@ -23,7 +23,7 @@ import org.traccar.PipelineBuilder; import org.traccar.TrackerServer; import org.traccar.config.Config; -import javax.inject.Inject; +import jakarta.inject.Inject; public class YwtProtocol extends BaseProtocol { diff --git a/src/main/java/org/traccar/reports/CombinedReportProvider.java b/src/main/java/org/traccar/reports/CombinedReportProvider.java new file mode 100644 index 000000000..bad3a61b3 --- /dev/null +++ b/src/main/java/org/traccar/reports/CombinedReportProvider.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports; + +import org.traccar.helper.model.DeviceUtil; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.CombinedReportItem; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; + +import jakarta.inject.Inject; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Set; +import java.util.stream.Collectors; + +public class CombinedReportProvider { + + private static final Set<String> EXCLUDE_TYPES = Set.of(Event.TYPE_DEVICE_MOVING); + + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public CombinedReportProvider(ReportUtils reportUtils, Storage storage) { + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection<CombinedReportItem> getObjects( + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws StorageException { + reportUtils.checkPeriodLimit(from, to); + + ArrayList<CombinedReportItem> result = new ArrayList<>(); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + CombinedReportItem item = new CombinedReportItem(); + item.setDeviceId(device.getId()); + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); + item.setRoute(positions.stream() + .map(p -> new double[] {p.getLongitude(), p.getLatitude()}) + .collect(Collectors.toList())); + var events = storage.getObjects(Event.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", device.getId()), + new Condition.Between("eventTime", "from", from, "to", to)), + new Order("eventTime"))); + item.setEvents(events.stream() + .filter(e -> e.getPositionId() > 0 && !EXCLUDE_TYPES.contains(e.getType())) + .collect(Collectors.toList())); + var eventPositions = events.stream() + .map(Event::getPositionId) + .collect(Collectors.toSet()); + item.setPositions(positions.stream() + .filter(p -> eventPositions.contains(p.getId())) + .collect(Collectors.toList())); + result.add(item); + } + return result; + } +} diff --git a/src/main/java/org/traccar/reports/CsvExportProvider.java b/src/main/java/org/traccar/reports/CsvExportProvider.java index df55c470e..521dc120a 100644 --- a/src/main/java/org/traccar/reports/CsvExportProvider.java +++ b/src/main/java/org/traccar/reports/CsvExportProvider.java @@ -21,7 +21,7 @@ import org.traccar.model.Position; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Date; diff --git a/src/main/java/org/traccar/reports/DevicesReportProvider.java b/src/main/java/org/traccar/reports/DevicesReportProvider.java new file mode 100644 index 000000000..7b4294d53 --- /dev/null +++ b/src/main/java/org/traccar/reports/DevicesReportProvider.java @@ -0,0 +1,78 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports; + +import jakarta.inject.Inject; +import org.jxls.util.JxlsHelper; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Message; +import org.traccar.model.User; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.DeviceReportItem; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.stream.Collectors; + +public class DevicesReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public DevicesReportProvider(Config config, ReportUtils reportUtils, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection<DeviceReportItem> getObjects(long userId) throws StorageException { + + var positions = PositionUtil.getLatestPositions(storage, userId).stream() + .collect(Collectors.toMap(Message::getDeviceId, p -> p)); + + return storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Device.class))).stream() + .map(device -> new DeviceReportItem(device, positions.get(device.getId()))) + .collect(Collectors.toUnmodifiableList()); + } + + public void getExcel(OutputStream outputStream, long userId) throws StorageException, IOException { + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "devices.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("items", getObjects(userId)); + JxlsHelper.getInstance().setUseFastFormulaProcessor(false) + .processTemplate(inputStream, outputStream, context); + } + } +} diff --git a/src/main/java/org/traccar/reports/EventsReportProvider.java b/src/main/java/org/traccar/reports/EventsReportProvider.java index 30f55ba80..f252f28cc 100644 --- a/src/main/java/org/traccar/reports/EventsReportProvider.java +++ b/src/main/java/org/traccar/reports/EventsReportProvider.java @@ -19,6 +19,7 @@ package org.traccar.reports; import org.apache.poi.ss.util.WorkbookUtil; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.helper.model.DeviceUtil; import org.traccar.model.Device; import org.traccar.model.Event; import org.traccar.model.Geofence; @@ -34,7 +35,7 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -76,7 +77,7 @@ public class EventsReportProvider { reportUtils.checkPeriodLimit(from, to); ArrayList<Event> result = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { Collection<Event> events = getEvents(device.getId(), from, to); boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS); for (Event event : events) { @@ -104,7 +105,7 @@ public class EventsReportProvider { HashMap<Long, String> geofenceNames = new HashMap<>(); HashMap<Long, String> maintenanceNames = new HashMap<>(); HashMap<Long, Position> positions = new HashMap<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { Collection<Event> events = getEvents(device.getId(), from, to); boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS); for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) { diff --git a/src/main/java/org/traccar/reports/GpxExportProvider.java b/src/main/java/org/traccar/reports/GpxExportProvider.java index ccbd97fc3..1c45b6416 100644 --- a/src/main/java/org/traccar/reports/GpxExportProvider.java +++ b/src/main/java/org/traccar/reports/GpxExportProvider.java @@ -24,7 +24,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Date; diff --git a/src/main/java/org/traccar/reports/KmlExportProvider.java b/src/main/java/org/traccar/reports/KmlExportProvider.java index 24fcfb8ab..24dca018c 100644 --- a/src/main/java/org/traccar/reports/KmlExportProvider.java +++ b/src/main/java/org/traccar/reports/KmlExportProvider.java @@ -23,7 +23,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.OutputStream; import java.io.PrintWriter; import java.text.SimpleDateFormat; diff --git a/src/main/java/org/traccar/reports/RouteReportProvider.java b/src/main/java/org/traccar/reports/RouteReportProvider.java index 3ee651619..d761fe1e5 100644 --- a/src/main/java/org/traccar/reports/RouteReportProvider.java +++ b/src/main/java/org/traccar/reports/RouteReportProvider.java @@ -19,6 +19,7 @@ package org.traccar.reports; import org.apache.poi.ss.util.WorkbookUtil; import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.helper.model.DeviceUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; import org.traccar.model.Group; @@ -31,7 +32,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -41,6 +42,8 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.Map; +import java.util.HashMap; public class RouteReportProvider { @@ -48,6 +51,8 @@ public class RouteReportProvider { private final ReportUtils reportUtils; private final Storage storage; + private final Map<String, Integer> namesCount = new HashMap<>(); + @Inject public RouteReportProvider(Config config, ReportUtils reportUtils, Storage storage) { this.config = config; @@ -60,12 +65,18 @@ public class RouteReportProvider { reportUtils.checkPeriodLimit(from, to); ArrayList<Position> result = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { result.addAll(PositionUtil.getPositions(storage, device.getId(), from, to)); } return result; } + + private String getUniqueSheetName(String key) { + namesCount.compute(key, (k, value) -> value == null ? 1 : (value + 1)); + return namesCount.get(key) > 1 ? key + '-' + namesCount.get(key) : key; + } + public void getExcel(OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds, Date from, Date to) throws StorageException, IOException { @@ -73,11 +84,11 @@ public class RouteReportProvider { ArrayList<DeviceReportSection> devicesRoutes = new ArrayList<>(); ArrayList<String> sheetNames = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { var positions = PositionUtil.getPositions(storage, device.getId(), from, to); DeviceReportSection deviceRoutes = new DeviceReportSection(); deviceRoutes.setDeviceName(device.getName()); - sheetNames.add(WorkbookUtil.createSafeSheetName(deviceRoutes.getDeviceName())); + sheetNames.add(WorkbookUtil.createSafeSheetName(getUniqueSheetName(deviceRoutes.getDeviceName()))); if (device.getGroupId() > 0) { Group group = storage.getObject(Group.class, new Request( new Columns.All(), new Condition.Equals("id", device.getGroupId()))); diff --git a/src/main/java/org/traccar/reports/StopsReportProvider.java b/src/main/java/org/traccar/reports/StopsReportProvider.java index ec3fd2215..2160fec0e 100644 --- a/src/main/java/org/traccar/reports/StopsReportProvider.java +++ b/src/main/java/org/traccar/reports/StopsReportProvider.java @@ -19,7 +19,7 @@ package org.traccar.reports; import org.apache.poi.ss.util.WorkbookUtil; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.helper.model.PositionUtil; +import org.traccar.helper.model.DeviceUtil; import org.traccar.model.Device; import org.traccar.model.Group; import org.traccar.reports.common.ReportUtils; @@ -31,7 +31,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -55,20 +55,14 @@ public class StopsReportProvider { this.storage = storage; } - private Collection<StopReportItem> detectStops(Device device, Date from, Date to) throws StorageException { - boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); - var positions = PositionUtil.getPositions(storage, device.getId(), from, to); - return reportUtils.detectTripsAndStops(device, positions, ignoreOdometer, StopReportItem.class); - } - public Collection<StopReportItem> getObjects( long userId, Collection<Long> deviceIds, Collection<Long> groupIds, Date from, Date to) throws StorageException { reportUtils.checkPeriodLimit(from, to); ArrayList<StopReportItem> result = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { - result.addAll(detectStops(device, from, to)); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + result.addAll(reportUtils.detectTripsAndStops(device, from, to, StopReportItem.class)); } return result; } @@ -80,8 +74,8 @@ public class StopsReportProvider { ArrayList<DeviceReportSection> devicesStops = new ArrayList<>(); ArrayList<String> sheetNames = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { - Collection<StopReportItem> stops = detectStops(device, from, to); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + Collection<StopReportItem> stops = reportUtils.detectTripsAndStops(device, from, to, StopReportItem.class); DeviceReportSection deviceStops = new DeviceReportSection(); deviceStops.setDeviceName(device.getName()); sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName())); diff --git a/src/main/java/org/traccar/reports/SummaryReportProvider.java b/src/main/java/org/traccar/reports/SummaryReportProvider.java index 9415f3e81..ffde0b067 100644 --- a/src/main/java/org/traccar/reports/SummaryReportProvider.java +++ b/src/main/java/org/traccar/reports/SummaryReportProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ import org.traccar.api.security.PermissionsService; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.DeviceUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.helper.model.UserUtil; import org.traccar.model.Device; @@ -29,18 +30,25 @@ import org.traccar.reports.common.ReportUtils; import org.traccar.reports.model.SummaryReportItem; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Order; +import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Paths; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collection; import java.util.Date; +import java.util.List; public class SummaryReportProvider { @@ -58,96 +66,105 @@ public class SummaryReportProvider { this.storage = storage; } - private SummaryReportItem calculateSummaryResult(Device device, Collection<Position> positions) { + private Position getEdgePosition(long deviceId, Date from, Date to, boolean end) throws StorageException { + return storage.getObject(Position.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", deviceId), + new Condition.Between("fixTime", "from", from, "to", to)), + new Order("fixTime", end, 1))); + } + + private Collection<SummaryReportItem> calculateDeviceResult( + Device device, Date from, Date to, boolean fast) throws StorageException { + SummaryReportItem result = new SummaryReportItem(); result.setDeviceId(device.getId()); result.setDeviceName(device.getName()); - if (positions != null && !positions.isEmpty()) { - Position firstPosition = null; - Position previousPosition = null; + + Position first = null; + Position last = null; + if (fast) { + first = getEdgePosition(device.getId(), from, to, false); + last = getEdgePosition(device.getId(), from, to, true); + } else { + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); for (Position position : positions) { - if (firstPosition == null) { - firstPosition = position; + if (first == null) { + first = position; } - previousPosition = position; if (position.getSpeed() > result.getMaxSpeed()) { result.setMaxSpeed(position.getSpeed()); } + last = position; } + } + + if (first != null && last != null) { boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); - result.setDistance(PositionUtil.calculateDistance(firstPosition, previousPosition, !ignoreOdometer)); - result.setSpentFuel(reportUtils.calculateFuel(firstPosition, previousPosition)); + result.setDistance(PositionUtil.calculateDistance(first, last, !ignoreOdometer)); + result.setSpentFuel(reportUtils.calculateFuel(first, last)); long durationMilliseconds; - if (firstPosition.hasAttribute(Position.KEY_HOURS) && previousPosition.hasAttribute(Position.KEY_HOURS)) { - durationMilliseconds = - previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS); + if (first.hasAttribute(Position.KEY_HOURS) && last.hasAttribute(Position.KEY_HOURS)) { + durationMilliseconds = last.getLong(Position.KEY_HOURS) - first.getLong(Position.KEY_HOURS); result.setEngineHours(durationMilliseconds); } else { - durationMilliseconds = - previousPosition.getFixTime().getTime() - firstPosition.getFixTime().getTime(); + durationMilliseconds = last.getFixTime().getTime() - first.getFixTime().getTime(); } if (durationMilliseconds > 0) { - result.setAverageSpeed( - UnitsConverter.knotsFromMps(result.getDistance() * 1000 / durationMilliseconds)); + result.setAverageSpeed(UnitsConverter.knotsFromMps(result.getDistance() * 1000 / durationMilliseconds)); } if (!ignoreOdometer - && firstPosition.getDouble(Position.KEY_ODOMETER) != 0 - && previousPosition.getDouble(Position.KEY_ODOMETER) != 0) { - result.setStartOdometer(firstPosition.getDouble(Position.KEY_ODOMETER)); - result.setEndOdometer(previousPosition.getDouble(Position.KEY_ODOMETER)); + && first.getDouble(Position.KEY_ODOMETER) != 0 && last.getDouble(Position.KEY_ODOMETER) != 0) { + result.setStartOdometer(first.getDouble(Position.KEY_ODOMETER)); + result.setEndOdometer(last.getDouble(Position.KEY_ODOMETER)); } else { - result.setStartOdometer(firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE)); - result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE)); + result.setStartOdometer(first.getDouble(Position.KEY_TOTAL_DISTANCE)); + result.setEndOdometer(last.getDouble(Position.KEY_TOTAL_DISTANCE)); } - result.setStartTime(firstPosition.getFixTime()); - result.setEndTime(previousPosition.getFixTime()); + result.setStartTime(first.getFixTime()); + result.setEndTime(last.getFixTime()); + return List.of(result); } - return result; - } - private int getDay(long userId, Date date) throws StorageException { - Calendar calendar = Calendar.getInstance(UserUtil.getTimezone( - permissionsService.getServer(), permissionsService.getUser(userId))); - calendar.setTime(date); - return calendar.get(Calendar.DAY_OF_MONTH); + return List.of(); } - private Collection<SummaryReportItem> calculateSummaryResults( - long userId, Device device, Date from, Date to, boolean daily) throws StorageException { + private Collection<SummaryReportItem> calculateDeviceResults( + Device device, ZonedDateTime from, ZonedDateTime to, boolean daily) throws StorageException { - var positions = PositionUtil.getPositions(storage, device.getId(), from, to); + boolean fast = Duration.between(from, to).toSeconds() > config.getLong(Keys.REPORT_FAST_THRESHOLD); var results = new ArrayList<SummaryReportItem>(); - if (daily && !positions.isEmpty()) { - int startIndex = 0; - int startDay = getDay(userId, positions.iterator().next().getFixTime()); - for (int i = 0; i < positions.size(); i++) { - int currentDay = getDay(userId, positions.get(i).getFixTime()); - if (currentDay != startDay) { - results.add(calculateSummaryResult(device, positions.subList(startIndex, i))); - startIndex = i; - startDay = currentDay; - } + if (daily) { + while (from.truncatedTo(ChronoUnit.DAYS).isBefore(to.truncatedTo(ChronoUnit.DAYS))) { + ZonedDateTime fromDay = from.truncatedTo(ChronoUnit.DAYS); + ZonedDateTime nextDay = fromDay.plus(1, ChronoUnit.DAYS); + results.addAll(calculateDeviceResult( + device, Date.from(from.toInstant()), Date.from(nextDay.toInstant()), fast)); + from = nextDay; } - results.add(calculateSummaryResult(device, positions.subList(startIndex, positions.size()))); + results.addAll(calculateDeviceResult(device, Date.from(from.toInstant()), Date.from(to.toInstant()), fast)); } else { - results.add(calculateSummaryResult(device, positions)); + results.addAll(calculateDeviceResult(device, Date.from(from.toInstant()), Date.from(to.toInstant()), fast)); } - return results; } public Collection<SummaryReportItem> getObjects( - long userId, Collection<Long> deviceIds, - Collection<Long> groupIds, Date from, Date to, boolean daily) throws StorageException { + long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to, boolean daily) throws StorageException { reportUtils.checkPeriodLimit(from, to); + var tz = UserUtil.getTimezone(permissionsService.getServer(), permissionsService.getUser(userId)).toZoneId(); + ArrayList<SummaryReportItem> result = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { - Collection<SummaryReportItem> deviceResults = calculateSummaryResults(userId, device, from, to, daily); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + var deviceResults = calculateDeviceResults( + device, from.toInstant().atZone(tz), to.toInstant().atZone(tz), daily); for (SummaryReportItem summaryReport : deviceResults) { if (summaryReport.getStartTime() != null && summaryReport.getEndTime() != null) { result.add(summaryReport); diff --git a/src/main/java/org/traccar/reports/TripsReportProvider.java b/src/main/java/org/traccar/reports/TripsReportProvider.java index 265811354..9ff7232af 100644 --- a/src/main/java/org/traccar/reports/TripsReportProvider.java +++ b/src/main/java/org/traccar/reports/TripsReportProvider.java @@ -19,7 +19,7 @@ package org.traccar.reports; import org.apache.poi.ss.util.WorkbookUtil; import org.traccar.config.Config; import org.traccar.config.Keys; -import org.traccar.helper.model.PositionUtil; +import org.traccar.helper.model.DeviceUtil; import org.traccar.model.Device; import org.traccar.model.Group; import org.traccar.reports.common.ReportUtils; @@ -31,7 +31,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -55,20 +55,14 @@ public class TripsReportProvider { this.storage = storage; } - private Collection<TripReportItem> detectTrips(Device device, Date from, Date to) throws StorageException { - boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); - var positions = PositionUtil.getPositions(storage, device.getId(), from, to); - return reportUtils.detectTripsAndStops(device, positions, ignoreOdometer, TripReportItem.class); - } - public Collection<TripReportItem> getObjects( long userId, Collection<Long> deviceIds, Collection<Long> groupIds, Date from, Date to) throws StorageException { reportUtils.checkPeriodLimit(from, to); ArrayList<TripReportItem> result = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { - result.addAll(detectTrips(device, from, to)); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + result.addAll(reportUtils.detectTripsAndStops(device, from, to, TripReportItem.class)); } return result; } @@ -80,8 +74,8 @@ public class TripsReportProvider { ArrayList<DeviceReportSection> devicesTrips = new ArrayList<>(); ArrayList<String> sheetNames = new ArrayList<>(); - for (Device device: reportUtils.getAccessibleDevices(userId, deviceIds, groupIds)) { - Collection<TripReportItem> trips = detectTrips(device, from, to); + for (Device device: DeviceUtil.getAccessibleDevices(storage, userId, deviceIds, groupIds)) { + Collection<TripReportItem> trips = reportUtils.detectTripsAndStops(device, from, to, TripReportItem.class); DeviceReportSection deviceTrips = new DeviceReportSection(); deviceTrips.setDeviceName(device.getName()); sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName())); diff --git a/src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java b/src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java new file mode 100644 index 000000000..8b139a572 --- /dev/null +++ b/src/main/java/org/traccar/reports/common/ExpressionEvaluatorFactory.java @@ -0,0 +1,58 @@ +package org.traccar.reports.common; + +import org.apache.commons.jexl3.JexlBuilder; +import org.apache.commons.jexl3.introspection.JexlPermissions; +import org.jxls.expression.ExpressionEvaluator; +import org.jxls.expression.JexlExpressionEvaluator; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ExpressionEvaluatorFactory implements org.jxls.expression.ExpressionEvaluatorFactory { + + private final JexlPermissions permissions = new JexlPermissions() { + @Override + public boolean allow(Package pack) { + return true; + } + + @Override + public boolean allow(Class<?> clazz) { + return true; + } + + @Override + public boolean allow(Constructor<?> ctor) { + return true; + } + + @Override + public boolean allow(Method method) { + return true; + } + + @Override + public boolean allow(Field field) { + return true; + } + + @Override + public JexlPermissions compose(String... src) { + return this; + } + }; + + @Override + public ExpressionEvaluator createExpressionEvaluator(String expression) { + JexlExpressionEvaluator expressionEvaluator = expression == null + ? new JexlExpressionEvaluator() + : new JexlExpressionEvaluator(expression); + expressionEvaluator.setJexlEngine(new JexlBuilder() + .silent(true) + .strict(false) + .permissions(permissions) + .create()); + return expressionEvaluator; + } +} diff --git a/src/main/java/org/traccar/reports/common/ReportMailer.java b/src/main/java/org/traccar/reports/common/ReportMailer.java index 221b35ae1..9fb30fe9f 100644 --- a/src/main/java/org/traccar/reports/common/ReportMailer.java +++ b/src/main/java/org/traccar/reports/common/ReportMailer.java @@ -22,11 +22,11 @@ import org.traccar.mail.MailManager; import org.traccar.model.User; import org.traccar.storage.StorageException; -import javax.activation.DataHandler; -import javax.inject.Inject; -import javax.mail.MessagingException; -import javax.mail.internet.MimeBodyPart; -import javax.mail.util.ByteArrayDataSource; +import jakarta.activation.DataHandler; +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.util.ByteArrayDataSource; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -55,7 +55,7 @@ public class ReportMailer { stream.toByteArray(), "application/octet-stream"))); User user = permissionsService.getUser(userId); - mailManager.sendMessage(user, "Report", "The report is in the attachment.", attachment); + mailManager.sendMessage(user, false, "Report", "The report is in the attachment.", attachment); } catch (StorageException | IOException | MessagingException e) { LOGGER.warn("Email report failed", e); } diff --git a/src/main/java/org/traccar/reports/common/ReportUtils.java b/src/main/java/org/traccar/reports/common/ReportUtils.java index a7c420095..3b8e84887 100644 --- a/src/main/java/org/traccar/reports/common/ReportUtils.java +++ b/src/main/java/org/traccar/reports/common/ReportUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,8 @@ */ package org.traccar.reports.common; +import jakarta.annotation.Nullable; +import jakarta.inject.Inject; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.tools.generic.DateTool; import org.apache.velocity.tools.generic.NumberTool; @@ -31,12 +33,13 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.geocoder.Geocoder; import org.traccar.helper.UnitsConverter; +import org.traccar.helper.model.AttributeUtil; import org.traccar.helper.model.PositionUtil; import org.traccar.helper.model.UserUtil; import org.traccar.model.BaseModel; import org.traccar.model.Device; import org.traccar.model.Driver; -import org.traccar.model.Group; +import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.reports.model.BaseReportItem; @@ -48,44 +51,35 @@ import org.traccar.storage.Storage; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigDecimal; -import java.math.RoundingMode; +import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.Date; -import java.util.LinkedList; import java.util.List; import java.util.Locale; -import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; -@Singleton public class ReportUtils { private final Config config; private final Storage storage; private final PermissionsService permissionsService; - private final TripsConfig tripsConfig; private final VelocityEngine velocityEngine; private final Geocoder geocoder; @Inject public ReportUtils( Config config, Storage storage, PermissionsService permissionsService, - TripsConfig tripsConfig, VelocityEngine velocityEngine, @Nullable Geocoder geocoder) { + VelocityEngine velocityEngine, @Nullable Geocoder geocoder) { this.config = config; this.storage = storage; this.permissionsService = permissionsService; - this.tripsConfig = tripsConfig; this.velocityEngine = velocityEngine; this.geocoder = geocoder; } @@ -106,49 +100,11 @@ public class ReportUtils { } } - public Collection<Device> getAccessibleDevices( - long userId, Collection<Long> deviceIds, Collection<Long> groupIds) throws StorageException { - - var devices = storage.getObjects(Device.class, new Request( - new Columns.All(), - new Condition.Permission(User.class, userId, Device.class))); - var deviceById = devices.stream() - .collect(Collectors.toUnmodifiableMap(Device::getId, x -> x)); - var devicesByGroup = devices.stream() - .filter(x -> x.getGroupId() > 0) - .collect(Collectors.groupingBy(Device::getGroupId)); - - var groups = storage.getObjects(Group.class, new Request( - new Columns.All(), - new Condition.Permission(User.class, userId, Group.class))); - var groupsByGroup = groups.stream() - .filter(x -> x.getGroupId() > 0) - .collect(Collectors.groupingBy(Group::getGroupId)); - - var results = deviceIds.stream() - .map(deviceById::get) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - - var groupQueue = new LinkedList<>(groupIds); - while (!groupQueue.isEmpty()) { - long groupId = groupQueue.pop(); - results.addAll(devicesByGroup.getOrDefault(groupId, Collections.emptyList())); - groupQueue.addAll(groupsByGroup.getOrDefault(groupId, Collections.emptyList()) - .stream().map(Group::getId).collect(Collectors.toUnmodifiableList())); - } - - return results; - } - - public double calculateFuel(Position firstPosition, Position lastPosition) { - - if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null - && lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) { - - BigDecimal value = BigDecimal.valueOf(firstPosition.getDouble(Position.KEY_FUEL_LEVEL) - - lastPosition.getDouble(Position.KEY_FUEL_LEVEL)); - return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue(); + public double calculateFuel(Position first, Position last) { + if (first.hasAttribute(Position.KEY_FUEL_USED) && last.hasAttribute(Position.KEY_FUEL_USED)) { + return last.getDouble(Position.KEY_FUEL_USED) - first.getDouble(Position.KEY_FUEL_USED); + } else if (first.hasAttribute(Position.KEY_FUEL_LEVEL) && last.hasAttribute(Position.KEY_FUEL_LEVEL)) { + return first.getDouble(Position.KEY_FUEL_LEVEL) - last.getDouble(Position.KEY_FUEL_LEVEL); } return 0; } @@ -205,20 +161,9 @@ public class ReportUtils { } private TripReportItem calculateTrip( - Device device, ArrayList<Position> positions, int startIndex, int endIndex, + Device device, Position startTrip, Position endTrip, double maxSpeed, boolean ignoreOdometer) throws StorageException { - Position startTrip = positions.get(startIndex); - Position endTrip = positions.get(endIndex); - - double speedMax = 0; - for (int i = startIndex; i <= endIndex; i++) { - double speed = positions.get(i).getSpeed(); - if (speed > speedMax) { - speedMax = speed; - } - } - TripReportItem trip = new TripReportItem(); long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime(); @@ -251,7 +196,7 @@ public class ReportUtils { if (tripDuration > 0) { trip.setAverageSpeed(UnitsConverter.knotsFromMps(trip.getDistance() * 1000 / tripDuration)); } - trip.setMaxSpeed(speedMax); + trip.setMaxSpeed(maxSpeed); trip.setSpentFuel(calculateFuel(startTrip, endTrip)); trip.setDriverUniqueId(findDriver(startTrip, endTrip)); @@ -271,10 +216,7 @@ public class ReportUtils { } private StopReportItem calculateStop( - Device device, ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) { - - Position startStop = positions.get(startIndex); - Position endStop = positions.get(endIndex); + Device device, Position startStop, Position endStop, boolean ignoreOdometer) { StopReportItem stop = new StopReportItem(); @@ -318,17 +260,17 @@ public class ReportUtils { @SuppressWarnings("unchecked") private <T extends BaseReportItem> T calculateTripOrStop( - Device device, ArrayList<Position> positions, int startIndex, int endIndex, + Device device, Position startPosition, Position endPosition, double maxSpeed, boolean ignoreOdometer, Class<T> reportClass) throws StorageException { if (reportClass.equals(TripReportItem.class)) { - return (T) calculateTrip(device, positions, startIndex, endIndex, ignoreOdometer); + return (T) calculateTrip(device, startPosition, endPosition, maxSpeed, ignoreOdometer); } else { - return (T) calculateStop(device, positions, startIndex, endIndex, ignoreOdometer); + return (T) calculateStop(device, startPosition, endPosition, ignoreOdometer); } } - private boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) { + private boolean isMoving(List<Position> positions, int index, TripsConfig tripsConfig) { if (tripsConfig.getMinimalNoDataDuration() > 0) { boolean beforeGap = index < positions.size() - 1 && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime() @@ -343,13 +285,26 @@ public class ReportUtils { return positions.get(index).getBoolean(Position.KEY_MOTION); } - public <T extends BaseReportItem> Collection<T> detectTripsAndStops( - Device device, Collection<Position> positionCollection, boolean ignoreOdometer, - Class<T> reportClass) throws StorageException { + public <T extends BaseReportItem> List<T> detectTripsAndStops( + Device device, Date from, Date to, Class<T> reportClass) throws StorageException { + + long threshold = config.getLong(Keys.REPORT_FAST_THRESHOLD); + if (Duration.between(from.toInstant(), to.toInstant()).toSeconds() > threshold) { + return fastTripsAndStops(device, from, to, reportClass); + } else { + return slowTripsAndStops(device, from, to, reportClass); + } + } + + public <T extends BaseReportItem> List<T> slowTripsAndStops( + Device device, Date from, Date to, Class<T> reportClass) throws StorageException { - Collection<T> result = new ArrayList<>(); + List<T> result = new ArrayList<>(); + TripsConfig tripsConfig = new TripsConfig( + new AttributeUtil.StorageProvider(config, storage, permissionsService, device)); + boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); - ArrayList<Position> positions = new ArrayList<>(positionCollection); + var positions = PositionUtil.getPositions(storage, device.getId(), from, to); if (!positions.isEmpty()) { boolean trips = reportClass.equals(TripReportItem.class); @@ -359,17 +314,23 @@ public class ReportUtils { motionState.setMotionState(initialValue); boolean detected = trips == motionState.getMotionState(); + double maxSpeed = 0; int startEventIndex = detected ? 0 : -1; int startNoEventIndex = -1; for (int i = 0; i < positions.size(); i++) { boolean motion = isMoving(positions, i, tripsConfig); if (motionState.getMotionState() != motion) { if (motion == trips) { - startEventIndex = detected ? startEventIndex : i; + if (!detected) { + startEventIndex = i; + maxSpeed = positions.get(i).getSpeed(); + } startNoEventIndex = -1; } else { startNoEventIndex = i; } + } else { + maxSpeed = Math.max(maxSpeed, positions.get(i).getSpeed()); } MotionProcessor.updateState(motionState, positions.get(i), motion, tripsConfig); @@ -379,17 +340,58 @@ public class ReportUtils { startNoEventIndex = -1; } else if (startEventIndex >= 0 && startNoEventIndex >= 0) { result.add(calculateTripOrStop( - device, positions, startEventIndex, startNoEventIndex, ignoreOdometer, reportClass)); + device, positions.get(startEventIndex), positions.get(startNoEventIndex), + maxSpeed, ignoreOdometer, reportClass)); detected = false; startEventIndex = -1; startNoEventIndex = -1; } } } - if (startEventIndex >= 0 && startEventIndex < positions.size() - 1) { + if (detected & startEventIndex >= 0 && startEventIndex < positions.size() - 1) { int endIndex = startNoEventIndex >= 0 ? startNoEventIndex : positions.size() - 1; result.add(calculateTripOrStop( - device, positions, startEventIndex, endIndex, ignoreOdometer, reportClass)); + device, positions.get(startEventIndex), positions.get(endIndex), + maxSpeed, ignoreOdometer, reportClass)); + } + } + + return result; + } + + public <T extends BaseReportItem> List<T> fastTripsAndStops( + Device device, Date from, Date to, Class<T> reportClass) throws StorageException { + + List<T> result = new ArrayList<>(); + boolean ignoreOdometer = config.getBoolean(Keys.REPORT_IGNORE_ODOMETER); + boolean trips = reportClass.equals(TripReportItem.class); + Set<String> filter = Set.of(Event.TYPE_DEVICE_MOVING, Event.TYPE_DEVICE_STOPPED); + + var events = storage.getObjects(Event.class, new Request( + new Columns.All(), + new Condition.And( + new Condition.Equals("deviceId", device.getId()), + new Condition.Between("eventTime", "from", from, "to", to)), + new Order("eventTime"))); + var filteredEvents = events.stream() + .filter(event -> filter.contains(event.getType())) + .collect(Collectors.toList()); + + Event startEvent = null; + for (Event event : filteredEvents) { + boolean motion = event.getType().equals(Event.TYPE_DEVICE_MOVING); + if (motion == trips) { + startEvent = event; + } else if (startEvent != null) { + Position startPosition = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", startEvent.getPositionId()))); + Position endPosition = storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", event.getPositionId()))); + if (startPosition != null && endPosition != null) { + result.add(calculateTripOrStop( + device, startPosition, endPosition, 0, ignoreOdometer, reportClass)); + } + startEvent = null; } } diff --git a/src/main/java/org/traccar/reports/common/TripsConfig.java b/src/main/java/org/traccar/reports/common/TripsConfig.java index 52db97b74..2792114d4 100644 --- a/src/main/java/org/traccar/reports/common/TripsConfig.java +++ b/src/main/java/org/traccar/reports/common/TripsConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,37 +16,28 @@ */ package org.traccar.reports.common; -import org.traccar.config.Config; import org.traccar.config.Keys; +import org.traccar.helper.model.AttributeUtil; -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton public class TripsConfig { - @Inject - public TripsConfig(Config config) { + public TripsConfig(AttributeUtil.Provider attributeProvider) { this( - config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE), - config.getLong(Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000, - config.getLong(Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000, - config.getLong(Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000, - config.getBoolean(Keys.REPORT_TRIP_USE_IGNITION), - config.getBoolean(Keys.EVENT_MOTION_PROCESS_INVALID_POSITIONS), - config.getDouble(Keys.EVENT_MOTION_SPEED_THRESHOLD)); + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE), + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION) * 1000, + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION) * 1000, + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION) * 1000, + AttributeUtil.lookup(attributeProvider, Keys.REPORT_TRIP_USE_IGNITION)); } public TripsConfig( double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration, - long minimalNoDataDuration, boolean useIgnition, boolean processInvalidPositions, double speedThreshold) { + long minimalNoDataDuration, boolean useIgnition) { this.minimalTripDistance = minimalTripDistance; this.minimalTripDuration = minimalTripDuration; this.minimalParkingDuration = minimalParkingDuration; this.minimalNoDataDuration = minimalNoDataDuration; this.useIgnition = useIgnition; - this.processInvalidPositions = processInvalidPositions; - this.speedThreshold = speedThreshold; } private final double minimalTripDistance; @@ -79,16 +70,4 @@ public class TripsConfig { return useIgnition; } - private final boolean processInvalidPositions; - - public boolean getProcessInvalidPositions() { - return processInvalidPositions; - } - - private final double speedThreshold; - - public double getSpeedThreshold() { - return speedThreshold; - } - } diff --git a/src/main/java/org/traccar/reports/model/CombinedReportItem.java b/src/main/java/org/traccar/reports/model/CombinedReportItem.java new file mode 100644 index 000000000..810e00916 --- /dev/null +++ b/src/main/java/org/traccar/reports/model/CombinedReportItem.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports.model; + +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.util.List; + +public class CombinedReportItem { + + private long deviceId; + + public long getDeviceId() { + return deviceId; + } + + public void setDeviceId(long deviceId) { + this.deviceId = deviceId; + } + + private List<double[]> route; + + public List<double[]> getRoute() { + return route; + } + + public void setRoute(List<double[]> route) { + this.route = route; + } + + private List<Event> events; + + public List<Event> getEvents() { + return events; + } + + public void setEvents(List<Event> events) { + this.events = events; + } + + private List<Position> positions; + + public List<Position> getPositions() { + return positions; + } + + public void setPositions(List<Position> positions) { + this.positions = positions; + } + +} diff --git a/src/main/java/org/traccar/reports/model/DeviceReportItem.java b/src/main/java/org/traccar/reports/model/DeviceReportItem.java new file mode 100644 index 000000000..74cd5f32c --- /dev/null +++ b/src/main/java/org/traccar/reports/model/DeviceReportItem.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.reports.model; + +import org.traccar.model.Device; +import org.traccar.model.Position; + +public class DeviceReportItem { + + public DeviceReportItem(Device device, Position position) { + this.device = device; + this.position = position; + } + + private Device device; + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + private Position position; + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + +} diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java index e1de3b3af..742428fd8 100644 --- a/src/main/java/org/traccar/schedule/ScheduleManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ package org.traccar.schedule; import com.google.inject.Injector; import org.traccar.LifecycleObject; -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.List; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Stream; @Singleton public class ScheduleManager implements LifecycleObject { @@ -38,12 +38,15 @@ public class ScheduleManager implements LifecycleObject { @Override public void start() { executor = Executors.newSingleThreadScheduledExecutor(); - var tasks = List.of( + Stream.of( + TaskHealthCheck.class, + TaskClearStatus.class, + TaskExpirations.class, + TaskDeleteTemporary.class, TaskReports.class, TaskDeviceInactivityCheck.class, - TaskWebSocketKeepalive.class, - TaskHealthCheck.class); - tasks.forEach(task -> injector.getInstance(task).schedule(executor)); + TaskWebSocketKeepalive.class) + .forEachOrdered(task -> injector.getInstance(task).schedule(executor)); } @Override diff --git a/src/main/java/org/traccar/schedule/TaskClearStatus.java b/src/main/java/org/traccar/schedule/TaskClearStatus.java new file mode 100644 index 000000000..78fecc0ea --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskClearStatus.java @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.schedule; + +import jakarta.inject.Inject; +import org.traccar.broadcast.BroadcastService; +import org.traccar.helper.model.DeviceUtil; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; + +import java.util.concurrent.ScheduledExecutorService; + +public class TaskClearStatus implements ScheduleTask { + + @Inject + public TaskClearStatus(BroadcastService broadcastService, Storage storage) throws StorageException { + if (broadcastService.singleInstance()) { + DeviceUtil.resetStatus(storage); + } + } + + @Override + public void schedule(ScheduledExecutorService executor) { + } + + @Override + public void run() { + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java b/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java new file mode 100644 index 000000000..0cead59fb --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskDeleteTemporary.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.schedule; + +import jakarta.inject.Inject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.model.User; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.util.Date; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskDeleteTemporary implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskDeleteTemporary.class); + + private static final long CHECK_PERIOD_HOURS = 1; + + private final Storage storage; + + @Inject + public TaskDeleteTemporary(Storage storage) { + this.storage = storage; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS); + } + + @Override + public void run() { + try { + storage.removeObject(User.class, new Request( + new Condition.And( + new Condition.Equals("temporary", true), + new Condition.Compare("expirationTime", "<", "time", new Date())))); + } catch (StorageException e) { + LOGGER.warn("Failed to delete temporary users", e); + } + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java index 57c64dc5b..8e45568d5 100644 --- a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java +++ b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,17 +20,19 @@ import org.slf4j.LoggerFactory; import org.traccar.database.NotificationManager; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.Group; import org.traccar.model.Position; import org.traccar.storage.Storage; import org.traccar.storage.StorageException; import org.traccar.storage.query.Columns; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; public class TaskDeviceInactivityCheck implements ScheduleTask { @@ -64,22 +66,45 @@ public class TaskDeviceInactivityCheck implements ScheduleTask { Map<Event, Position> events = new HashMap<>(); try { + Map<Long, Group> groups = storage.getObjects(Group.class, new Request(new Columns.All())) + .stream().collect(Collectors.toMap(Group::getId, group -> group)); for (Device device : storage.getObjects(Device.class, new Request(new Columns.All()))) { - if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) { + if (device.getLastUpdate() != null && checkDevice(device, groups, currentTime, checkPeriod)) { Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId()); event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime()); events.put(event, null); } } } catch (StorageException e) { - LOGGER.warn("Get devices error", e); + LOGGER.warn("Database error", e); } notificationManager.updateEvents(events); } - private boolean checkDevice(Device device, long currentTime, long checkPeriod) { - long deviceInactivityStart = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_START); + private long getAttribute(Device device, Map<Long, Group> groups, String key) { + long deviceValue = device.getLong(key); + if (deviceValue > 0) { + return deviceValue; + } else { + long groupId = device.getGroupId(); + while (groupId > 0) { + Group group = groups.get(groupId); + if (group == null) { + return 0; + } + long groupValue = group.getLong(key); + if (groupValue > 0) { + return groupValue; + } + groupId = group.getGroupId(); + } + return 0; + } + } + + private boolean checkDevice(Device device, Map<Long, Group> groups, long currentTime, long checkPeriod) { + long deviceInactivityStart = getAttribute(device, groups, ATTRIBUTE_DEVICE_INACTIVITY_START); if (deviceInactivityStart > 0) { long timeThreshold = device.getLastUpdate().getTime() + deviceInactivityStart; if (currentTime >= timeThreshold) { @@ -88,7 +113,7 @@ public class TaskDeviceInactivityCheck implements ScheduleTask { return true; } - long deviceInactivityPeriod = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_PERIOD); + long deviceInactivityPeriod = getAttribute(device, groups, ATTRIBUTE_DEVICE_INACTIVITY_PERIOD); if (deviceInactivityPeriod > 0) { long count = (currentTime - timeThreshold - 1) / deviceInactivityPeriod; timeThreshold += count * deviceInactivityPeriod; diff --git a/src/main/java/org/traccar/schedule/TaskExpirations.java b/src/main/java/org/traccar/schedule/TaskExpirations.java new file mode 100644 index 000000000..94f855c5f --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskExpirations.java @@ -0,0 +1,130 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.schedule; + +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.mail.MailManager; +import org.traccar.model.Device; +import org.traccar.model.Disableable; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.notification.TextTemplateFormatter; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskExpirations implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskExpirations.class); + + private static final long CHECK_PERIOD_HOURS = 1; + + private final Config config; + private final Storage storage; + private final TextTemplateFormatter textTemplateFormatter; + private final MailManager mailManager; + + @Inject + public TaskExpirations( + Config config, Storage storage, TextTemplateFormatter textTemplateFormatter, MailManager mailManager) { + this.config = config; + this.storage = storage; + this.textTemplateFormatter = textTemplateFormatter; + this.mailManager = mailManager; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS); + } + + private boolean checkTimeTrigger(Disableable disableable, long currentTime, long offsetTime) { + if (disableable.getExpirationTime() != null) { + long previousTime = currentTime - TimeUnit.HOURS.toMillis(CHECK_PERIOD_HOURS); + long expirationTime = disableable.getExpirationTime().getTime() + offsetTime; + return previousTime < expirationTime && currentTime >= expirationTime; + } + return false; + } + + private void sendUserExpiration( + Server server, User user, String template) throws MessagingException { + var velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("expiration", user.getExpirationTime()); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); + } + + private void sendDeviceExpiration( + Server server, Device device, String template) throws MessagingException, StorageException { + var users = storage.getObjects(User.class, new Request( + new Columns.All(), new Condition.Permission(User.class, Device.class, device.getId()))); + for (User user : users) { + var velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("expiration", device.getExpirationTime()); + velocityContext.put("device", device); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); + } + } + + @Override + public void run() { + try { + + long currentTime = System.currentTimeMillis(); + Server server = storage.getObject(Server.class, new Request(new Columns.All())); + + if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_USER)) { + long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER); + var users = storage.getObjects(User.class, new Request(new Columns.All())); + for (User user : users) { + if (checkTimeTrigger(user, currentTime, 0)) { + sendUserExpiration(server, user, "userExpiration"); + } else if (reminder > 0 && checkTimeTrigger(user, currentTime, -reminder)) { + sendUserExpiration(server, user, "userExpirationReminder"); + } + } + } + + if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_DEVICE)) { + long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER); + var devices = storage.getObjects(Device.class, new Request(new Columns.All())); + for (Device device : devices) { + if (checkTimeTrigger(device, currentTime, 0)) { + sendDeviceExpiration(server, device, "deviceExpiration"); + } else if (reminder > 0 && checkTimeTrigger(device, currentTime, -reminder)) { + sendDeviceExpiration(server, device, "deviceExpirationReminder"); + } + } + } + + } catch (StorageException | MessagingException e) { + LOGGER.warn("Failed to check expirations", e); + } + } + +} diff --git a/src/main/java/org/traccar/schedule/TaskHealthCheck.java b/src/main/java/org/traccar/schedule/TaskHealthCheck.java index a8c9873ce..a60935f18 100644 --- a/src/main/java/org/traccar/schedule/TaskHealthCheck.java +++ b/src/main/java/org/traccar/schedule/TaskHealthCheck.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import org.slf4j.LoggerFactory; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.inject.Inject; -import javax.ws.rs.client.Client; +import jakarta.inject.Inject; +import jakarta.ws.rs.client.Client; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -34,6 +34,8 @@ public class TaskHealthCheck implements ScheduleTask { private final Config config; private final Client client; + private final long gracePeriod = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); + private SystemD systemD; private boolean enabled; @@ -77,14 +79,22 @@ public class TaskHealthCheck implements ScheduleTask { @Override public void run() { LOGGER.debug("Health check running"); - int status = client.target(getUrl()).request().get().getStatus(); - if (status == 200) { - int result = systemD.sd_notify(0, "WATCHDOG=1"); - if (result < 0) { - LOGGER.warn("Health check notify error {}", result); + if (System.currentTimeMillis() > gracePeriod) { + int status = client.target(getUrl()).request().get().getStatus(); + if (status == 200) { + notifyWatchdog(); + } else { + LOGGER.warn("Health check failed with status {}", status); } } else { - LOGGER.warn("Health check failed with status {}", status); + notifyWatchdog(); + } + } + + private void notifyWatchdog() { + int result = systemD.sd_notify(0, "WATCHDOG=1"); + if (result < 0) { + LOGGER.warn("Health check notify error {}", result); } } diff --git a/src/main/java/org/traccar/schedule/TaskReports.java b/src/main/java/org/traccar/schedule/TaskReports.java index 004a6078c..e0fa6f8d6 100644 --- a/src/main/java/org/traccar/schedule/TaskReports.java +++ b/src/main/java/org/traccar/schedule/TaskReports.java @@ -18,7 +18,7 @@ package org.traccar.schedule; import com.google.inject.Injector; import com.google.inject.servlet.RequestScoper; import com.google.inject.servlet.ServletScopes; -import net.fortuna.ical4j.model.component.VEvent; +import net.fortuna.ical4j.model.Period; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.model.BaseModel; @@ -39,7 +39,7 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.util.Collections; import java.util.Date; import java.util.List; @@ -51,7 +51,7 @@ public class TaskReports implements ScheduleTask { private static final Logger LOGGER = LoggerFactory.getLogger(TaskReports.class); - private static final long CHECK_PERIOD_MINUTES = 1; + private static final long CHECK_PERIOD_MINUTES = 15; private final Storage storage; private final Injector injector; @@ -77,16 +77,14 @@ public class TaskReports implements ScheduleTask { Calendar calendar = storage.getObject(Calendar.class, new Request( new Columns.All(), new Condition.Equals("id", report.getCalendarId()))); - var lastEvents = calendar.findEvents(lastCheck); - var currentEvents = calendar.findEvents(currentCheck); + var lastEvents = calendar.findPeriods(lastCheck); + var currentEvents = calendar.findPeriods(currentCheck); if (!lastEvents.isEmpty() && currentEvents.isEmpty()) { - VEvent event = lastEvents.iterator().next(); - Date from = event.getStartDate().getDate(); - Date to = event.getEndDate().getDate(); + Period period = lastEvents.iterator().next(); RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap()); try (RequestScoper.CloseableScope ignored = scope.open()) { - executeReport(report, from, to); + executeReport(report, period.getStart(), period.getEnd()); } } } diff --git a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java index e6c2e8b6d..d9e0c6f0b 100644 --- a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java +++ b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java @@ -17,7 +17,7 @@ package org.traccar.schedule; import org.traccar.session.ConnectionManager; -import javax.inject.Inject; +import jakarta.inject.Inject; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; diff --git a/src/main/java/org/traccar/session/ConnectionManager.java b/src/main/java/org/traccar/session/ConnectionManager.java index 37a42d827..42dcf5ce9 100644 --- a/src/main/java/org/traccar/session/ConnectionManager.java +++ b/src/main/java/org/traccar/session/ConnectionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import org.traccar.database.NotificationManager; import org.traccar.model.BaseModel; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.LogRecord; import org.traccar.model.Position; import org.traccar.model.User; import org.traccar.session.cache.CacheManager; @@ -39,8 +40,8 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Arrays; @@ -62,9 +63,11 @@ public class ConnectionManager implements BroadcastInterface { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class); private final long deviceTimeout; + private final boolean showUnknownDevices; private final Map<Long, DeviceSession> sessionsByDeviceId = new ConcurrentHashMap<>(); - private final Map<Endpoint, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map<SocketAddress, Map<String, DeviceSession>> sessionsByEndpoint = new ConcurrentHashMap<>(); + private final Map<SocketAddress, String> unknownByEndpoint = new ConcurrentHashMap<>(); private final Config config; private final CacheManager cacheManager; @@ -93,6 +96,7 @@ public class ConnectionManager implements BroadcastInterface { this.broadcastService = broadcastService; this.deviceLookupService = deviceLookupService; deviceTimeout = config.getLong(Keys.STATUS_TIMEOUT); + showUnknownDevices = config.getBoolean(Keys.WEB_SHOW_UNKNOWN_DEVICES); broadcastService.registerListener(this); } @@ -102,11 +106,10 @@ public class ConnectionManager implements BroadcastInterface { public DeviceSession getDeviceSession( Protocol protocol, Channel channel, SocketAddress remoteAddress, - String... uniqueIds) throws StorageException { + String... uniqueIds) throws Exception { - Endpoint endpoint = new Endpoint(channel, remoteAddress); Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.getOrDefault( - endpoint, new ConcurrentHashMap<>()); + remoteAddress, new ConcurrentHashMap<>()); uniqueIds = Arrays.stream(uniqueIds).filter(Objects::nonNull).toArray(String[]::new); if (uniqueIds.length > 0) { @@ -122,28 +125,31 @@ public class ConnectionManager implements BroadcastInterface { Device device = deviceLookupService.lookup(uniqueIds); + String firstUniqueId = uniqueIds[0]; if (device == null && config.getBoolean(Keys.DATABASE_REGISTER_UNKNOWN)) { - device = addUnknownDevice(uniqueIds[0]); + if (firstUniqueId.matches(config.getString(Keys.DATABASE_REGISTER_UNKNOWN_REGEX))) { + device = addUnknownDevice(firstUniqueId); + } } if (device != null) { + unknownByEndpoint.remove(remoteAddress); device.checkDisabled(); DeviceSession oldSession = sessionsByDeviceId.remove(device.getId()); if (oldSession != null) { - Endpoint oldEndpoint = new Endpoint(oldSession.getChannel(), oldSession.getRemoteAddress()); - Map<String, DeviceSession> oldEndpointSessions = sessionsByEndpoint.get(oldEndpoint); + Map<String, DeviceSession> oldEndpointSessions = sessionsByEndpoint.get(oldSession.getRemoteAddress()); if (oldEndpointSessions != null && oldEndpointSessions.size() > 1) { oldEndpointSessions.remove(device.getUniqueId()); } else { - sessionsByEndpoint.remove(oldEndpoint); + sessionsByEndpoint.remove(oldSession.getRemoteAddress()); } } DeviceSession deviceSession = new DeviceSession( - device.getId(), device.getUniqueId(), protocol, channel, remoteAddress); + device.getId(), device.getUniqueId(), device.getModel(), protocol, channel, remoteAddress); endpointSessions.put(device.getUniqueId(), deviceSession); - sessionsByEndpoint.put(endpoint, endpointSessions); + sessionsByEndpoint.put(remoteAddress, endpointSessions); sessionsByDeviceId.put(device.getId(), deviceSession); if (oldSession == null) { @@ -152,6 +158,7 @@ public class ConnectionManager implements BroadcastInterface { return deviceSession; } else { + unknownByEndpoint.put(remoteAddress, firstUniqueId); LOGGER.warn("Unknown device - " + String.join(" ", uniqueIds) + " (" + ((InetSocketAddress) remoteAddress).getHostString() + ")"); return null; @@ -180,16 +187,19 @@ public class ConnectionManager implements BroadcastInterface { } public void deviceDisconnected(Channel channel, boolean supportsOffline) { - Endpoint endpoint = new Endpoint(channel, channel.remoteAddress()); - Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(endpoint); - if (endpointSessions != null) { - for (DeviceSession deviceSession : endpointSessions.values()) { - if (supportsOffline) { - updateDevice(deviceSession.getDeviceId(), Device.STATUS_OFFLINE, null); + SocketAddress remoteAddress = channel.remoteAddress(); + if (remoteAddress != null) { + Map<String, DeviceSession> endpointSessions = sessionsByEndpoint.remove(remoteAddress); + if (endpointSessions != null) { + for (DeviceSession deviceSession : endpointSessions.values()) { + if (supportsOffline) { + updateDevice(deviceSession.getDeviceId(), Device.STATUS_OFFLINE, null); + } + sessionsByDeviceId.remove(deviceSession.getDeviceId()); + cacheManager.removeDevice(deviceSession.getDeviceId()); } - sessionsByDeviceId.remove(deviceSession.getDeviceId()); - cacheManager.removeDevice(deviceSession.getDeviceId()); } + unknownByEndpoint.remove(remoteAddress); } } @@ -202,8 +212,7 @@ public class ConnectionManager implements BroadcastInterface { DeviceSession deviceSession = sessionsByDeviceId.remove(deviceId); if (deviceSession != null) { cacheManager.removeDevice(deviceId); - Endpoint endpoint = new Endpoint(deviceSession.getChannel(), deviceSession.getRemoteAddress()); - sessionsByEndpoint.computeIfPresent(endpoint, (e, sessions) -> { + sessionsByEndpoint.computeIfPresent(deviceSession.getRemoteAddress(), (e, sessions) -> { sessions.remove(deviceSession.getUniqueId()); return sessions.isEmpty() ? null : sessions; }); @@ -325,11 +334,9 @@ public class ConnectionManager implements BroadcastInterface { } @Override - public synchronized void invalidatePermission( - boolean local, - Class<? extends BaseModel> clazz1, long id1, - Class<? extends BaseModel> clazz2, long id2) { - if (clazz1.equals(User.class) && clazz2.equals(Device.class)) { + public synchronized <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission( + boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) { + if (link && clazz1.equals(User.class) && clazz2.equals(Device.class)) { if (listeners.containsKey(id1)) { userDevices.get(id1).add(id2); deviceUsers.put(id2, new HashSet<>(List.of(id1))); @@ -337,11 +344,34 @@ public class ConnectionManager implements BroadcastInterface { } } + public synchronized void updateLog(LogRecord record) { + var sessions = sessionsByEndpoint.getOrDefault(record.getAddress(), Map.of()); + if (sessions.isEmpty()) { + String unknownUniqueId = unknownByEndpoint.get(record.getAddress()); + if (unknownUniqueId != null && showUnknownDevices) { + record.setUniqueId(unknownUniqueId); + listeners.values().stream() + .flatMap(Set::stream) + .forEach((listener) -> listener.onUpdateLog(record)); + } + } else { + var firstEntry = sessions.entrySet().iterator().next(); + record.setUniqueId(firstEntry.getKey()); + record.setDeviceId(firstEntry.getValue().getDeviceId()); + for (long userId : deviceUsers.getOrDefault(record.getDeviceId(), Set.of())) { + for (UpdateListener listener : listeners.getOrDefault(userId, Set.of())) { + listener.onUpdateLog(record); + } + } + } + } + public interface UpdateListener { void onKeepalive(); void onUpdateDevice(Device device); void onUpdatePosition(Position position); void onUpdateEvent(Event event); + void onUpdateLog(LogRecord record); } public synchronized void addListener(long userId, UpdateListener listener) throws StorageException { diff --git a/src/main/java/org/traccar/session/DeviceSession.java b/src/main/java/org/traccar/session/DeviceSession.java index 009f90f5a..f124ca7f9 100644 --- a/src/main/java/org/traccar/session/DeviceSession.java +++ b/src/main/java/org/traccar/session/DeviceSession.java @@ -29,14 +29,17 @@ public class DeviceSession { private final long deviceId; private final String uniqueId; + private final String model; private final Protocol protocol; private final Channel channel; private final SocketAddress remoteAddress; public DeviceSession( - long deviceId, String uniqueId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { + long deviceId, String uniqueId, String model, + Protocol protocol, Channel channel, SocketAddress remoteAddress) { this.deviceId = deviceId; this.uniqueId = uniqueId; + this.model = model; this.protocol = protocol; this.channel = channel; this.remoteAddress = remoteAddress; @@ -50,6 +53,10 @@ public class DeviceSession { return uniqueId; } + public String getModel() { + return model; + } + public Channel getChannel() { return channel; } diff --git a/src/main/java/org/traccar/session/Endpoint.java b/src/main/java/org/traccar/session/Endpoint.java deleted file mode 100644 index 76aac3444..000000000 --- a/src/main/java/org/traccar/session/Endpoint.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2022 Anton Tananaev (anton@traccar.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.traccar.session; - -import io.netty.channel.Channel; - -import java.net.SocketAddress; -import java.util.Objects; - -public class Endpoint { - - private final Channel channel; - private final SocketAddress remoteAddress; - - public Endpoint(Channel channel, SocketAddress remoteAddress) { - this.channel = channel; - this.remoteAddress = remoteAddress; - } - - public Channel getChannel() { - return channel; - } - - public SocketAddress getRemoteAddress() { - return remoteAddress; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Endpoint endpoint = (Endpoint) o; - return channel.equals(endpoint.channel) && remoteAddress.equals(endpoint.remoteAddress); - } - - @Override - public int hashCode() { - return Objects.hash(channel, remoteAddress); - } - -} diff --git a/src/main/java/org/traccar/session/cache/CacheGraph.java b/src/main/java/org/traccar/session/cache/CacheGraph.java new file mode 100644 index 000000000..c99997288 --- /dev/null +++ b/src/main/java/org/traccar/session/cache/CacheGraph.java @@ -0,0 +1,139 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.cache; + +import org.traccar.model.BaseModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +public class CacheGraph { + + private final Map<CacheKey, CacheNode> roots = new HashMap<>(); + private final WeakValueMap<CacheKey, CacheNode> nodes = new WeakValueMap<>(); + + void addObject(BaseModel value) { + CacheKey key = new CacheKey(value); + CacheNode node = new CacheNode(value); + roots.put(key, node); + nodes.put(key, node); + } + + void removeObject(Class<? extends BaseModel> clazz, long id) { + CacheKey key = new CacheKey(clazz, id); + CacheNode node = nodes.remove(key); + if (node != null) { + node.getAllLinks(false).forEach(child -> child.getLinks(key.getClazz(), true).remove(node)); + } + roots.remove(key); + } + + @SuppressWarnings("unchecked") + <T extends BaseModel> T getObject(Class<T> clazz, long id) { + CacheNode node = nodes.get(new CacheKey(clazz, id)); + return node != null ? (T) node.getValue() : null; + } + + <T extends BaseModel> Stream<T> getObjects( + Class<? extends BaseModel> fromClass, long fromId, + Class<T> clazz, Set<Class<? extends BaseModel>> proxies, boolean forward) { + + CacheNode rootNode = nodes.get(new CacheKey(fromClass, fromId)); + if (rootNode != null) { + return getObjectStream(rootNode, clazz, proxies, forward); + } else { + return Stream.empty(); + } + } + + @SuppressWarnings("unchecked") + private <T extends BaseModel> Stream<T> getObjectStream( + CacheNode rootNode, Class<T> clazz, Set<Class<? extends BaseModel>> proxies, boolean forward) { + + if (proxies.contains(clazz)) { + return Stream.empty(); + } + + var directSteam = rootNode.getLinks(clazz, forward).stream() + .map(node -> (T) node.getValue()); + + var proxyStream = proxies.stream() + .flatMap(proxyClass -> rootNode.getLinks(proxyClass, forward).stream() + .flatMap(node -> getObjectStream(node, clazz, proxies, forward))); + + return Stream.concat(directSteam, proxyStream); + } + + void updateObject(BaseModel value) { + CacheNode node = nodes.get(new CacheKey(value)); + if (node != null) { + node.setValue(value); + } + } + + boolean addLink( + Class<? extends BaseModel> fromClazz, long fromId, + BaseModel toValue) { + boolean stop = true; + CacheNode fromNode = nodes.get(new CacheKey(fromClazz, fromId)); + if (fromNode != null) { + CacheKey toKey = new CacheKey(toValue); + CacheNode toNode = nodes.get(toKey); + if (toNode == null) { + stop = false; + toNode = new CacheNode(toValue); + nodes.put(toKey, toNode); + } + fromNode.getLinks(toValue.getClass(), true).add(toNode); + toNode.getLinks(fromClazz, false).add(fromNode); + } + return stop; + } + + void removeLink( + Class<? extends BaseModel> fromClazz, long fromId, + Class<? extends BaseModel> toClazz, long toId) { + CacheNode fromNode = nodes.get(new CacheKey(fromClazz, fromId)); + if (fromNode != null) { + CacheNode toNode = nodes.get(new CacheKey(toClazz, toId)); + if (toNode != null) { + fromNode.getLinks(toClazz, true).remove(toNode); + toNode.getLinks(fromClazz, false).remove(fromNode); + } + } + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + for (CacheNode node : roots.values()) { + printNode(stringBuilder, node, ""); + } + return stringBuilder.toString().trim(); + } + + private void printNode(StringBuilder stringBuilder, CacheNode node, String indentation) { + stringBuilder + .append('\n') + .append(indentation) + .append(node.getValue().getClass().getSimpleName()) + .append('(').append(node.getValue().getId()).append(')'); + node.getAllLinks(true).forEach(child -> printNode(stringBuilder, child, indentation + " ")); + } + +} diff --git a/src/main/java/org/traccar/session/cache/CacheKey.java b/src/main/java/org/traccar/session/cache/CacheKey.java index 23145e34b..f27d5fbf5 100644 --- a/src/main/java/org/traccar/session/cache/CacheKey.java +++ b/src/main/java/org/traccar/session/cache/CacheKey.java @@ -33,6 +33,10 @@ class CacheKey { this.id = id; } + public Class<? extends BaseModel> getClazz() { + return clazz; + } + public boolean classIs(Class<? extends BaseModel> clazz) { return clazz.equals(this.clazz); } diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java index 9d2350012..064e5672f 100644 --- a/src/main/java/org/traccar/session/cache/CacheManager.java +++ b/src/main/java/org/traccar/session/cache/CacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,8 @@ */ package org.traccar.session.cache; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; import org.traccar.broadcast.BroadcastInterface; import org.traccar.broadcast.BroadcastService; import org.traccar.config.Config; @@ -30,8 +30,10 @@ import org.traccar.model.Group; import org.traccar.model.GroupedModel; import org.traccar.model.Maintenance; import org.traccar.model.Notification; +import org.traccar.model.ObjectOperation; +import org.traccar.model.Permission; import org.traccar.model.Position; -import org.traccar.model.ScheduledModel; +import org.traccar.model.Schedulable; import org.traccar.model.Server; import org.traccar.model.User; import org.traccar.storage.Storage; @@ -40,19 +42,10 @@ import org.traccar.storage.query.Columns; import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @@ -60,10 +53,8 @@ import java.util.stream.Collectors; @Singleton public class CacheManager implements BroadcastInterface { - private static final Logger LOGGER = LoggerFactory.getLogger(CacheManager.class); - private static final int GROUP_DEPTH_LIMIT = 3; - private static final Collection<Class<? extends BaseModel>> CLASSES = Arrays.asList( - Attribute.class, Driver.class, Geofence.class, Maintenance.class, Notification.class); + private static final Set<Class<? extends BaseModel>> GROUPED_CLASSES = + Set.of(Attribute.class, Driver.class, Geofence.class, Maintenance.class, Notification.class); private final Config config; private final Storage storage; @@ -71,24 +62,26 @@ public class CacheManager implements BroadcastInterface { private final ReadWriteLock lock = new ReentrantReadWriteLock(); - private final Map<CacheKey, CacheValue> deviceCache = new HashMap<>(); - private final Map<Long, Integer> deviceReferences = new HashMap<>(); - private final Map<Long, Map<Class<? extends BaseModel>, Set<Long>>> deviceLinks = new HashMap<>(); - private final Map<Long, Position> devicePositions = new HashMap<>(); + private final CacheGraph graph = new CacheGraph(); private Server server; - private final Map<Long, List<User>> notificationUsers = new HashMap<>(); + private final Map<Long, Position> devicePositions = new HashMap<>(); + private final Map<Long, AtomicInteger> deviceReferences = new HashMap<>(); @Inject public CacheManager(Config config, Storage storage, BroadcastService broadcastService) throws StorageException { this.config = config; this.storage = storage; this.broadcastService = broadcastService; - invalidateServer(); - invalidateUsers(); + server = storage.getObject(Server.class, new Request(new Columns.All())); broadcastService.registerListener(this); } + @Override + public String toString() { + return graph.toString(); + } + public Config getConfig() { return config; } @@ -96,29 +89,17 @@ public class CacheManager implements BroadcastInterface { public <T extends BaseModel> T getObject(Class<T> clazz, long id) { try { lock.readLock().lock(); - var cacheValue = deviceCache.get(new CacheKey(clazz, id)); - return cacheValue != null ? cacheValue.getValue() : null; + return graph.getObject(clazz, id); } finally { lock.readLock().unlock(); } } - public <T extends BaseModel> List<T> getDeviceObjects(long deviceId, Class<T> clazz) { + public <T extends BaseModel> Set<T> getDeviceObjects(long deviceId, Class<T> clazz) { try { lock.readLock().lock(); - var links = deviceLinks.get(deviceId); - if (links != null) { - return links.getOrDefault(clazz, new LinkedHashSet<>()).stream() - .map(id -> { - var cacheValue = deviceCache.get(new CacheKey(clazz, id)); - return cacheValue != null ? cacheValue.<T>getValue() : null; - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } else { - LOGGER.warn("Device {} cache missing", deviceId); - return Collections.emptyList(); - } + return graph.getObjects(Device.class, deviceId, clazz, Set.of(Group.class), true) + .collect(Collectors.toUnmodifiableSet()); } finally { lock.readLock().unlock(); } @@ -142,37 +123,45 @@ public class CacheManager implements BroadcastInterface { } } - public List<User> getNotificationUsers(long notificationId, long deviceId) { + public Set<User> getNotificationUsers(long notificationId, long deviceId) { try { lock.readLock().lock(); - var users = deviceLinks.get(deviceId).get(User.class).stream() + Set<User> deviceUsers = getDeviceObjects(deviceId, User.class); + return graph.getObjects(Notification.class, notificationId, User.class, Set.of(), false) + .filter(deviceUsers::contains) .collect(Collectors.toUnmodifiableSet()); - return notificationUsers.getOrDefault(notificationId, new LinkedList<>()).stream() - .filter(user -> users.contains(user.getId())) - .collect(Collectors.toUnmodifiableList()); } finally { lock.readLock().unlock(); } } - public Driver findDriverByUniqueId(long deviceId, String driverUniqueId) { - return getDeviceObjects(deviceId, Driver.class).stream() - .filter(driver -> driver.getUniqueId().equals(driverUniqueId)) - .findFirst() - .orElse(null); + public Set<Notification> getDeviceNotifications(long deviceId) { + try { + lock.readLock().lock(); + var direct = graph.getObjects(Device.class, deviceId, Notification.class, Set.of(Group.class), true) + .map(BaseModel::getId) + .collect(Collectors.toUnmodifiableSet()); + return graph.getObjects(Device.class, deviceId, Notification.class, Set.of(Group.class, User.class), true) + .filter(notification -> notification.getAlways() || direct.contains(notification.getId())) + .collect(Collectors.toUnmodifiableSet()); + } finally { + lock.readLock().unlock(); + } } - public void addDevice(long deviceId) throws StorageException { + public void addDevice(long deviceId) throws Exception { try { lock.writeLock().lock(); - Integer references = deviceReferences.get(deviceId); - if (references != null) { - references += 1; - } else { - unsafeAddDevice(deviceId); - references = 1; + if (deviceReferences.computeIfAbsent(deviceId, k -> new AtomicInteger()).getAndIncrement() <= 0) { + Device device = storage.getObject(Device.class, new Request( + new Columns.All(), new Condition.Equals("id", deviceId))); + graph.addObject(device); + initializeCache(device); + if (device.getPositionId() > 0) { + devicePositions.put(deviceId, storage.getObject(Position.class, new Request( + new Columns.All(), new Condition.Equals("id", device.getPositionId())))); + } } - deviceReferences.put(deviceId, references); } finally { lock.writeLock().unlock(); } @@ -181,15 +170,10 @@ public class CacheManager implements BroadcastInterface { public void removeDevice(long deviceId) { try { lock.writeLock().lock(); - Integer references = deviceReferences.get(deviceId); - if (references != null) { - references -= 1; - if (references <= 0) { - unsafeRemoveDevice(deviceId); - deviceReferences.remove(deviceId); - } else { - deviceReferences.put(deviceId, references); - } + if (deviceReferences.computeIfAbsent(deviceId, k -> new AtomicInteger()).incrementAndGet() <= 0) { + graph.removeObject(Device.class, deviceId); + devicePositions.remove(deviceId); + deviceReferences.remove(deviceId); } } finally { lock.writeLock().unlock(); @@ -199,7 +183,7 @@ public class CacheManager implements BroadcastInterface { public void updatePosition(Position position) { try { lock.writeLock().lock(); - if (deviceLinks.containsKey(position.getDeviceId())) { + if (deviceReferences.containsKey(position.getDeviceId())) { devicePositions.put(position.getDeviceId(), position); } } finally { @@ -208,218 +192,140 @@ public class CacheManager implements BroadcastInterface { } @Override - public void invalidateObject(boolean local, Class<? extends BaseModel> clazz, long id) { - try { - var object = storage.getObject(clazz, new Request( - new Columns.All(), new Condition.Equals("id", id))); - if (object != null) { - updateOrInvalidate(local, object); - } else { - invalidate(clazz, id); - } - } catch (StorageException e) { - throw new RuntimeException(e); - } - } - - public <T extends BaseModel> void updateOrInvalidate(boolean local, T object) throws StorageException { + public <T extends BaseModel> void invalidateObject( + boolean local, Class<T> clazz, long id, ObjectOperation operation) throws Exception { if (local) { - broadcastService.invalidateObject(true, object.getClass(), object.getId()); + broadcastService.invalidateObject(true, clazz, id, operation); } - if (object instanceof Server) { - invalidateServer(); + if (operation == ObjectOperation.DELETE) { + graph.removeObject(clazz, id); + } + if (operation != ObjectOperation.UPDATE) { return; } - if (object instanceof User) { - invalidateUsers(); + + if (clazz.equals(Server.class)) { + server = storage.getObject(Server.class, new Request(new Columns.All())); return; } - boolean invalidate = false; - var before = getObject(object.getClass(), object.getId()); + var after = storage.getObject(clazz, new Request(new Columns.All(), new Condition.Equals("id", id))); + if (after == null) { + return; + } + var before = getObject(after.getClass(), after.getId()); if (before == null) { return; - } else if (object instanceof GroupedModel) { - if (((GroupedModel) before).getGroupId() != ((GroupedModel) object).getGroupId()) { - invalidate = true; - } - } else if (object instanceof ScheduledModel) { - if (((ScheduledModel) before).getCalendarId() != ((ScheduledModel) object).getCalendarId()) { - invalidate = true; - } } - if (invalidate) { - invalidate(object.getClass(), object.getId()); - } else { - try { - lock.writeLock().lock(); - deviceCache.get(new CacheKey(object.getClass(), object.getId())).setValue(object); - } finally { - lock.writeLock().unlock(); + + if (after instanceof GroupedModel) { + long beforeGroupId = ((GroupedModel) before).getGroupId(); + long afterGroupId = ((GroupedModel) after).getGroupId(); + if (beforeGroupId != afterGroupId) { + if (beforeGroupId > 0) { + invalidatePermission(clazz, id, Group.class, beforeGroupId, false); + } + if (afterGroupId > 0) { + invalidatePermission(clazz, id, Group.class, afterGroupId, true); + } } + } else if (after instanceof Schedulable) { + long beforeCalendarId = ((Schedulable) before).getCalendarId(); + long afterCalendarId = ((Schedulable) after).getCalendarId(); + if (beforeCalendarId != afterCalendarId) { + if (beforeCalendarId > 0) { + invalidatePermission(clazz, id, Calendar.class, beforeCalendarId, false); + } + if (afterCalendarId > 0) { + invalidatePermission(clazz, id, Calendar.class, afterCalendarId, true); + } + } + // TODO handle notification always change } - } - public <T extends BaseModel> void invalidate(Class<T> clazz, long id) throws StorageException { - invalidate(new CacheKey(clazz, id)); + graph.updateObject(after); } @Override - public void invalidatePermission( - boolean local, - Class<? extends BaseModel> clazz1, long id1, - Class<? extends BaseModel> clazz2, long id2) { + public <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission( + boolean local, Class<T1> clazz1, long id1, Class<T2> clazz2, long id2, boolean link) throws Exception { if (local) { - broadcastService.invalidatePermission(true, clazz1, id1, clazz2, id2); + broadcastService.invalidatePermission(true, clazz1, id1, clazz2, id2, link); } - try { - invalidate(new CacheKey(clazz1, id1), new CacheKey(clazz2, id2)); - } catch (StorageException e) { - throw new RuntimeException(e); + if (clazz1.equals(User.class) && GroupedModel.class.isAssignableFrom(clazz2)) { + invalidatePermission(clazz2, id2, clazz1, id1, link); + } else { + invalidatePermission(clazz1, id1, clazz2, id2, link); } } - private void invalidateServer() throws StorageException { - server = storage.getObject(Server.class, new Request(new Columns.All())); - } + private <T1 extends BaseModel, T2 extends BaseModel> void invalidatePermission( + Class<T1> fromClass, long fromId, Class<T2> toClass, long toId, boolean link) throws Exception { - private void invalidateUsers() throws StorageException { - notificationUsers.clear(); - Map<Long, User> users = new HashMap<>(); - storage.getObjects(User.class, new Request(new Columns.All())) - .forEach(user -> users.put(user.getId(), user)); - storage.getPermissions(User.class, Notification.class).forEach(permission -> { - long notificationId = permission.getPropertyId(); - var user = users.get(permission.getOwnerId()); - notificationUsers.computeIfAbsent(notificationId, k -> new LinkedList<>()).add(user); - }); - } + boolean groupLink = GroupedModel.class.isAssignableFrom(fromClass) && toClass.equals(Group.class); + boolean calendarLink = Schedulable.class.isAssignableFrom(fromClass) && toClass.equals(Calendar.class); + boolean userLink = fromClass.equals(User.class) && toClass.equals(Notification.class); - private void addObject(long deviceId, BaseModel object) { - deviceCache.computeIfAbsent(new CacheKey(object), k -> new CacheValue(object)).retain(deviceId); - } + boolean groupedLinks = GroupedModel.class.isAssignableFrom(fromClass) + && (GROUPED_CLASSES.contains(toClass) || toClass.equals(User.class)); + + if (!groupLink && !calendarLink && !userLink && !groupedLinks) { + return; + } - private void unsafeAddDevice(long deviceId) throws StorageException { - Map<Class<? extends BaseModel>, Set<Long>> links = new HashMap<>(); - - Device device = storage.getObject(Device.class, new Request( - new Columns.All(), new Condition.Equals("id", deviceId))); - if (device != null) { - addObject(deviceId, device); - - int groupDepth = 0; - long groupId = device.getGroupId(); - while (groupDepth < GROUP_DEPTH_LIMIT && groupId > 0) { - Group group = storage.getObject(Group.class, new Request( - new Columns.All(), new Condition.Equals("id", groupId))); - links.computeIfAbsent(Group.class, k -> new LinkedHashSet<>()).add(group.getId()); - addObject(deviceId, group); - groupId = group.getGroupId(); - groupDepth += 1; + if (link) { + BaseModel object = storage.getObject(toClass, new Request( + new Columns.All(), new Condition.Equals("id", toId))); + if (!graph.addLink(fromClass, fromId, object)) { + initializeCache(object); } + } else { + graph.removeLink(fromClass, fromId, toClass, toId); + } + } - for (Class<? extends BaseModel> clazz : CLASSES) { - var objects = storage.getObjects(clazz, new Request( - new Columns.All(), new Condition.Permission(Device.class, deviceId, clazz))); - links.put(clazz, objects.stream().map(BaseModel::getId).collect(Collectors.toSet())); - for (var object : objects) { - addObject(deviceId, object); - if (object instanceof ScheduledModel) { - var scheduled = (ScheduledModel) object; - if (scheduled.getCalendarId() > 0) { - var calendar = storage.getObject(Calendar.class, new Request( - new Columns.All(), new Condition.Equals("id", scheduled.getCalendarId()))); - links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()) - .add(calendar.getId()); - addObject(deviceId, calendar); - } - } + private void initializeCache(BaseModel object) throws Exception { + if (object instanceof User) { + for (Permission permission : storage.getPermissions(User.class, Notification.class)) { + if (permission.getOwnerId() == object.getId()) { + invalidatePermission( + permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId(), true); } } + } else { + if (object instanceof GroupedModel) { + long groupId = ((GroupedModel) object).getGroupId(); + if (groupId > 0) { + invalidatePermission(object.getClass(), object.getId(), Group.class, groupId, true); + } - var users = storage.getObjects(User.class, new Request( - new Columns.All(), new Condition.Permission(User.class, Device.class, deviceId))); - links.put(User.class, users.stream().map(BaseModel::getId).collect(Collectors.toSet())); - for (var user : users) { - addObject(deviceId, user); - var notifications = storage.getObjects(Notification.class, new Request( - new Columns.All(), - new Condition.Permission(User.class, user.getId(), Notification.class))).stream() - .filter(Notification::getAlways) - .collect(Collectors.toList()); - for (var notification : notifications) { - links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()) - .add(notification.getId()); - addObject(deviceId, notification); - if (notification.getCalendarId() > 0) { - var calendar = storage.getObject(Calendar.class, new Request( - new Columns.All(), new Condition.Equals("id", notification.getCalendarId()))); - links.computeIfAbsent(Notification.class, k -> new LinkedHashSet<>()) - .add(calendar.getId()); - addObject(deviceId, calendar); + for (Permission permission : storage.getPermissions(User.class, object.getClass())) { + if (permission.getPropertyId() == object.getId()) { + invalidatePermission( + object.getClass(), object.getId(), User.class, permission.getOwnerId(), true); } } - } - deviceLinks.put(deviceId, links); - - if (device.getPositionId() > 0) { - devicePositions.put(deviceId, storage.getObject(Position.class, new Request( - new Columns.All(), new Condition.Equals("id", device.getPositionId())))); + for (Class<? extends BaseModel> clazz : GROUPED_CLASSES) { + for (Permission permission : storage.getPermissions(object.getClass(), clazz)) { + if (permission.getOwnerId() == object.getId()) { + invalidatePermission( + object.getClass(), object.getId(), clazz, permission.getPropertyId(), true); + } + } + } } - } - } - - private void unsafeRemoveDevice(long deviceId) { - deviceCache.remove(new CacheKey(Device.class, deviceId)); - deviceLinks.remove(deviceId).forEach((clazz, ids) -> ids.forEach(id -> { - var key = new CacheKey(clazz, id); - deviceCache.computeIfPresent(key, (k, value) -> { - value.release(deviceId); - return value.getReferences().size() > 0 ? value : null; - }); - })); - devicePositions.remove(deviceId); - } - - private void invalidate(CacheKey... keys) throws StorageException { - try { - lock.writeLock().lock(); - unsafeInvalidate(keys); - } finally { - lock.writeLock().unlock(); - } - } - private void unsafeInvalidate(CacheKey[] keys) throws StorageException { - boolean invalidateServer = false; - boolean invalidateUsers = false; - Set<Long> linkedDevices = new HashSet<>(); - for (var key : keys) { - if (key.classIs(Server.class)) { - invalidateServer = true; - } else { - if (key.classIs(User.class) || key.classIs(Notification.class)) { - invalidateUsers = true; + if (object instanceof Schedulable) { + long calendarId = ((Schedulable) object).getCalendarId(); + if (calendarId > 0) { + invalidatePermission(object.getClass(), object.getId(), Calendar.class, calendarId, true); } - deviceCache.computeIfPresent(key, (k, value) -> { - linkedDevices.addAll(value.getReferences()); - return value; - }); } } - for (long deviceId : linkedDevices) { - unsafeRemoveDevice(deviceId); - unsafeAddDevice(deviceId); - } - if (invalidateServer) { - invalidateServer(); - } - if (invalidateUsers) { - invalidateUsers(); - } } } diff --git a/src/main/java/org/traccar/session/cache/CacheNode.java b/src/main/java/org/traccar/session/cache/CacheNode.java new file mode 100644 index 000000000..7b584f81a --- /dev/null +++ b/src/main/java/org/traccar/session/cache/CacheNode.java @@ -0,0 +1,40 @@ +package org.traccar.session.cache; + +import org.traccar.model.BaseModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +public class CacheNode { + + private BaseModel value; + + private final Map<Class<? extends BaseModel>, Set<CacheNode>> links = new HashMap<>(); + private final Map<Class<? extends BaseModel>, Set<CacheNode>> backlinks = new HashMap<>(); + + public CacheNode(BaseModel value) { + this.value = value; + } + + public BaseModel getValue() { + return value; + } + + public void setValue(BaseModel value) { + this.value = value; + } + + public Set<CacheNode> getLinks(Class<? extends BaseModel> clazz, boolean forward) { + var map = forward ? links : backlinks; + return map.computeIfAbsent(clazz, k -> new HashSet<>()); + } + + public Stream<CacheNode> getAllLinks(boolean forward) { + var map = forward ? links : backlinks; + return map.values().stream().flatMap(Set::stream); + } + +} diff --git a/src/main/java/org/traccar/session/cache/CacheValue.java b/src/main/java/org/traccar/session/cache/CacheValue.java deleted file mode 100644 index 1f0383ce5..000000000 --- a/src/main/java/org/traccar/session/cache/CacheValue.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2022 Anton Tananaev (anton@traccar.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.traccar.session.cache; - -import org.traccar.model.BaseModel; - -import java.util.HashSet; -import java.util.Set; - -class CacheValue { - - private BaseModel value; - private final Set<Long> references = new HashSet<>(); - - CacheValue(BaseModel value) { - this.value = value; - } - - public void retain(long deviceId) { - references.add(deviceId); - } - - public void release(long deviceId) { - references.remove(deviceId); - } - - @SuppressWarnings("unchecked") - public <T extends BaseModel> T getValue() { - return (T) value; - } - - public void setValue(BaseModel value) { - this.value = value; - } - - public Set<Long> getReferences() { - return references; - } - -} diff --git a/src/main/java/org/traccar/session/cache/WeakValueMap.java b/src/main/java/org/traccar/session/cache/WeakValueMap.java new file mode 100644 index 000000000..8323e2c30 --- /dev/null +++ b/src/main/java/org/traccar/session/cache/WeakValueMap.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.session.cache; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; + +public class WeakValueMap<K, V> { + + private final Map<K, WeakReference<V>> map = new HashMap<>(); + + public void put(K key, V value) { + map.put(key, new WeakReference<>(value)); + } + + public V get(K key) { + WeakReference<V> weakReference = map.get(key); + return (weakReference != null) ? weakReference.get() : null; + } + + public V remove(K key) { + WeakReference<V> weakReference = map.remove(key); + return (weakReference != null) ? weakReference.get() : null; + } + + private void clean() { + map.entrySet().removeIf(entry -> entry.getValue().get() == null); + } + +} diff --git a/src/main/java/org/traccar/session/state/OverspeedProcessor.java b/src/main/java/org/traccar/session/state/OverspeedProcessor.java index 62f6a3de2..221b51ff5 100644 --- a/src/main/java/org/traccar/session/state/OverspeedProcessor.java +++ b/src/main/java/org/traccar/session/state/OverspeedProcessor.java @@ -26,40 +26,46 @@ public final class OverspeedProcessor { } public static void updateState( - OverspeedState state, Position position, double speedLimit, long minimalDuration, long geofenceId) { + OverspeedState state, Position position, + double speedLimit, double multiplier, long minimalDuration, long geofenceId) { state.setEvent(null); boolean oldState = state.getOverspeedState(); if (oldState) { - boolean newState = position.getSpeed() > speedLimit; + boolean newState = position.getSpeed() > speedLimit * multiplier; if (newState) { - if (state.getOverspeedTime() != null) { - long oldTime = state.getOverspeedTime().getTime(); - long newTime = position.getFixTime().getTime(); - if (newTime - oldTime > minimalDuration) { - - Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position); - event.set(ATTRIBUTE_SPEED, position.getSpeed()); - event.set(Position.KEY_SPEED_LIMIT, speedLimit); - event.setGeofenceId(state.getOverspeedGeofenceId()); - - state.setOverspeedTime(null); - state.setOverspeedGeofenceId(0); - state.setEvent(event); - - } - } + checkEvent(state, position, speedLimit, minimalDuration); } else { state.setOverspeedState(false); state.setOverspeedTime(null); state.setOverspeedGeofenceId(0); } - } else if (position != null && position.getSpeed() > speedLimit) { + } else if (position != null && position.getSpeed() > speedLimit * multiplier) { state.setOverspeedState(true); state.setOverspeedTime(position.getFixTime()); state.setOverspeedGeofenceId(geofenceId); + + checkEvent(state, position, speedLimit, minimalDuration); } } + private static void checkEvent(OverspeedState state, Position position, double speedLimit, long minimalDuration) { + if (state.getOverspeedTime() != null) { + long oldTime = state.getOverspeedTime().getTime(); + long newTime = position.getFixTime().getTime(); + if (newTime - oldTime >= minimalDuration) { + + Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position); + event.set(ATTRIBUTE_SPEED, position.getSpeed()); + event.set(Position.KEY_SPEED_LIMIT, speedLimit); + event.setGeofenceId(state.getOverspeedGeofenceId()); + + state.setOverspeedTime(null); + state.setOverspeedGeofenceId(0); + state.setEvent(event); + + } + } + } } diff --git a/src/main/java/org/traccar/sms/HttpSmsClient.java b/src/main/java/org/traccar/sms/HttpSmsClient.java index 51b161594..a2a0dd57f 100644 --- a/src/main/java/org/traccar/sms/HttpSmsClient.java +++ b/src/main/java/org/traccar/sms/HttpSmsClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org) * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,11 +21,11 @@ import org.traccar.config.Keys; import org.traccar.helper.DataConverter; import org.traccar.notification.MessageException; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -57,7 +57,10 @@ public class HttpSmsClient implements SmsManager { } } template = config.getString(Keys.SMS_HTTP_TEMPLATE).trim(); - if (template.charAt(0) == '{' || template.charAt(0) == '[') { + if (template.charAt(0) == '<') { + encode = false; + mediaType = MediaType.APPLICATION_XML_TYPE; + } else if (template.charAt(0) == '{' || template.charAt(0) == '[') { encode = false; mediaType = MediaType.APPLICATION_JSON_TYPE; } else { @@ -67,7 +70,7 @@ public class HttpSmsClient implements SmsManager { } private String prepareValue(String value) throws UnsupportedEncodingException { - return encode ? URLEncoder.encode(value, StandardCharsets.UTF_8.name()) : value; + return encode ? URLEncoder.encode(value, StandardCharsets.UTF_8) : value; } private String preparePayload(String destAddress, String message) { diff --git a/src/main/java/org/traccar/sms/SmsManager.java b/src/main/java/org/traccar/sms/SmsManager.java index 9ab25d9cb..8cf99c9e8 100644 --- a/src/main/java/org/traccar/sms/SmsManager.java +++ b/src/main/java/org/traccar/sms/SmsManager.java @@ -20,7 +20,6 @@ import org.traccar.notification.MessageException; public interface SmsManager { - void sendMessage( - String destAddress, String message, boolean command) throws InterruptedException, MessageException; + void sendMessage(String destAddress, String message, boolean command) throws MessageException; } diff --git a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java index edf089f37..a25eedb2c 100644 --- a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java +++ b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,22 +15,25 @@ */ package org.traccar.speedlimit; +import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.helper.UnitsConverter; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.ws.rs.client.AsyncInvoker; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.InvocationCallback; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.ws.rs.client.AsyncInvoker; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.InvocationCallback; public class OverpassSpeedLimitProvider implements SpeedLimitProvider { private final Client client; private final String url; - public OverpassSpeedLimitProvider(Client client, String url) { + public OverpassSpeedLimitProvider(Config config, Client client, String url) { + int accuracy = config.getInteger(Keys.SPEED_LIMIT_ACCURACY); this.client = client; - this.url = url + "?data=[out:json];way[maxspeed](around:100.0,%f,%f);out%%20tags;"; + this.url = url + "?data=[out:json];way[maxspeed](around:" + accuracy + ",%f,%f);out%%20tags;"; } private Double parseSpeed(String value) { diff --git a/src/main/java/org/traccar/storage/DatabaseModule.java b/src/main/java/org/traccar/storage/DatabaseModule.java index 3e3483818..9d9e5bd5e 100644 --- a/src/main/java/org/traccar/storage/DatabaseModule.java +++ b/src/main/java/org/traccar/storage/DatabaseModule.java @@ -29,7 +29,7 @@ import liquibase.resource.ResourceAccessor; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.inject.Singleton; +import jakarta.inject.Singleton; import javax.sql.DataSource; import java.io.File; import java.io.IOException; diff --git a/src/main/java/org/traccar/storage/DatabaseStorage.java b/src/main/java/org/traccar/storage/DatabaseStorage.java index a049a641c..d20429319 100644 --- a/src/main/java/org/traccar/storage/DatabaseStorage.java +++ b/src/main/java/org/traccar/storage/DatabaseStorage.java @@ -27,7 +27,7 @@ import org.traccar.storage.query.Condition; import org.traccar.storage.query.Order; import org.traccar.storage.query.Request; -import javax.inject.Inject; +import jakarta.inject.Inject; import javax.sql.DataSource; import java.sql.SQLException; import java.util.HashMap; diff --git a/src/main/java/org/traccar/storage/MemoryStorage.java b/src/main/java/org/traccar/storage/MemoryStorage.java index f19897ff8..9b5db1209 100644 --- a/src/main/java/org/traccar/storage/MemoryStorage.java +++ b/src/main/java/org/traccar/storage/MemoryStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,35 +18,155 @@ package org.traccar.storage; import org.traccar.model.BaseModel; import org.traccar.model.Pair; import org.traccar.model.Permission; +import org.traccar.model.Server; +import org.traccar.storage.query.Condition; import org.traccar.storage.query.Request; +import java.beans.Introspector; +import java.lang.reflect.Method; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; public class MemoryStorage extends Storage { + private final Map<Class<?>, Map<Long, Object>> objects = new HashMap<>(); private final Map<Pair<Class<?>, Class<?>>, Set<Pair<Long, Long>>> permissions = new HashMap<>(); + private final AtomicLong increment = new AtomicLong(); + + public MemoryStorage() { + Server server = new Server(); + server.setId(1); + server.setRegistration(true); + objects.put(Server.class, Map.of(server.getId(), server)); + } + @Override public <T> List<T> getObjects(Class<T> clazz, Request request) { - return null; + return objects.computeIfAbsent(clazz, key -> new HashMap<>()).values().stream() + .filter(object -> checkCondition(request.getCondition(), object)) + .map(object -> (T) object) + .collect(Collectors.toList()); + } + + private boolean checkCondition(Condition genericCondition, Object object) { + if (genericCondition == null) { + return true; + } + + if (genericCondition instanceof Condition.Compare) { + + var condition = (Condition.Compare) genericCondition; + Object value = retrieveValue(object, condition.getVariable()); + int result = ((Comparable) value).compareTo(condition.getValue()); + switch (condition.getOperator()) { + case "<": + return result < 0; + case "<=": + return result <= 0; + case ">": + return result > 0; + case ">=": + return result >= 0; + case "=": + return result == 0; + default: + throw new RuntimeException("Unsupported comparison condition"); + } + + } else if (genericCondition instanceof Condition.Between) { + + var condition = (Condition.Between) genericCondition; + Object fromValue = retrieveValue(object, condition.getFromVariable()); + int fromResult = ((Comparable) fromValue).compareTo(condition.getFromValue()); + Object toValue = retrieveValue(object, condition.getToVariable()); + int toResult = ((Comparable) toValue).compareTo(condition.getToValue()); + return fromResult >= 0 && toResult <= 0; + + } else if (genericCondition instanceof Condition.Binary) { + + var condition = (Condition.Binary) genericCondition; + if (condition.getOperator().equals("AND")) { + return checkCondition(condition.getFirst(), object) && checkCondition(condition.getSecond(), object); + } else if (condition.getOperator().equals("OR")) { + return checkCondition(condition.getFirst(), object) || checkCondition(condition.getSecond(), object); + } + + } else if (genericCondition instanceof Condition.Permission) { + + var condition = (Condition.Permission) genericCondition; + long id = (Long) retrieveValue(object, "id"); + return getPermissionsSet(condition.getOwnerClass(), condition.getPropertyClass()).stream() + .anyMatch(pair -> { + if (condition.getOwnerId() > 0) { + return pair.getFirst() == condition.getOwnerId() && pair.getSecond() == id; + } else { + return pair.getFirst() == id && pair.getSecond() == condition.getPropertyId(); + } + }); + + } else if (genericCondition instanceof Condition.LatestPositions) { + + return false; + + } + + return false; + } + + private Object retrieveValue(Object object, String key) { + try { + Method method = object.getClass().getMethod( + "get" + Character.toUpperCase(key.charAt(0)) + key.substring(1)); + return method.invoke(object); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } } @Override public <T> long addObject(T entity, Request request) { - return 0; + long id = increment.incrementAndGet(); + objects.computeIfAbsent(entity.getClass(), key -> new HashMap<>()).put(id, entity); + return id; } @Override public <T> void updateObject(T entity, Request request) { + Set<String> columns = new HashSet<>(request.getColumns().getColumns(entity.getClass(), "get")); + Collection<Object> items; + if (request.getCondition() != null) { + long id = (Long) ((Condition.Equals) request.getCondition()).getValue(); + items = List.of(objects.computeIfAbsent(entity.getClass(), key -> new HashMap<>()).get(id)); + } else { + items = objects.computeIfAbsent(entity.getClass(), key -> new HashMap<>()).values(); + } + for (Method setter : entity.getClass().getMethods()) { + if (setter.getName().startsWith("set") && setter.getParameterCount() == 1 + && columns.contains(Introspector.decapitalize(setter.getName()))) { + try { + Method getter = entity.getClass().getMethod(setter.getName().replaceFirst("set", "get")); + Object value = getter.invoke(entity); + for (Object object : items) { + setter.invoke(object, value); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + } } @Override public void removeObject(Class<?> clazz, Request request) { + long id = (Long) ((Condition.Equals) request.getCondition()).getValue(); + objects.computeIfAbsent(clazz, key -> new HashMap<>()).remove(id); } private Set<Pair<Long, Long>> getPermissionsSet(Class<?> ownerClass, Class<?> propertyClass) { diff --git a/src/main/java/org/traccar/web/ConsoleServlet.java b/src/main/java/org/traccar/web/ConsoleServlet.java index 902a4f7a9..0012ba077 100644 --- a/src/main/java/org/traccar/web/ConsoleServlet.java +++ b/src/main/java/org/traccar/web/ConsoleServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ package org.traccar.web; import org.h2.server.web.ConnectionInfo; -import org.h2.server.web.WebServlet; +import org.h2.server.web.JakartaWebServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.traccar.config.Config; @@ -26,7 +26,7 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class ConsoleServlet extends WebServlet { +public class ConsoleServlet extends JakartaWebServlet { private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleServlet.class); @@ -41,7 +41,7 @@ public class ConsoleServlet extends WebServlet { super.init(); try { - Field field = WebServlet.class.getDeclaredField("server"); + Field field = JakartaWebServlet.class.getDeclaredField("server"); field.setAccessible(true); org.h2.server.web.WebServer server = (org.h2.server.web.WebServer) field.get(this); diff --git a/src/main/java/org/traccar/web/ModernDefaultServlet.java b/src/main/java/org/traccar/web/ModernDefaultServlet.java new file mode 100644 index 000000000..a7c8cdb29 --- /dev/null +++ b/src/main/java/org/traccar/web/ModernDefaultServlet.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web; + +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.util.resource.Resource; +import org.traccar.config.Config; +import org.traccar.config.Keys; + +import jakarta.inject.Inject; +import java.io.File; +import java.io.IOException; + +public class ModernDefaultServlet extends DefaultServlet { + + private Resource overrideResource; + + @Inject + public ModernDefaultServlet(Config config) { + String override = config.getString(Keys.WEB_OVERRIDE); + if (override != null) { + overrideResource = Resource.newResource(new File(override)); + } + } + + @Override + public Resource getResource(String pathInContext) { + if (overrideResource != null) { + try { + Resource override = overrideResource.addPath(pathInContext); + if (override.exists()) { + return override; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return super.getResource(pathInContext.indexOf('.') < 0 ? "/" : pathInContext); + } + + @Override + public String getWelcomeFile(String pathInContext) { + return super.getWelcomeFile("/"); + } + +} diff --git a/src/main/java/org/traccar/web/OverrideFilter.java b/src/main/java/org/traccar/web/OverrideFilter.java new file mode 100644 index 000000000..9780c9ede --- /dev/null +++ b/src/main/java/org/traccar/web/OverrideFilter.java @@ -0,0 +1,88 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web; + +import com.google.inject.Provider; +import org.traccar.api.security.PermissionsService; +import org.traccar.model.Server; +import org.traccar.storage.StorageException; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Singleton +public class OverrideFilter implements Filter { + + private final Provider<PermissionsService> permissionsServiceProvider; + + @Inject + public OverrideFilter(Provider<PermissionsService> permissionsServiceProvider) { + this.permissionsServiceProvider = permissionsServiceProvider; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + if (((HttpServletRequest) request).getServletPath().startsWith("/api")) { + chain.doFilter(request, response); + return; + } + + ResponseWrapper wrappedResponse = new ResponseWrapper((HttpServletResponse) response); + + chain.doFilter(request, wrappedResponse); + + byte[] bytes = wrappedResponse.getCapture(); + if (bytes != null) { + if (wrappedResponse.getContentType() != null && wrappedResponse.getContentType().contains("text/html") + || ((HttpServletRequest) request).getPathInfo().endsWith("manifest.webmanifest")) { + + Server server; + try { + server = permissionsServiceProvider.get().getServer(); + } catch (StorageException e) { + throw new RuntimeException(e); + } + + String title = server.getString("title", "Traccar"); + String description = server.getString("description", "Traccar GPS Tracking System"); + String colorPrimary = server.getString("colorPrimary", "#1a237e"); + + String alteredContent = new String(wrappedResponse.getCapture()) + .replace("${title}", title) + .replace("${description}", description) + .replace("${colorPrimary}", colorPrimary); + + byte[] data = alteredContent.getBytes(); + response.setContentLength(data.length); + response.getOutputStream().write(data); + + } else { + response.getOutputStream().write(bytes); + } + } + } + +} diff --git a/src/main/java/org/traccar/web/ResponseWrapper.java b/src/main/java/org/traccar/web/ResponseWrapper.java new file mode 100644 index 000000000..a0eaf6788 --- /dev/null +++ b/src/main/java/org/traccar/web/ResponseWrapper.java @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class ResponseWrapper extends HttpServletResponseWrapper { + + private final ByteArrayOutputStream capture; + private ServletOutputStream output; + + public ResponseWrapper(HttpServletResponse response) { + super(response); + capture = new ByteArrayOutputStream(response.getBufferSize()); + } + + @Override + public ServletOutputStream getOutputStream() { + if (output == null) { + output = new ServletOutputStream() { + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + } + + @Override + public void write(int b) { + capture.write(b); + } + + @Override + public void flush() throws IOException { + capture.flush(); + } + + @Override + public void close() throws IOException { + capture.close(); + } + }; + } + return output; + } + + @Override + public void flushBuffer() throws IOException { + super.flushBuffer(); + if (output != null) { + output.flush(); + } + } + + public byte[] getCapture() throws IOException { + if (output != null) { + output.close(); + return capture.toByteArray(); + } + return null; + } + +} diff --git a/src/main/java/org/traccar/web/ThrottlingFilter.java b/src/main/java/org/traccar/web/ThrottlingFilter.java index 054af652f..1bad33db6 100644 --- a/src/main/java/org/traccar/web/ThrottlingFilter.java +++ b/src/main/java/org/traccar/web/ThrottlingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,13 @@ import org.eclipse.jetty.servlets.DoSFilter; import org.traccar.config.Config; import org.traccar.config.Keys; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; @Singleton public class ThrottlingFilter extends DoSFilter { @@ -39,6 +39,7 @@ public class ThrottlingFilter extends DoSFilter { if (config.hasKey(Keys.WEB_MAX_REQUESTS_PER_SECOND)) { setMaxRequestsPerSec(config.getInteger(Keys.WEB_MAX_REQUESTS_PER_SECOND)); } + setMaxRequestMs(config.getInteger(Keys.WEB_MAX_REQUEST_SECONDS) * 1000L); } @Override diff --git a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java index 14d9d3dbc..3e73c41ad 100644 --- a/src/main/java/org/traccar/web/WebInjectionManagerFactory.java +++ b/src/main/java/org/traccar/web/WebInjectionManagerFactory.java @@ -23,7 +23,7 @@ import org.jvnet.hk2.guice.bridge.api.GuiceBridge; import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge; import org.traccar.Main; -import javax.annotation.Priority; +import jakarta.annotation.Priority; @Priority(20) public class WebInjectionManagerFactory implements InjectionManagerFactory { diff --git a/src/main/java/org/traccar/web/WebModule.java b/src/main/java/org/traccar/web/WebModule.java index 0722c5d1e..a32a6f447 100644 --- a/src/main/java/org/traccar/web/WebModule.java +++ b/src/main/java/org/traccar/web/WebModule.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2022 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ public class WebModule extends ServletModule { @Override protected void configureServlets() { + filter("/*").through(OverrideFilter.class); filter("/api/*").through(ThrottlingFilter.class); filter("/api/media/*").through(MediaFilter.class); serve("/api/socket").with(AsyncSocketServlet.class); diff --git a/src/main/java/org/traccar/web/WebRequestLog.java b/src/main/java/org/traccar/web/WebRequestLog.java new file mode 100644 index 000000000..3f3286003 --- /dev/null +++ b/src/main/java/org/traccar/web/WebRequestLog.java @@ -0,0 +1,57 @@ +/* + * Copyright 2023 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.web; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.RequestLog; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.DateCache; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.traccar.api.resource.SessionResource; + +import java.util.Locale; +import java.util.TimeZone; + +public class WebRequestLog extends ContainerLifeCycle implements RequestLog { + + private final Writer writer; + + private final DateCache dateCache = new DateCache( + "dd/MMM/yyyy:HH:mm:ss ZZZ", Locale.getDefault(), TimeZone.getTimeZone("GMT")); + + public WebRequestLog(Writer writer) { + this.writer = writer; + addBean(writer); + } + + @Override + public void log(Request request, Response response) { + try { + Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY); + writer.write(String.format("%s - %s [%s] \"%s %s %s\" %d %d", + request.getRemoteHost(), + userId != null ? String.valueOf(userId) : "-", + dateCache.format(request.getTimeStamp()), + request.getMethod(), + request.getOriginalURI(), + request.getProtocol(), + response.getCommittedMetaData().getStatus(), + response.getHttpChannel().getBytesWritten())); + } catch (Throwable ignored) { + } + } + +} diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java index 79d19cc9b..4759942b1 100644 --- a/src/main/java/org/traccar/web/WebServer.java +++ b/src/main/java/org/traccar/web/WebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2023 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.proxy.AsyncProxyServlet; -import org.eclipse.jetty.server.CustomRequestLog; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.RequestLogWriter; import org.eclipse.jetty.server.Server; @@ -52,19 +51,16 @@ import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.helper.ObjectMapperContextResolver; -import javax.servlet.DispatcherType; -import javax.servlet.ServletException; -import javax.servlet.SessionCookieConfig; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.ServletException; +import jakarta.servlet.SessionCookieConfig; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import javax.sql.DataSource; import java.io.File; import java.io.IOException; import java.io.Writer; import java.net.InetSocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.EnumSet; public class WebServer implements LifecycleObject { @@ -103,14 +99,8 @@ public class WebServer implements LifecycleObject { @Override protected void handleErrorPage( HttpServletRequest request, Writer writer, int code, String message) throws IOException { - Path index = Paths.get(config.getString(Keys.WEB_PATH), "index.html"); - if (code == HttpStatus.NOT_FOUND_404 - && !request.getPathInfo().startsWith("/api/") && Files.exists(index)) { - writer.write(Files.readString(index)); - } else { - writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>" - + code + " - " + HttpStatus.getMessage(code) + "</body></html>"); - } + writer.write("<!DOCTYPE><html><head><title>Error</title></head><html><body>" + + code + " - " + HttpStatus.getMessage(code) + "</body></html>"); } }); @@ -124,8 +114,7 @@ public class WebServer implements LifecycleObject { RequestLogWriter logWriter = new RequestLogWriter(config.getString(Keys.WEB_REQUEST_LOG_PATH)); logWriter.setAppend(true); logWriter.setRetainDays(config.getInteger(Keys.WEB_REQUEST_LOG_RETAIN_DAYS)); - CustomRequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.NCSA_FORMAT); - server.setRequestLog(requestLog); + server.setRequestLog(new WebRequestLog(logWriter)); } } @@ -150,7 +139,7 @@ public class WebServer implements LifecycleObject { } private void initWebApp(ServletContextHandler servletHandler) { - ServletHolder servletHolder = new ServletHolder(DefaultServlet.class); + ServletHolder servletHolder = new ServletHolder(new ModernDefaultServlet(config)); servletHolder.setInitParameter("resourceBase", new File(config.getString(Keys.WEB_PATH)).getAbsolutePath()); servletHolder.setInitParameter("dirAllowed", "false"); if (config.getBoolean(Keys.WEB_DEBUG)) { @@ -202,14 +191,16 @@ public class WebServer implements LifecycleObject { sessionHandler.setSessionCache(sessionCache); } + SessionCookieConfig sessionCookieConfig = servletHandler.getServletContext().getSessionCookieConfig(); + int sessionTimeout = config.getInteger(Keys.WEB_SESSION_TIMEOUT); if (sessionTimeout > 0) { servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout); + sessionCookieConfig.setMaxAge(sessionTimeout); } String sameSiteCookie = config.getString(Keys.WEB_SAME_SITE_COOKIE); if (sameSiteCookie != null) { - SessionCookieConfig sessionCookieConfig = servletHandler.getServletContext().getSessionCookieConfig(); switch (sameSiteCookie.toLowerCase()) { case "lax": sessionCookieConfig.setComment(HttpCookie.SAME_SITE_LAX_COMMENT); diff --git a/src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory b/src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory new file mode 100644 index 000000000..75d628857 --- /dev/null +++ b/src/main/resources/META-INF/services/org.jxls.expression.ExpressionEvaluatorFactory @@ -0,0 +1 @@ +org.traccar.reports.common.ExpressionEvaluatorFactory diff --git a/src/test/java/org/traccar/BaseTest.java b/src/test/java/org/traccar/BaseTest.java index c784150dd..ce19d8ace 100644 --- a/src/test/java/org/traccar/BaseTest.java +++ b/src/test/java/org/traccar/BaseTest.java @@ -32,8 +32,9 @@ public class BaseTest { decoder.setCacheManager(cacheManager); var connectionManager = mock(ConnectionManager.class); var uniqueIdsProvided = new HashSet<Boolean>(); - when(connectionManager.getDeviceSession(any(), any(), any(), any())).thenAnswer(invocation -> { - var mock = new DeviceSession(1L, "", mock(Protocol.class), mock(Channel.class), mock(SocketAddress.class)); + when(connectionManager.getDeviceSession(any(), any(), any(), any(String[].class))).thenAnswer(invocation -> { + var mock = new DeviceSession( + 1L, "", null, mock(Protocol.class), mock(Channel.class), mock(SocketAddress.class)); if (uniqueIdsProvided.isEmpty()) { if (invocation.getArguments().length > 3) { uniqueIdsProvided.add(true); diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java index 5e37f44b9..23ba562f8 100644 --- a/src/test/java/org/traccar/ProtocolTest.java +++ b/src/test/java/org/traccar/ProtocolTest.java @@ -26,11 +26,11 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ProtocolTest extends BaseTest { @@ -112,7 +112,7 @@ public class ProtocolTest extends BaseTest { Object decodedObject = decoder.decode(null, null, object); Position position; if (decodedObject instanceof Collection) { - position = (Position) ((Collection) decodedObject).iterator().next(); + position = (Position) ((Collection<?>) decodedObject).iterator().next(); } else { position = (Position) decodedObject; } @@ -155,11 +155,11 @@ public class ProtocolTest extends BaseTest { private void verifyDecodedList(Object decodedObject, boolean checkLocation, Position expected) { - assertNotNull("list is null", decodedObject); - assertTrue("not a list", decodedObject instanceof List); - assertFalse("list is empty", ((List) decodedObject).isEmpty()); + assertNotNull(decodedObject, "list is null"); + assertTrue(decodedObject instanceof List, "not a list"); + assertFalse(((List<?>) decodedObject).isEmpty(), "list is empty"); - for (Object item : (List) decodedObject) { + for (Object item : (List<?>) decodedObject) { verifyDecodedPosition(item, checkLocation, false, expected); } @@ -167,8 +167,8 @@ public class ProtocolTest extends BaseTest { private void verifyDecodedPosition(Object decodedObject, boolean checkLocation, boolean checkAttributes, Position expected) { - assertNotNull("position is null", decodedObject); - assertTrue("not a position", decodedObject instanceof Position); + assertNotNull(decodedObject, "position is null"); + assertTrue(decodedObject instanceof Position, "not a position"); Position position = (Position) decodedObject; @@ -179,47 +179,46 @@ public class ProtocolTest extends BaseTest { if (expected.getFixTime() != null) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - assertEquals("time", dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime())); + assertEquals(dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime()), "time"); } - assertEquals("valid", expected.getValid(), position.getValid()); - assertEquals("latitude", expected.getLatitude(), position.getLatitude(), 0.00001); - assertEquals("longitude", expected.getLongitude(), position.getLongitude(), 0.00001); + assertEquals(expected.getValid(), position.getValid(), "valid"); + assertEquals(expected.getLatitude(), position.getLatitude(), 0.00001, "latitude"); + assertEquals(expected.getLongitude(), position.getLongitude(), 0.00001, "longitude"); } else { assertNotNull(position.getServerTime()); assertNotNull(position.getFixTime()); - assertTrue("year > 1999", position.getFixTime().after(new Date(915148800000L))); - assertTrue("time < +25 hours", - position.getFixTime().getTime() < System.currentTimeMillis() + 25 * 3600000); + assertTrue(position.getFixTime().after(new Date(915148800000L)), "year > 1999"); + assertTrue(position.getFixTime().getTime() < System.currentTimeMillis() + 25 * 3600000, "time < +25 h"); - assertTrue("latitude >= -90", position.getLatitude() >= -90); - assertTrue("latitude <= 90", position.getLatitude() <= 90); + assertTrue(position.getLatitude() >= -90, "latitude >= -90"); + assertTrue(position.getLatitude() <= 90, "latitude <= 90"); - assertTrue("longitude >= -180", position.getLongitude() >= -180); - assertTrue("longitude <= 180", position.getLongitude() <= 180); + assertTrue(position.getLongitude() >= -180, "longitude >= -180"); + assertTrue(position.getLongitude() <= 180, "longitude <= 180"); } - assertTrue("altitude >= -12262", position.getAltitude() >= -12262); - assertTrue("altitude <= 18000", position.getAltitude() <= 18000); + assertTrue(position.getAltitude() >= -12262, "altitude >= -12262"); + assertTrue(position.getAltitude() <= 18000, "altitude <= 18000"); - assertTrue("speed >= 0", position.getSpeed() >= 0); - assertTrue("speed <= 869", position.getSpeed() <= 869); + assertTrue(position.getSpeed() >= 0, "speed >= 0"); + assertTrue(position.getSpeed() <= 869, "speed <= 869"); - assertTrue("course >= 0", position.getCourse() >= 0); - assertTrue("course <= 360", position.getCourse() <= 360); + assertTrue(position.getCourse() >= 0, "course >= 0"); + assertTrue(position.getCourse() <= 360, "course <= 360"); - assertNotNull("protocol is null", position.getProtocol()); + assertNotNull(position.getProtocol(), "protocol is null"); - assertTrue("deviceId > 0", position.getDeviceId() > 0); + assertTrue(position.getDeviceId() > 0, "deviceId > 0"); } Map<String, Object> attributes = position.getAttributes(); if (checkAttributes) { - assertFalse("no attributes", attributes.isEmpty()); + assertFalse(attributes.isEmpty(), "no attributes"); } if (attributes.containsKey(Position.KEY_INDEX)) { @@ -262,6 +261,10 @@ public class ProtocolTest extends BaseTest { assertTrue(attributes.get(Position.KEY_FUEL_LEVEL) instanceof Number); } + if (attributes.containsKey(Position.KEY_FUEL_USED)) { + assertTrue(attributes.get(Position.KEY_FUEL_USED) instanceof Number); + } + if (attributes.containsKey(Position.KEY_POWER)) { assertTrue(attributes.get(Position.KEY_POWER) instanceof Number); } @@ -331,11 +334,11 @@ public class ProtocolTest extends BaseTest { } private void checkInteger(Object value, int min, int max) { - assertNotNull("value is null", value); - assertTrue("not int or long", value instanceof Integer || value instanceof Long); + assertNotNull(value, "value is null"); + assertTrue(value instanceof Integer || value instanceof Long, "not int or long"); long number = ((Number) value).longValue(); - assertTrue("value too low", number >= min); - assertTrue("value too high", number <= max); + assertTrue(number >= min, "value too low"); + assertTrue(number <= max, "value too high"); } protected void verifyCommand( @@ -344,8 +347,8 @@ public class ProtocolTest extends BaseTest { } protected void verifyFrame(ByteBuf expected, Object object) { - assertNotNull("buffer is null", object); - assertTrue("not a buffer", object instanceof ByteBuf); + assertNotNull(object, "buffer is null"); + assertTrue(object instanceof ByteBuf, "not a buffer"); assertEquals(ByteBufUtil.hexDump(expected), ByteBufUtil.hexDump((ByteBuf) object)); } diff --git a/src/test/java/org/traccar/calendar/CalendarTest.java b/src/test/java/org/traccar/calendar/CalendarTest.java index def67ff76..4106f89a9 100644 --- a/src/test/java/org/traccar/calendar/CalendarTest.java +++ b/src/test/java/org/traccar/calendar/CalendarTest.java @@ -1,7 +1,7 @@ package org.traccar.calendar; import net.fortuna.ical4j.data.ParserException; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.model.Calendar; import java.io.IOException; @@ -11,7 +11,8 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CalendarTest { @@ -45,14 +46,12 @@ public class CalendarTest { calendar.setData(calendarString.getBytes()); DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX"); - Date date = format.parse("2016-12-13 22:59:59+05"); - assertTrue(!calendar.checkMoment(date)); - date = format.parse("2016-12-13 23:00:01+05"); - assertTrue(calendar.checkMoment(date)); + assertFalse(calendar.checkMoment(format.parse("2016-12-13 22:59:59+05"))); + assertTrue(calendar.checkMoment(format.parse("2016-12-13 23:00:01+05"))); + assertTrue(calendar.checkMoment(format.parse("2016-12-13 06:59:59+05"))); + assertFalse(calendar.checkMoment(format.parse("2016-12-13 07:00:01+05"))); - date = format.parse("2016-12-13 06:59:59+05"); - assertTrue(calendar.checkMoment(date)); - date = format.parse("2016-12-13 07:00:01+05"); - assertTrue(!calendar.checkMoment(date)); + var periods = calendar.findPeriods(format.parse("2016-12-13 06:59:59+05")); + assertFalse(periods.isEmpty()); } } diff --git a/src/test/java/org/traccar/config/ConfigTest.java b/src/test/java/org/traccar/config/ConfigTest.java index 8ba6dace6..f48e79acf 100644 --- a/src/test/java/org/traccar/config/ConfigTest.java +++ b/src/test/java/org/traccar/config/ConfigTest.java @@ -1,8 +1,8 @@ package org.traccar.config; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ConfigTest { diff --git a/src/test/java/org/traccar/database/GroupTreeTest.java b/src/test/java/org/traccar/database/GroupTreeTest.java index b547aab60..7c0d478f8 100644 --- a/src/test/java/org/traccar/database/GroupTreeTest.java +++ b/src/test/java/org/traccar/database/GroupTreeTest.java @@ -1,13 +1,13 @@ package org.traccar.database; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.model.Device; import org.traccar.model.Group; import java.util.ArrayList; import java.util.Collection; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GroupTreeTest { diff --git a/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java b/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java index 522958052..f0e354620 100644 --- a/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java +++ b/src/test/java/org/traccar/forward/PositionForwarderUrlTest.java @@ -1,13 +1,13 @@ package org.traccar.forward; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Device; import org.traccar.model.Position; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/org/traccar/geocoder/AddressFormatTest.java b/src/test/java/org/traccar/geocoder/AddressFormatTest.java index 0cc5168ef..b3a248bb3 100644 --- a/src/test/java/org/traccar/geocoder/AddressFormatTest.java +++ b/src/test/java/org/traccar/geocoder/AddressFormatTest.java @@ -1,8 +1,8 @@ package org.traccar.geocoder; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class AddressFormatTest { diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java index ff33b1f1c..ef2dd062d 100644 --- a/src/test/java/org/traccar/geocoder/GeocoderTest.java +++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java @@ -1,13 +1,13 @@ package org.traccar.geocoder; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; import java.util.Locale; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GeocoderTest { @@ -17,7 +17,7 @@ public class GeocoderTest { private final Client client = ClientBuilder.newClient(); - @Ignore + @Disabled @Test public void testGoogle() { Geocoder geocoder = new GoogleGeocoder(client, null, null, 0, new AddressFormat()); @@ -25,7 +25,7 @@ public class GeocoderTest { assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", address); } - @Ignore + @Disabled @Test public void testNominatim() { Geocoder geocoder = new NominatimGeocoder(client, null, null, null, 0, new AddressFormat()); @@ -33,7 +33,7 @@ public class GeocoderTest { assertEquals("35 West 9th Street, NYC, New York, US", address); } - @Ignore + @Disabled @Test public void testGisgraphy() { Geocoder geocoder = new GisgraphyGeocoder(client, null, 0, new AddressFormat()); @@ -41,7 +41,7 @@ public class GeocoderTest { assertEquals("Rue du Jardinet, Paris, ÃŽle-de-France, FR", address); } - @Ignore + @Disabled @Test public void testOpenCage() { Geocoder geocoder = new OpenCageGeocoder( @@ -50,7 +50,7 @@ public class GeocoderTest { assertEquals("Charleston Road, California, US", address); } - @Ignore + @Disabled @Test public void testGeocodeFarm() { Geocoder geocoder = new GeocodeFarmGeocoder(client, null, null, 0, new AddressFormat()); @@ -58,7 +58,7 @@ public class GeocoderTest { assertEquals("Estrella Avenue, Arcadia, California, United States", address); } - @Ignore + @Disabled @Test public void testGeocodeXyz() { Geocoder geocoder = new GeocodeXyzGeocoder(client, null, 0, new AddressFormat()); @@ -66,7 +66,7 @@ public class GeocoderTest { assertEquals("605 ESTRELLA AVE, ARCADIA, California United States of America, US", address); } - @Ignore + @Disabled @Test public void testBan() { Geocoder geocoder = new BanGeocoder(client, 0, new AddressFormat("%f [%d], %c")); @@ -74,15 +74,15 @@ public class GeocoderTest { assertEquals("8 Avenue Gustave Eiffel 75007 Paris [75, Paris, ÃŽle-de-France], FR", address); } - @Ignore + @Disabled @Test public void testHere() { - Geocoder geocoder = new HereGeocoder(client, null, "", "", null, 0, new AddressFormat()); + Geocoder geocoder = new HereGeocoder(client, null, "aDc9qgsCpRbO9ioJIIAXzF6JYU7w8H5O260e9hsGrms", null, 0, new AddressFormat()); String address = geocoder.getAddress(48.8575, 2.2944, null); - assertEquals("6 Avenue Gustave Eiffel, Paris, ÃŽle-de-France, FRA", address); + assertEquals("1 Tour Eiffel, Paris, ÃŽle-de-France, FRA", address); } - @Ignore + @Disabled @Test public void testMapmyIndia() { Geocoder geocoder = new MapmyIndiaGeocoder(client, "", "", 0, new AddressFormat("%f")); @@ -90,7 +90,7 @@ public class GeocoderTest { assertEquals("New Delhi, Delhi. 1 m from India Gate pin-110001 (India)", address); } - @Ignore + @Disabled @Test public void testPositionStack() { Geocoder geocoder = new PositionStackGeocoder(client, "", 0, new AddressFormat("%f")); @@ -98,7 +98,7 @@ public class GeocoderTest { assertEquals("India Gate, New Delhi, India", address); } - @Ignore + @Disabled @Test public void testMapbox() { Geocoder geocoder = new MapboxGeocoder(client, "", 0, new AddressFormat("%f")); @@ -106,7 +106,7 @@ public class GeocoderTest { assertEquals("120 East 13th Street, New York, New York 10003, United States", address); } - @Ignore + @Disabled @Test public void testMapTiler() { Geocoder geocoder = new MapTilerGeocoder(client, "", 0, new AddressFormat()); @@ -114,7 +114,7 @@ public class GeocoderTest { assertEquals("East 13th Street, New York City, New York, United States", address); } - @Ignore + @Disabled @Test public void testGeoapify() { Geocoder geocoder = new GeoapifyGeocoder(client, "", null, 0, new AddressFormat()); diff --git a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java index 9a02cec76..106b041fc 100644 --- a/src/test/java/org/traccar/geofence/GeofenceCircleTest.java +++ b/src/test/java/org/traccar/geofence/GeofenceCircleTest.java @@ -1,12 +1,12 @@ package org.traccar.geofence; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.ParseException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class GeofenceCircleTest { diff --git a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java index 5baecd771..bbf19cc38 100644 --- a/src/test/java/org/traccar/geofence/GeofencePolygonTest.java +++ b/src/test/java/org/traccar/geofence/GeofencePolygonTest.java @@ -1,12 +1,12 @@ package org.traccar.geofence; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.ParseException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class GeofencePolygonTest { diff --git a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java index b7ee14510..9b7bbb7d1 100644 --- a/src/test/java/org/traccar/geofence/GeofencePolylineTest.java +++ b/src/test/java/org/traccar/geofence/GeofencePolylineTest.java @@ -1,15 +1,15 @@ package org.traccar.geofence; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.config.Config; import org.traccar.config.Keys; import org.traccar.model.Geofence; import java.text.ParseException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java index 876b6b688..da5ae3340 100644 --- a/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java +++ b/src/test/java/org/traccar/geolocation/GeolocationProviderTest.java @@ -1,22 +1,22 @@ package org.traccar.geolocation; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.CellTower; import org.traccar.model.Network; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class GeolocationProviderTest extends BaseTest { private final Client client = ClientBuilder.newClient(); - @Ignore + @Disabled @Test public void testMozilla() throws Exception { MozillaGeolocationProvider provider = new MozillaGeolocationProvider(client, null); diff --git a/src/test/java/org/traccar/handler/ComputedAttributesTest.java b/src/test/java/org/traccar/handler/ComputedAttributesTest.java index 2668c4f14..e2af703c2 100644 --- a/src/test/java/org/traccar/handler/ComputedAttributesTest.java +++ b/src/test/java/org/traccar/handler/ComputedAttributesTest.java @@ -1,13 +1,13 @@ package org.traccar.handler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.config.Config; import org.traccar.model.Attribute; import org.traccar.model.Position; import java.util.Date; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ComputedAttributesTest { @@ -41,7 +41,7 @@ public class ComputedAttributesTest { attribute.setExpression("(bitFlag & 4) != 0"); assertEquals(true, handler.computeAttribute(attribute, position)); - attribute.setExpression("if (event == 42) \"lowBattery\""); + attribute.setExpression("event == 42 ? \"lowBattery\" : null"); assertEquals("lowBattery", handler.computeAttribute(attribute, position)); attribute.setExpression("speed > 5 && valid"); diff --git a/src/test/java/org/traccar/handler/DistanceHandlerTest.java b/src/test/java/org/traccar/handler/DistanceHandlerTest.java index a18b14edd..7d2f1e2e3 100644 --- a/src/test/java/org/traccar/handler/DistanceHandlerTest.java +++ b/src/test/java/org/traccar/handler/DistanceHandlerTest.java @@ -1,11 +1,11 @@ package org.traccar.handler; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.config.Config; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; public class DistanceHandlerTest { diff --git a/src/test/java/org/traccar/handler/FilterHandlerTest.java b/src/test/java/org/traccar/handler/FilterHandlerTest.java index a1102da88..36bb84f19 100644 --- a/src/test/java/org/traccar/handler/FilterHandlerTest.java +++ b/src/test/java/org/traccar/handler/FilterHandlerTest.java @@ -1,7 +1,7 @@ package org.traccar.handler; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.config.Config; import org.traccar.config.Keys; @@ -11,8 +11,8 @@ import org.traccar.session.cache.CacheManager; import java.util.Date; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; @@ -23,16 +23,17 @@ public class FilterHandlerTest extends BaseTest { private FilterHandler passingHandler; private FilterHandler filteringHandler; - @Before + @BeforeEach public void passingHandler() { var config = mock(Config.class); when(config.getBoolean(Keys.FILTER_ENABLE)).thenReturn(true); var cacheManager = mock(CacheManager.class); when(cacheManager.getConfig()).thenReturn(config); - passingHandler = new FilterHandler(config, cacheManager, null); + when(cacheManager.getObject(any(), anyLong())).thenReturn(mock(Device.class)); + passingHandler = new FilterHandler(config, cacheManager, null, null); } - @Before + @BeforeEach public void filteringHandler() { var config = mock(Config.class); when(config.getBoolean(Keys.FILTER_ENABLE)).thenReturn(true); @@ -50,7 +51,7 @@ public class FilterHandlerTest extends BaseTest { var cacheManager = mock(CacheManager.class); when(cacheManager.getConfig()).thenReturn(config); when(cacheManager.getObject(any(), anyLong())).thenReturn(mock(Device.class)); - filteringHandler = new FilterHandler(config, cacheManager, null); + filteringHandler = new FilterHandler(config, cacheManager, null, null); } private Position createPosition(Date time, boolean valid, double speed) { @@ -71,18 +72,18 @@ public class FilterHandlerTest extends BaseTest { Position position = createPosition(new Date(), true, 10); - assertNotNull(filteringHandler.handlePosition(position)); - assertNotNull(passingHandler.handlePosition(position)); + assertFalse(filteringHandler.filter(position)); + assertFalse(passingHandler.filter(position)); position = createPosition(new Date(Long.MAX_VALUE), true, 10); - assertNull(filteringHandler.handlePosition(position)); - assertNotNull(passingHandler.handlePosition(position)); + assertTrue(filteringHandler.filter(position)); + assertFalse(passingHandler.filter(position)); position = createPosition(new Date(), false, 10); - assertNull(filteringHandler.handlePosition(position)); - assertNotNull(passingHandler.handlePosition(position)); + assertTrue(filteringHandler.filter(position)); + assertFalse(passingHandler.filter(position)); } @@ -92,7 +93,7 @@ public class FilterHandlerTest extends BaseTest { Position position = createPosition(new Date(), true, 0); position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); - assertNotNull(filteringHandler.handlePosition(position)); + assertFalse(filteringHandler.filter(position)); } diff --git a/src/test/java/org/traccar/handler/MotionHandlerTest.java b/src/test/java/org/traccar/handler/MotionHandlerTest.java index 93fd16206..10cdf6a90 100644 --- a/src/test/java/org/traccar/handler/MotionHandlerTest.java +++ b/src/test/java/org/traccar/handler/MotionHandlerTest.java @@ -1,10 +1,15 @@ package org.traccar.handler; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.model.Device; import org.traccar.model.Position; -import org.traccar.reports.common.TripsConfig; +import org.traccar.session.cache.CacheManager; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -13,10 +18,13 @@ public class MotionHandlerTest { @Test public void testCalculateMotion() { - TripsConfig tripsConfig = mock(TripsConfig.class); - when(tripsConfig.getSpeedThreshold()).thenReturn(0.01); + var cacheManager = mock(CacheManager.class); + when(cacheManager.getObject(eq(Device.class), anyLong())).thenReturn(mock(Device.class)); + var config = mock(Config.class); + when(config.getString(Keys.EVENT_MOTION_SPEED_THRESHOLD.getKey())).thenReturn("0.01"); + when(cacheManager.getConfig()).thenReturn(config); - MotionHandler motionHandler = new MotionHandler(tripsConfig); + MotionHandler motionHandler = new MotionHandler(cacheManager); Position position = motionHandler.handlePosition(new Position()); diff --git a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java index 550a93da3..66dc55c85 100644 --- a/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/AlertEventHandlerTest.java @@ -1,6 +1,6 @@ package org.traccar.handler.events; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.config.Config; import org.traccar.model.Event; @@ -9,8 +9,8 @@ import org.traccar.session.cache.CacheManager; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; public class AlertEventHandlerTest extends BaseTest { diff --git a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java index 4997a0e0f..bc24e42f5 100644 --- a/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/CommandResultEventHandlerTest.java @@ -1,14 +1,14 @@ package org.traccar.handler.events; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.Event; import org.traccar.model.Position; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class CommandResultEventHandlerTest extends BaseTest { diff --git a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java index 84898bea0..972932df4 100644 --- a/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/IgnitionEventHandlerTest.java @@ -1,6 +1,6 @@ package org.traccar.handler.events; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.Event; import org.traccar.model.Position; @@ -8,7 +8,7 @@ import org.traccar.session.cache.CacheManager; import java.util.Map; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; public class IgnitionEventHandlerTest extends BaseTest { diff --git a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java index aa2d0bbe3..661336d76 100644 --- a/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/MaintenanceEventHandlerTest.java @@ -1,19 +1,20 @@ package org.traccar.handler.events; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.Maintenance; import org.traccar.model.Position; import org.traccar.session.cache.CacheManager; -import java.util.Arrays; import java.util.Date; +import java.util.Set; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.anyLong; public class MaintenanceEventHandlerTest extends BaseTest { @@ -29,7 +30,7 @@ public class MaintenanceEventHandlerTest extends BaseTest { var maintenance = mock(Maintenance.class); when(maintenance.getType()).thenReturn(Position.KEY_TOTAL_DISTANCE); - var maintenances = Arrays.asList(maintenance); + var maintenances = Set.of(maintenance); var cacheManager = mock(CacheManager.class); when(cacheManager.getDeviceObjects(anyLong(), eq(Maintenance.class))).thenReturn(maintenances); @@ -48,12 +49,12 @@ public class MaintenanceEventHandlerTest extends BaseTest { assertTrue(eventHandler.analyzePosition(position).isEmpty()); lastPosition.set(Position.KEY_TOTAL_DISTANCE, 9999); - position.set(Position.KEY_TOTAL_DISTANCE, 10001); - assertTrue(eventHandler.analyzePosition(position).size() == 1); + position.set(Position.KEY_TOTAL_DISTANCE, 10001); + assertEquals(1, eventHandler.analyzePosition(position).size()); lastPosition.set(Position.KEY_TOTAL_DISTANCE, 11999); - position.set(Position.KEY_TOTAL_DISTANCE, 12001); - assertTrue(eventHandler.analyzePosition(position).size() == 1); + position.set(Position.KEY_TOTAL_DISTANCE, 12001); + assertEquals(1, eventHandler.analyzePosition(position).size()); } diff --git a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java index b77676dc8..c61ae913d 100644 --- a/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/MotionEventHandlerTest.java @@ -1,6 +1,6 @@ package org.traccar.handler.events; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.Event; import org.traccar.model.Position; @@ -13,8 +13,8 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class MotionEventHandlerTest extends BaseTest { @@ -36,7 +36,7 @@ public class MotionEventHandlerTest extends BaseTest { @Test public void testMotionWithPosition() throws ParseException { - TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false, false, 0.01); + TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false); MotionState state = new MotionState(); @@ -63,7 +63,7 @@ public class MotionEventHandlerTest extends BaseTest { @Test public void testMotionFluctuation() throws ParseException { - TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false, false, 0.01); + TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, false); MotionState state = new MotionState(); @@ -94,7 +94,7 @@ public class MotionEventHandlerTest extends BaseTest { @Test public void testStopWithPositionIgnition() throws ParseException { - TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, true, false, 0.01); + TripsConfig tripsConfig = new TripsConfig(500, 300000, 300000, 0, true); MotionState state = new MotionState(); state.setMotionStreak(true); diff --git a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java index ee18ee052..97d929551 100644 --- a/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java +++ b/src/test/java/org/traccar/handler/events/OverspeedEventHandlerTest.java @@ -1,6 +1,6 @@ package org.traccar.handler.events; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.model.Event; import org.traccar.model.Position; @@ -12,9 +12,9 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class OverspeedEventHandlerTest extends BaseTest { @@ -35,14 +35,14 @@ public class OverspeedEventHandlerTest extends BaseTest { private void testOverspeedWithPosition(long geofenceId) throws ParseException { OverspeedState state = new OverspeedState(); - OverspeedProcessor.updateState(state, position("2017-01-01 00:00:00", 50), 40, 15000, geofenceId); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:00", 50), 40, 1, 15000, geofenceId); assertNull(state.getEvent()); verifyState(state, true, geofenceId); - OverspeedProcessor.updateState(state, position("2017-01-01 00:00:10", 55), 40, 15000, geofenceId); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:10", 55), 40, 1, 15000, geofenceId); assertNull(state.getEvent()); - OverspeedProcessor.updateState(state, position("2017-01-01 00:00:20", 55), 40, 15000, geofenceId); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:20", 55), 40, 1, 15000, geofenceId); assertNotNull(state.getEvent()); assertEquals(Event.TYPE_DEVICE_OVERSPEED, state.getEvent().getType()); assertEquals(55, state.getEvent().getDouble("speed"), 0.1); @@ -50,11 +50,11 @@ public class OverspeedEventHandlerTest extends BaseTest { assertEquals(geofenceId, state.getEvent().getGeofenceId()); verifyState(state, true, 0); - OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 55), 40, 15000, geofenceId); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 55), 40, 1, 15000, geofenceId); assertNull(state.getEvent()); verifyState(state, true, 0); - OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 30), 40, 15000, geofenceId); + OverspeedProcessor.updateState(state, position("2017-01-01 00:00:30", 30), 40, 1, 15000, geofenceId); assertNull(state.getEvent()); verifyState(state, false, 0); } diff --git a/src/test/java/org/traccar/helper/BcdUtilTest.java b/src/test/java/org/traccar/helper/BcdUtilTest.java index 86a32f725..440cd90d4 100644 --- a/src/test/java/org/traccar/helper/BcdUtilTest.java +++ b/src/test/java/org/traccar/helper/BcdUtilTest.java @@ -1,9 +1,9 @@ package org.traccar.helper; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BcdUtilTest { diff --git a/src/test/java/org/traccar/helper/BitBufferTest.java b/src/test/java/org/traccar/helper/BitBufferTest.java index c2abad36d..3b3521213 100644 --- a/src/test/java/org/traccar/helper/BitBufferTest.java +++ b/src/test/java/org/traccar/helper/BitBufferTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BitBufferTest { diff --git a/src/test/java/org/traccar/helper/BitUtilTest.java b/src/test/java/org/traccar/helper/BitUtilTest.java index 90431bf55..803c327bc 100644 --- a/src/test/java/org/traccar/helper/BitUtilTest.java +++ b/src/test/java/org/traccar/helper/BitUtilTest.java @@ -1,10 +1,10 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BitUtilTest { diff --git a/src/test/java/org/traccar/helper/BufferUtilTest.java b/src/test/java/org/traccar/helper/BufferUtilTest.java index 0196cef9d..707e419ec 100644 --- a/src/test/java/org/traccar/helper/BufferUtilTest.java +++ b/src/test/java/org/traccar/helper/BufferUtilTest.java @@ -2,11 +2,11 @@ package org.traccar.helper; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class BufferUtilTest { diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java index 248f4dcae..51f62aba0 100644 --- a/src/test/java/org/traccar/helper/ChecksumTest.java +++ b/src/test/java/org/traccar/helper/ChecksumTest.java @@ -2,12 +2,12 @@ package org.traccar.helper; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ChecksumTest { diff --git a/src/test/java/org/traccar/helper/DateBuilderTest.java b/src/test/java/org/traccar/helper/DateBuilderTest.java index b6323cc1d..35797a3ef 100644 --- a/src/test/java/org/traccar/helper/DateBuilderTest.java +++ b/src/test/java/org/traccar/helper/DateBuilderTest.java @@ -1,13 +1,13 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DateBuilderTest { diff --git a/src/test/java/org/traccar/helper/DateUtilTest.java b/src/test/java/org/traccar/helper/DateUtilTest.java index ec42e71ae..b5a4b1eab 100644 --- a/src/test/java/org/traccar/helper/DateUtilTest.java +++ b/src/test/java/org/traccar/helper/DateUtilTest.java @@ -1,13 +1,13 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DateUtilTest { diff --git a/src/test/java/org/traccar/helper/DistanceCalculatorTest.java b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java index a7457b6c4..676fda5ea 100644 --- a/src/test/java/org/traccar/helper/DistanceCalculatorTest.java +++ b/src/test/java/org/traccar/helper/DistanceCalculatorTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DistanceCalculatorTest { diff --git a/src/test/java/org/traccar/helper/LogTest.java b/src/test/java/org/traccar/helper/LogTest.java index ef33c32ba..a264896b5 100644 --- a/src/test/java/org/traccar/helper/LogTest.java +++ b/src/test/java/org/traccar/helper/LogTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LogTest { diff --git a/src/test/java/org/traccar/helper/ObdDecoderTest.java b/src/test/java/org/traccar/helper/ObdDecoderTest.java index d5071bd51..2233ab5b3 100644 --- a/src/test/java/org/traccar/helper/ObdDecoderTest.java +++ b/src/test/java/org/traccar/helper/ObdDecoderTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ObdDecoderTest { diff --git a/src/test/java/org/traccar/helper/PatternBuilderTest.java b/src/test/java/org/traccar/helper/PatternBuilderTest.java index 4c76bc463..a8657a2e7 100644 --- a/src/test/java/org/traccar/helper/PatternBuilderTest.java +++ b/src/test/java/org/traccar/helper/PatternBuilderTest.java @@ -1,8 +1,8 @@ package org.traccar.helper; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PatternBuilderTest { diff --git a/src/test/java/org/traccar/helper/PatternUtilTest.java b/src/test/java/org/traccar/helper/PatternUtilTest.java index 77660078a..ff8efe773 100644 --- a/src/test/java/org/traccar/helper/PatternUtilTest.java +++ b/src/test/java/org/traccar/helper/PatternUtilTest.java @@ -1,13 +1,13 @@ package org.traccar.helper; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PatternUtilTest { - @Ignore + @Disabled @Test public void testCheckPattern() { diff --git a/src/test/java/org/traccar/helper/ServletHelperTest.java b/src/test/java/org/traccar/helper/ServletHelperTest.java deleted file mode 100644 index e419b6491..000000000 --- a/src/test/java/org/traccar/helper/ServletHelperTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.traccar.helper; - -import org.apache.struts.mock.MockHttpServletRequest; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -public class ServletHelperTest { - - @Test - public void testRetrieveRemoteAddressProxyMultiple() { - MockRequest request = new MockRequest(); - request.setRemoteAddress("147.120.1.5"); - request.addHeader("X-FORWARDED-FOR", "231.23.45.65, 10.20.10.33, 10.20.20.34"); - - assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request)); - } - - @Test - public void testRetrieveRemoteAddressProxySingle() { - MockRequest request = new MockRequest(); - request.setRemoteAddress("147.120.1.5"); - request.addHeader("X-FORWARDED-FOR", "231.23.45.65"); - - assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request)); - } - - @Test - public void testRetrieveRemoteAddressNoProxy() { - MockRequest request = new MockRequest(); - request.setRemoteAddress("231.23.45.65"); - - assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request)); - } - - private final static class MockRequest extends MockHttpServletRequest { - - private String remoteAddress; - - private Map<String, String> headers = new HashMap<>(); - - public void setRemoteAddress(String remoteAddress) { - this.remoteAddress = remoteAddress; - } - - public void addHeader(String name, String value) { - headers.put(name, value); - } - - @Override - public String getHeader(String name) { - return headers.get(name); - } - - @Override - public String getRemoteAddr() { - return remoteAddress; - } - - } - -} diff --git a/src/test/java/org/traccar/helper/WebHelperTest.java b/src/test/java/org/traccar/helper/WebHelperTest.java new file mode 100644 index 000000000..da18be11e --- /dev/null +++ b/src/test/java/org/traccar/helper/WebHelperTest.java @@ -0,0 +1,39 @@ +package org.traccar.helper; + +import org.junit.jupiter.api.Test; + +import jakarta.servlet.http.HttpServletRequest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class WebHelperTest { + + @Test + public void testRetrieveRemoteAddressProxyMultiple() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getRemoteAddr()).thenReturn("147.120.1.5"); + when(request.getHeader("X-FORWARDED-FOR")).thenReturn("231.23.45.65, 10.20.10.33, 10.20.20.34"); + + assertEquals("231.23.45.65", WebHelper.retrieveRemoteAddress(request)); + } + + @Test + public void testRetrieveRemoteAddressProxySingle() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getRemoteAddr()).thenReturn("147.120.1.5"); + when(request.getHeader("X-FORWARDED-FOR")).thenReturn("231.23.45.65"); + + assertEquals("231.23.45.65", WebHelper.retrieveRemoteAddress(request)); + } + + @Test + public void testRetrieveRemoteAddressNoProxy() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getRemoteAddr()).thenReturn("231.23.45.65"); + + assertEquals("231.23.45.65", WebHelper.retrieveRemoteAddress(request)); + } + +} diff --git a/src/test/java/org/traccar/notification/NotificiationMailTest.java b/src/test/java/org/traccar/notification/NotificiationMailTest.java index b82bec02e..ccc8cc47d 100644 --- a/src/test/java/org/traccar/notification/NotificiationMailTest.java +++ b/src/test/java/org/traccar/notification/NotificiationMailTest.java @@ -1,13 +1,13 @@ package org.traccar.notification; -import org.junit.Ignore; -import org.junit.Test; - -import javax.mail.Message; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import jakarta.mail.Message; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; import java.util.Properties; public class NotificiationMailTest { @@ -25,7 +25,7 @@ public class NotificiationMailTest { private static final int PORT = 25; - @Ignore + @Disabled @Test public void test() throws Exception { diff --git a/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java index 02d42447e..bd50e1d14 100644 --- a/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AdmFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AdmFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java index 810d53bf7..f09a42ba8 100644 --- a/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AdmProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AdmProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java index 79f37c4e4..26f59015d 100644 --- a/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/AdmProtocolEncoderTest.java @@ -16,11 +16,11 @@ */ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class AdmProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java index 36ea3d361..b322bbbc7 100644 --- a/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AisProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AisProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java index 585e02c02..84108160a 100644 --- a/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AlematicsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AlematicsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java index 5e0923e9c..aec26c0c1 100644 --- a/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AnytrekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AnytrekProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java index 369c13115..6aef8409b 100644 --- a/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ApelProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ApelProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java index b055221f2..316e897a7 100644 --- a/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AplicomFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class AplicomFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java index 4b25830f8..b4b5da0ce 100644 --- a/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AplicomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AplicomProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java index e821c9dba..d0dd368e7 100644 --- a/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AppelloProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AppelloProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java index 38c5d3b6d..fa9cc9851 100644 --- a/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AquilaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AquilaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java index fd65ce585..9f567c87b 100644 --- a/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Ardi01ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Ardi01ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java index b07dff150..e3a2658a8 100644 --- a/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArknavProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArknavProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java index 9686c694b..c81ef1684 100644 --- a/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArknavX8ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArknavX8ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java index 515f7f7b2..42e1ab477 100644 --- a/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java index f96da5203..e38e63755 100644 --- a/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArnaviBinaryProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java index 92ca5d2b0..4997e535d 100644 --- a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArnaviFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java index e27367119..ed9fd0cfe 100644 --- a/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ArnaviTextProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java index 2d8798dff..3dabcac5d 100644 --- a/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AstraProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AstraProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java index 773a8f7f5..c83a5fd4e 100644 --- a/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/At2000FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class At2000FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java index 8c32289f1..718542e97 100644 --- a/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/At2000ProtocolDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class At2000ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java index 958814e53..7b2334919 100644 --- a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AtrackFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java index b6b09fc25..be6fb5c45 100644 --- a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AtrackProtocolDecoderTest extends ProtocolTest { @@ -43,6 +43,12 @@ public class AtrackProtocolDecoderTest extends ProtocolTest { decoder.setCustom(true); + verifyPositions(decoder, binary( + "4050d78502e01d29000312fa45441d6d647d8e67647d8e67647eef190205437c021846e6001a020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010c0000000000000000000000000000000000000000000000000e647d8e85647d8e86647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010c0000000000000000000000000000000000000000000000000e647d8ea3647d8ea4647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010b0000000000000000000000000000000000000000000000000e647d8ec1647d8ec2647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010a0000000000000000000000000000000000000000000000000e647d8edf647d8ee0647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010a0000000000000000000000000000000000000000000000000e647d8efd647d8efe647eef190205437c021846e60019020002d7f000070000000000000007d007d000254349254d5625525025564e254d4625454c25545225455425464c254d4c25464325534d25494e3025484125484225484325415400010a0000000000000000000000000000000000000000000000000e")); + + verifyPositions(decoder, binary( + "405099280272000300014399e3f93d136438abdf644083f56440842afb2711c701b9eaee0067020003e0bb03de0000000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c3400000000000000000000000000000000000000000000930025000000000000000000000000000000006438abdf644083f76440842afb2711c701b9eaee0067710003e0bb03de0100000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c3400000000000000000000000000000000000000000000950025000000000000000000000000000000006438abdf644083f76440842afb2711c701b9eaee0067840003e0bb03de0100000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c3400000000000000000000000000000000000000000000950025000000000000000000000000000000006438abdf644083f86440842afb2711c701b9eaee0067760003e0bb03de0100000000000007d007d00025434925454c25455425464325464c255250254d4c25534d25545225494125454f25564e254d56254256254548255a4c33255a4f3134255a4f3131255a4f3130255a4f32255a4c340000000000000000000000000000000000000000000095002500000000000000000000000000000000")); + verifyPositions(decoder, buffer( "@P,7A66,153,9022,863003048505515,20210207000103,20210207000103,20210207000103,103939276,1348903,97,2,5628,8,0,0,0,0,,2000,2000,\u001a,%CI%BC,3:224:F128833445E6002C09C6\r\n")); diff --git a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java index 368f7ed4c..d2ef74c02 100644 --- a/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AuroProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AuroProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java index 30152e94c..f46fe6a3c 100644 --- a/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AustinNbProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AustinNbProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java index 8e17d5673..f0c9c17bd 100644 --- a/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AutoFonProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; diff --git a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java index 7f837451c..be4b38a14 100644 --- a/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AutoGradeProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AutoGradeProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java index 51bbd0d8c..3b2f2c9b7 100644 --- a/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AutoTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class AutoTrackProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java index 7f41d6b47..7d44e1b15 100644 --- a/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/AvemaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java index 1da432cea..ada32f8ee 100644 --- a/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Avl301ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; diff --git a/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java index ea3b38e7d..84b09e7b7 100644 --- a/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class B2316ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java b/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java index e5a442f2f..f25daca76 100644 --- a/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BceFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class BceFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java index 1d980b7e5..fdfb721c5 100644 --- a/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BceProtocolDecoderTest.java @@ -1,13 +1,13 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; public class BceProtocolDecoderTest extends ProtocolTest { - @Ignore + @Disabled @Test public void testDecodeFail() throws Exception { diff --git a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java index bfaa3ccd8..93c05fad6 100644 --- a/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/BceProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java index 2fb93f2a9..7ec070615 100644 --- a/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BlackKiteProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class BlackKiteProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java index 1de542e24..33496fada 100644 --- a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java index 0a19eb3b0..d2af8282a 100644 --- a/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BoxProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class BoxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java index ae4c47229..fd3561d18 100644 --- a/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/BstplProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class BstplProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java index 28876075d..0dc6529e1 100644 --- a/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/C2stekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class C2stekProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java index 1a8431f23..932facaee 100644 --- a/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CalAmpProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CalAmpProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java index d12d4aa9f..e5791c706 100644 --- a/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CarTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CarTrackProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java index 71137cacf..d5e60e69c 100644 --- a/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CarscopProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CarscopProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java index 1add623b7..52c6a86a6 100644 --- a/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CastelProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -12,6 +12,14 @@ public class CastelProtocolDecoderTest extends ProtocolTest { var decoder = inject(new CastelProtocolDecoder(null)); verifyAttribute(decoder, binary( + "40404700043231335732303139303033353400000000000000400BBE723A5DEF723A5D000000000000000000000000000000000000030100011900030001012603030145C90D0A"), + Position.KEY_DTCS, "P0326"); + + verifyAttribute(decoder, binary( + "40404500033231334c323031373030303432320000000000004006e1ad205bf1ad205b48510f000000000050160000000000020400053f007c000083040001511346160d0a"), + Position.KEY_DTCS, "P1351"); + + verifyAttribute(decoder, binary( "40403a00043231334744503230313830323133343300000000a002000001000001012011004d414c43333831434d4b4d353637313438c8fc0d0a"), Position.KEY_RESULT, "MALC381CMKM567148"); diff --git a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java index 9b3a285da..e3e59b7e7 100644 --- a/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/CastelProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java index 4022d688b..dd8b812fa 100644 --- a/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CautelaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CautelaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java index c266dd1f4..aae9b434d 100644 --- a/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CellocatorFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CellocatorFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java index 27cad39a3..01dd9f4cb 100644 --- a/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CellocatorProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java index bb5d4979d..4d5c8cfe4 100644 --- a/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/CellocatorProtocolEncoderTest.java @@ -1,13 +1,13 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class CellocatorProtocolEncoderTest extends ProtocolTest { - @Ignore + @Disabled @Test public void testEncode() throws Exception { diff --git a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java index 835ab5fe5..df5141e44 100644 --- a/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CguardProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CguardProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java index 0c1fc1f5d..1f99e02e7 100644 --- a/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CityeasyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; diff --git a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java index 6da9856c8..0b97ed9c8 100644 --- a/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/CityeasyProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java index 6ac2ae01d..659c5aa9c 100644 --- a/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ContinentalProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ContinentalProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java index f95bfb54d..6e3ce2481 100644 --- a/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/CradlepointProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class CradlepointProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java index c63775858..6219b3da4 100644 --- a/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DingtekFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DingtekFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java index 7e5f68e05..3131ee1df 100644 --- a/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DingtekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DingtekProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java index 3faccd7ea..a9b80664c 100644 --- a/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DishaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DishaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java index 99760546f..c2449ea91 100644 --- a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DmtHttpProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java index 9c14a1ebe..6919f8ed8 100644 --- a/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DmtProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DmtProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java index b9e3da67d..b24f30965 100644 --- a/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DolphinProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DolphinProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java new file mode 100644 index 000000000..26de75141 --- /dev/null +++ b/src/test/java/org/traccar/protocol/DraginoProtocolDecoderTest.java @@ -0,0 +1,20 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Position; + +public class DraginoProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new DraginoProtocolDecoder(null)); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("{\"end_device_ids\":{\"device_id\":\"eui-a840412b81874509\",\"application_ids\":{\"application_id\":\"teast12344321\"},\"dev_eui\":\"A840412B81874509\",\"join_eui\":\"A840410000000102\",\"dev_addr\":\"260BB59A\"},\"correlation_ids\":[\"gs:uplink:01HDTGK4QWSRX0AMP8E6R1R809\"],\"received_at\":\"2023-10-28T06:47:23.853335601Z\",\"uplink_message\":{\"session_key_id\":\"AYt1CUoFjSPmZ+/Hf8bbng==\",\"f_port\":2,\"f_cnt\":1,\"frm_payload\":\"AAAAAAAAAAAPoiACpwEQ\",\"decoded_payload\":{\"ALARM_status\":\"FALSE\",\"BatV\":4.002,\"Hum\":67.9,\"LON\":\"ON\",\"Latitude\":0,\"Location\":0,\"Longitude\":0,\"MD\":0,\"Tem\":27.2},\"rx_metadata\":[{\"gateway_ids\":{\"gateway_id\":\"eui-a840411b7c4c4150\",\"eui\":\"A840411B7C4C4150\"},\"time\":\"2023-10-28T06:47:23.522309Z\",\"timestamp\":2618357987,\"rssi\":-101,\"channel_rssi\":-101,\"snr\":8.5,\"uplink_token\":\"CiIKIAoUZXVpLWE4NDA0MTFiN2M0YzQxNTASCKhAQRt8TEFQEOPxw+AJGgwI+9zyqQYQ+LGJswIguO2wkpqnNg==\",\"received_at\":\"2023-10-28T06:47:23.643979512Z\"},{\"gateway_ids\":{\"gateway_id\":\"test123123\",\"eui\":\"A840411D178C4150\"},\"time\":\"2023-10-28T06:47:23.522842Z\",\"timestamp\":3064971227,\"rssi\":-78,\"channel_rssi\":-78,\"snr\":8.8,\"uplink_token\":\"ChgKFgoKdGVzdDEyMzEyMxIIqEBBHReMQVAQ2/++tQsaDAj73PKpBhDyj+e1AiD43pX0ma44\",\"received_at\":\"2023-10-28T06:47:23.649709554Z\"},{\"gateway_ids\":{\"gateway_id\":\"eui-b8ea26fdfe2d5d28\",\"eui\":\"B8EA26FDFE2D5D28\"},\"time\":\"2023-10-28T06:47:23.536703Z\",\"timestamp\":2967400725,\"rssi\":-38,\"channel_rssi\":-38,\"snr\":14.2,\"frequency_offset\":\"1061\",\"uplink_token\":\"CiIKIAoUZXVpLWI4ZWEyNmZkZmUyZDVkMjgSCLjqJv3+LV0oEJXi+4YLGgwI+9zyqQYQxqentwIgiPT2tq60NQ==\",\"received_at\":\"2023-10-28T06:47:23.532394512Z\"},{\"gateway_ids\":{\"gateway_id\":\"eu868-1\",\"eui\":\"A840411B7E5E1868\"},\"time\":\"2023-10-28T06:47:23.526195Z\",\"timestamp\":3486064627,\"rssi\":-35,\"channel_rssi\":-35,\"snr\":9,\"uplink_token\":\"ChUKEwoHZXU4NjgtMRIIqEBBG35eGGgQ87+k/gwaDAj73PKpBhDZzuW4AiC4mpPNusM1\",\"received_at\":\"2023-10-28T06:47:23.537018603Z\"}],\"settings\":{\"data_rate\":{\"lora\":{\"bandwidth\":125000,\"spreading_factor\":7,\"coding_rate\":\"4/5\"}},\"frequency\":\"868100000\",\"timestamp\":2618357987,\"time\":\"2023-10-28T06:47:23.522309Z\"},\"received_at\":\"2023-10-28T06:47:23.644805734Z\",\"consumed_airtime\":\"0.066816s\",\"network_ids\":{\"net_id\":\"000013\",\"ns_id\":\"EC656E0000000181\",\"tenant_id\":\"ttn\",\"cluster_id\":\"eu1\",\"cluster_address\":\"eu1.cloud.thethings.network\"}}}"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java index 7e3ec0706..b0c90ef7c 100644 --- a/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Dsf22FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java index 2e52b950d..dbf6f782c 100644 --- a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Dsf22ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java index f74a40d13..495e20e8e 100644 --- a/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DualcamFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java index a61f20c13..e1c31fc47 100644 --- a/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DualcamProtocolDecoderTest extends ProtocolTest { @@ -14,6 +14,9 @@ public class DualcamProtocolDecoderTest extends ProtocolTest { "000000050001403a4abaa31444000400")); verifyNull(decoder, binary( + "000d001e64736d2f706963747572652f3233313032302f3233313435322e6a706700")); + + verifyNull(decoder, binary( "00010006000000110000")); verifyNull(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java index 1cdd82664..05b2757ad 100644 --- a/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/DwayProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class DwayProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java index ef319449e..d40463c1e 100644 --- a/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EasyTrackProtocolDecoderTest extends ProtocolTest { @@ -10,6 +10,9 @@ public class EasyTrackProtocolDecoderTest extends ProtocolTest { var decoder = inject(new EasyTrackProtocolDecoder(null)); + verifyAttributes(decoder, text( + "*ET,358999999999916,OB,BD$V14.2;R08258;S166;P058.4;O079.2;C025;L081.5;XM091.393;M722379;F352.956;T0037184;A01;B00;D00;GX3;GY-6;GZ-268;@4#")); + verifyNotNull(decoder, text( "*ET,354522180593498,JZ,0,20222,262,724,4#")); diff --git a/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java index 1e1934435..a2a720263 100644 --- a/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/EasyTrackProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EasyTrackProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java index ec1467c09..6e2c41a7f 100644 --- a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java index 380888843..300a1c5b6 100644 --- a/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/EelinkProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java index dde71d1e7..af5e77116 100644 --- a/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EgtsFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EgtsFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java index 0f5a40605..aa0d666b1 100644 --- a/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EgtsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EgtsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java index 2d2a211c3..c53d65ceb 100644 --- a/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EnforaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EnforaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java index 0134e8052..5f1d152f6 100644 --- a/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EnnfuProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EnnfuProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java index 50db6c743..68a3ee9e3 100644 --- a/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EnvotechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EnvotechProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java index 2d9b19b70..4cccff878 100644 --- a/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EsealProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EsealProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java index 3ea8de5d6..e87a56b67 100644 --- a/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/EsealProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EsealProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java index f808cd4a8..830ee4df0 100644 --- a/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EskyFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class EskyFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java index 678007f5c..feec3ebfb 100644 --- a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java index 36a26bbe3..ccd7d4d20 100644 --- a/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ExtremTracProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ExtremTracProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java index 417c49de5..de320778a 100644 --- a/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FifotrackFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java index ec08b432c..77778d885 100644 --- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,6 +11,13 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest { var decoder = inject(new FifotrackProtocolDecoder(null)); + verifyAttributes(decoder, buffer( + "$$159,866344056951341,399D,A03,,230716222659,240|8|2724|20EEF33,4.20,100,003E,1,AE233FC0D2E0:-65|3E286D5FB6E8:-65|28BD890A4A0E:-67|8ED81B5DFC3A:-70|8AD81B5DFC3A:-70*5F")); + + verifyAttribute(decoder, buffer( + "$$99,865413050150407,7F,A03,,230626072722,460|0|25FC|AC2AB0B,3.74,52,0019,0,A,0,13,22.643466,114.018211*74"), + Position.KEY_SATELLITES, 13); + verifyPosition(decoder, buffer( "$$95,866104023192332,1,A03,,210414055249,460|0|25FC|104C,4.18,100,000F,0,A,2,9,22.643175,114.018150*75")); diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java index c3352f510..643c9202d 100644 --- a/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/FifotrackProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class FifotrackProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java new file mode 100644 index 000000000..3cd74766d --- /dev/null +++ b/src/test/java/org/traccar/protocol/FleetGuideProtocolDecoderTest.java @@ -0,0 +1,33 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class FleetGuideProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new FleetGuideProtocolDecoder(null)); + + verifyNull(decoder, binary( + "5300188f8a020001f36a")); + + verifyPositions(decoder, false, binary( + "5322188f8a0200140700f355831a83ec06030c008065052c06ffffffff033006808001180003200100005ec0")); + + verifyPositions(decoder, binary( + "539e598f8a020003020700e378351ac39f040c04ffa92e806a28a13b1f00b638030c000067052c06ffffffff033006808001180003200100000700e478351ac40204303dab2e80cb27a13b1c00ac40021b30e778351ac502043082a72e8054020530bf30021b30e978351ac69f0d0c0433a72e80c123a13b2000df3807002579351ac79f06020a170000df28021b476179351ac8020f30021c81279d79351ac9020f30021c8207d979351aca020f30021c8157157a351acb022b8140517a351acc022b608d7a351acd022b60c97a351ace022b30057b351acf022b30417b351ad0022b307d7b351ad1020f3048021b30b97b351ad2020f3070030c004066021630f57b351ad30213841080021730317c351ad4021330c00217306d7c351ad5020f3050021b30a97c351ad602138170021830e57c351ad7020f3058021b30217d351ad8021385500218305d7d351ad9022b8110997d351ada022b8170d57d351adb022b30117e351adc020f3068030ce184680216304d7e351add020f3060030ce34469021630897e351ade021260e4021830c57e351adf021230e5021830017f351ae002293022f2")); + + verifyPositions(decoder, binary( + "538958235a02008408070027398f1a7da3040c0485c7437f9db8a635ca016189030c76a567052c06bd05ffff033006138009340f69fb0080008000800118010320018005070029398f1a7e08043b39f0437f14b9a635cb010288030c7108233b2b398f1a7f08043bec18447f7fbca6357c010b8008263b2d398f1a8008043be53d447f61c0a63562011480030c7708213b6f3b")); + + verifyPositions(decoder, binary( + "53361886b60e00540700cf3b8f1a838d060e041dc683a1b672a04b0000ba885d00030c0ea567052c06ffffffff03300680800118000720070000000000006bc3")); + + verifyPositions(decoder, binary( + "53dd581fc10e0094080700373d8f1a5e2f090e04cbb6c2a3d3078f4b5d023090b500030cac6567052c06ffffffff0330068080054810fdfdfdff0548121bf0000005481321d4830005481454cf08040e15eee20100054816770308040e171c0208040718281f0804071984000804071a583a000001180107200781050000000007003b3d8f1a5f2f040e04dcfbc2a3b15a8f4b67023890b4081c7c1f08067c2e08067c61081b7c842008057c8308067c443c08107c3e3d8f1a6008047cf125c3a31ca48f4b7c0242081e7c2208067c3908067c6b08067cef08147cac21080c7ce03e080e7cbb62")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java index c794c9094..79131c483 100644 --- a/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlespiProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlespiProtocolDecoderTest extends ProtocolTest { @@ -11,14 +11,15 @@ public class FlespiProtocolDecoderTest extends ProtocolTest { var decoder = inject(new FlespiProtocolDecoder(null)); - verifyPositions(decoder, request(HttpMethod.POST, "/", - buffer("[{\"position.speed\":0,\"position.latitude\":53.90573,\"time.valid.status\":true,\"timestamp\":1506956075,\"position.satellites\":10,\"message.buffered.status\":false,\"business.mode.status\":true,\"gps.status\":true,\"position.longitude\":27.455848,\"position.direction\":0,\"ident\":\"605630\"},{\"siren.status\":false,\"business.mode.status\":true,\"position.satellites\":8,\"timestamp\":1506695785,\"led.status\":false,\"position.latitude\":53.905569,\"position.longitude\":27.455986,\"position.speed\":0,\"gradual.stop.status\":false,\"position.direction\":262.643854,\"hardware.version.enum\":223,\"vehicle.mileage\":160,\"message.buffered.status\":false,\"blinkers.status\":false,\"ident\":\"605630\",\"position.altitude\":233.48,\"immobilizer.status\":false}]"))); + verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( + "[{\"position.speed\":0,\"position.latitude\":53.90573,\"time.valid.status\":true,\"timestamp\":1506956075,\"position.satellites\":10,\"message.buffered.status\":false,\"business.mode.status\":true,\"gps.status\":true,\"position.longitude\":27.455848,\"position.direction\":0,\"ident\":\"605630\"},{\"siren.status\":false,\"business.mode.status\":true,\"position.satellites\":8,\"timestamp\":1506695785,\"led.status\":false,\"position.latitude\":53.905569,\"position.longitude\":27.455986,\"position.speed\":0,\"gradual.stop.status\":false,\"position.direction\":262.643854,\"hardware.version.enum\":223,\"vehicle.mileage\":160,\"message.buffered.status\":false,\"blinkers.status\":false,\"ident\":\"605630\",\"position.altitude\":233.48,\"immobilizer.status\":false}]"))); - verifyPositions(decoder, request(HttpMethod.POST, "/", - buffer("[{\"geofence.inside.status\":false,\"position.valid\":false,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":10.11223,\"refrigerator.sensor.temperature#1\":62.5,\"gnss.antenna.cut.status\":true,\"din\":3,\"ain#3\":0,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":0,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":66.7,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":0,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":68.2,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":68.9,\"ident\":\"605630\",\"timestamp\":946684840,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8165,\"external.powersource.voltage\":15.298,\"gnss.enum\":\"glonass\",\"position.longitude\":20.88774,\"battery.voltage\":4.088,\"refrigerator.sensor.temperature#5\":71.3,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false},{\"geofence.inside.status\":false,\"position.valid\":true,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":57.986744,\"refrigerator.sensor.temperature#1\":74.1,\"gnss.antenna.cut.status\":false,\"ain#3\":0,\"position.hdop\":21.1,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":219,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":70.5,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":5,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":71.3,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":69.3,\"ident\":\"605630\",\"timestamp\":1392272112,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8174,\"external.powersource.voltage\":15.303,\"gnss.enum\":\"glonass\",\"position.longitude\":56.207576,\"battery.voltage\":3.934,\"refrigerator.sensor.temperature#5\":68.1,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false}]"))); + verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( + "[{\"geofence.inside.status\":false,\"position.valid\":false,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":10.11223,\"refrigerator.sensor.temperature#1\":62.5,\"gnss.antenna.cut.status\":true,\"din\":3,\"ain#3\":0,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":0,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":66.7,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":0,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":68.2,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":68.9,\"ident\":\"605630\",\"timestamp\":946684840,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8165,\"external.powersource.voltage\":15.298,\"gnss.enum\":\"glonass\",\"position.longitude\":20.88774,\"battery.voltage\":4.088,\"refrigerator.sensor.temperature#5\":71.3,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false},{\"geofence.inside.status\":false,\"position.valid\":true,\"ain#4\":0,\"rs232.sensor.value#1\":0,\"position.direction\":0,\"rs232.sensor.value#0\":0,\"position.speed\":0,\"position.latitude\":57.986744,\"refrigerator.sensor.temperature#1\":74.1,\"gnss.antenna.cut.status\":false,\"ain#3\":0,\"position.hdop\":21.1,\"refrigerator.sensor.temperature#3\":71.4,\"position.altitude\":219,\"shock.event.trigger\":false,\"alarm.mode.status\":false,\"ibutton.event.connect\":false,\"refrigerator.sensor.temperature#4\":70.5,\"internal.battery.voltage.limit.lower.status\":false,\"ain#2\":0,\"gsm.signal.level\":0,\"refrigerator.connection.status\":0,\"position.satellites\":5,\"external.powersource.voltage.range.outside.status\":false,\"refrigerator.sensor.temperature#2\":71.3,\"incline.event.trigger\":false,\"alarm.event.trigger\":false,\"movement.status\":true,\"refrigerator.sensor.temperature#6\":69.3,\"ident\":\"605630\",\"timestamp\":1392272112,\"engine.ignition.status\":true,\"gsm.sim.status\":true,\"record.seqnum\":8174,\"external.powersource.voltage\":15.303,\"gnss.enum\":\"glonass\",\"position.longitude\":56.207576,\"battery.voltage\":3.934,\"refrigerator.sensor.temperature#5\":68.1,\"ain#1\":0,\"internal.bus.supply.voltage.range.outside.status\":false}]"))); + + verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( + "[{\"ain#1\":1,\"ain#2\":0,\"ain#3\":0,\"ain#4\":0,\"alarm.event.trigger\":true,\"custom.SOS\":1,\"custom.dparam\":3.141593,\"custom.ign\":1,\"custom.iparam\":-55,\"custom.tparam\":\"lorem\",\"din\":722,\"dout\":1048576,\"ident\":\"namo:namo\",\"position.altitude\":300,\"position.direction\":0,\"position.hdop\":1.1,\"position.latitude\":53.90821,\"position.longitude\":27.524165,\"position.satellites\":7,\"position.speed\":0,\"timestamp\":1508508510.013227}]"))); - verifyPositions(decoder, request(HttpMethod.POST, "/", - buffer("[{\"ain#1\":1,\"ain#2\":0,\"ain#3\":0,\"ain#4\":0,\"alarm.event.trigger\":true,\"custom.SOS\":1,\"custom.dparam\":3.141593,\"custom.ign\":1,\"custom.iparam\":-55,\"custom.tparam\":\"lorem\",\"din\":722,\"dout\":1048576,\"ident\":\"namo:namo\",\"position.altitude\":300,\"position.direction\":0,\"position.hdop\":1.1,\"position.latitude\":53.90821,\"position.longitude\":27.524165,\"position.satellites\":7,\"position.speed\":0,\"timestamp\":1508508510.013227}]"))); } -}
\ No newline at end of file +} diff --git a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java index ffb8eb3a9..bd54b4be0 100644 --- a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlexApiProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java index ed44fc62e..b0b0910c6 100644 --- a/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlexCommProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlexCommProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java index 6b289396d..3bff46df0 100644 --- a/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlexibleReportProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FlexibleReportProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java index 6e5ebf4cf..2e0274d11 100644 --- a/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FlextrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; diff --git a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java index 8e62c878e..7918243e6 100644 --- a/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FoxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FoxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java index 1163b4e04..f8cdb8934 100644 --- a/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FreedomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FreedomProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java index a7ce042e5..53ed07da7 100644 --- a/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FreematicsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FreematicsProtocolDecoderTest extends ProtocolTest { @@ -11,6 +11,9 @@ public class FreematicsProtocolDecoderTest extends ProtocolTest { var decoder = inject(new FreematicsProtocolDecoder(null)); verifyPositions(decoder, text( + "UCFLFAYM#0:33770,24:300,82:53.000000,*F9")); + + verifyPositions(decoder, text( "M0ZR4X0#0:204391,11:140221,10:8445000,A:49.215920,B:18.737755,C:410,D:0,E:208,24:1252,20:0;0;0,82:47*B5")); verifyNull(decoder, text( diff --git a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java index a101ef45d..40d8e619e 100644 --- a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FutureWayFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java index e529d5c90..e8af86d66 100644 --- a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class FutureWayProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java index c8b4e35fe..059fc21df 100644 --- a/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/G1rusProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class G1rusProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java index a5aba3513..530f0930e 100644 --- a/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GalileoFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GalileoFrameDecoderTest extends ProtocolTest { @@ -13,6 +13,10 @@ public class GalileoFrameDecoderTest extends ProtocolTest { var decoder = inject(new GalileoFrameDecoder()); assertEquals( + binary("01003f01001c475b166133303035333430363431383437393000001d000064897bb003000b0221c20512a60a0000000802000f209d7b8964300f2536fbfd103c1d01"), + decoder.decode(null, null, binary("01003f01001c475b166133303035333430363431383437393000001d000064897bb003000b0221c20512a60a0000000802000f209d7b8964300f2536fbfd103c1d01"))); + + assertEquals( binary("011780011102e603383633353931303238393630323437043200801c"), decoder.decode(null, null, binary("011780011102e603383633353931303238393630323437043200801c"))); diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java index 7addc8e75..2611832ff 100644 --- a/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GalileoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GalileoProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java index 29fe4cc94..af640f3d4 100644 --- a/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/GalileoProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java index bfb33de22..6f1bd88db 100644 --- a/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GatorProtocolDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GatorProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java new file mode 100644 index 000000000..af6c71e37 --- /dev/null +++ b/src/test/java/org/traccar/protocol/GatorProtocolEncoderTest.java @@ -0,0 +1,23 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; +import org.traccar.model.Device; + +import static org.mockito.Mockito.when; + +public class GatorProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncode() throws Exception { + var encoder = inject(new GatorProtocolEncoder(null)); + var device = encoder.getCacheManager().getObject(Device.class, 1); + when(device.getUniqueId()).thenReturn("13332082112"); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POSITION_SINGLE); + verifyCommand(encoder, command, binary("24243000062008958C070D")); + } +} diff --git a/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java index 67a730f97..0f90c9b07 100644 --- a/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GenxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GenxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java index 31ae1d5e8..3701fa772 100644 --- a/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl100ProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gl100ProtocolDecoderTest extends ProtocolTest { @@ -12,6 +12,9 @@ public class Gl100ProtocolDecoderTest extends ProtocolTest { var decoder = inject(new Gl100ProtocolDecoder(null)); verifyPosition(decoder, text( + "+RESP:GTRTL,359464032011616,1,0,0,0,0.1,0,1662.5,,36.822301,-1.309476,20230706032920,0639,0002,08DF,1F5E,00,095,0101050105,4470")); + + verifyPosition(decoder, text( "+RESP:GTLGL,359464030492644,1,2,1,0,0.4,0,299.7,1,5.455551,51.449776,20160311083229,0204,0016,03EC,BD94,00,0036,0102090501")); verifyPosition(decoder, text( diff --git a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java index cad70c35b..680b3b77c 100644 --- a/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200BinaryProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gl200BinaryProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java index 51816222d..6fd3f0aaa 100644 --- a/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200FrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Gl200FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java index f3a77228c..b6e50e07d 100644 --- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,8 +11,55 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { var decoder = inject(new Gl200TextProtocolDecoder(null)); + verifyPositions(decoder, buffer( + "+RESP:GTERI,C2010D,869653060009939,GV600M,00000100,,10,1,0,0.0,0,163.9,10.239379,45.931389,20231210233723,0222,0010,2F31,006D7621,,0.0,,,,3,410000,,1,0,6,4,0,,01BF,Sondaporte,EA7D0A3882F6,1,3484,21.81,43,00,00,0,0,0,20240114221802,1875$")); + + verifyPositions(decoder, buffer( + "+RESP:GTERI,8020050605,867488060225819,GV355CEU,00000106,12390,10,1,1,0.0,37,253.0,10.240915,45.930262,20240226074452,0222,0010,2F31,0063952B,03,7,0,0.0,,,,,100,110000,0,0,1,FFFFF,VR3USHNSSNJ711765,0,H311796,109.40,0,,,,,,,46.54,29.91,16.63,12.88,,8080,,,00,0,20240226074453,2548$")); + + verifyAttributes(decoder, buffer( + "+RESP:GTCAN,F10413,862599050467479,GV350M,0,1,C18CFDFF,3BKHHZ8X7GF723380,2,H2898058,232160.50,896,0,64,L/H1.5,P99.20,0,40532.95,,,,,,,,,,,1,0.0,234,2812.2,-78.508807,-0.218812,20240226211921,,,,,,20240226211922,DE03$")); + + verifyPositions(decoder, buffer( + "+RESP:GTFRI,8020040305,866314060272661,,,50,1,1,0.0,0,2957.9,-78.691727,-0.951205,20231227162916,,,,,00,0.0,,,,,100,210100,,,,20231227162916,0117$")); + + verifyPositions(decoder, buffer( + "+BUFF:GTFRI,8020040200,866314060249032,,12194,10,1,3,0.0,0,20.1,-71.596533,-33.524718,20230926200338,0730,0001,772A,052B253E,02,0,0.0,,,,,0,420000,,,,20230926200340,1549$")); + + verifyAttribute(decoder, buffer( + "+RESP:GTFRI,423037,866884047716519,GT501,0,1,1,5,12,0.1,0,46.8,-95.559173,30.109955,20231110185836,6,0e36c9916485,-50,,,,e831cd5eb79d,-73,,,,ccf4110c4bd5,-79,,,,acdb48973168,-79,,,,80ab4dc323c4,-82,,,,ec8eb5cfa1c6,-89,,,,310,10,711D,81ECF0F,00,,93,20231110185839,0005$"), + Position.KEY_BATTERY_LEVEL, 93); + + verifyPositions(decoder, buffer( + "+RESP:GTFRI,8020040200,866314060109269,,,10,1,1,0.0,0,9.0,-71.596601,-33.524595,20230722145338,0730,0001,772A,052B253E,00,0.0,,,,,100,210100,,,,20230722145341,0F4C$")); + + verifyAttributes(decoder, buffer( + "+RESP:GTCAN,F1040C,862599050497393,GV350M,0,0,FFFFFFFF,,1,,,,,,,,,,,,,,,,,,,,,,,0,,,1,0.0,70,2961.6,-78.691750,-0.951135,20230703191659,,,,,,20230703191659,5A4A$")); + + verifyAttribute(decoder, buffer( + "+RESP:GTBAA,F1040C,862599050497393,GV350M,FF,3,0,04,000A,780541256AE9,3065,0,0.0,213,2908.3,-78.691944,-0.951426,20230511173150,,,,,,20230511175001,0159$"), + "accessoryVoltage", 3.065); + + verifyAttribute(decoder, buffer( + "+RESP:GTBID,C20105,866833040163013,GV350M,1,0,000A,B80EA11FF800,2934,0,0.0,0,1506.5,-99.192686,18.932709,20221026025339,0334,0020,0232,029D4E02,,20221026181026,9F1D$"), + "accessoryVoltage", 2.934); + + verifyAttribute(decoder, buffer( + "+RESP:GTDTT,410502,864802030541621,,,,1,35,45637561747261636b0d0a434f4d422c302c39342e302c2d312e302c2c2c4844430d0a,20230421034626,EA2E$"), + Position.KEY_FUEL_LEVEL, 94.0); + + verifyNull(decoder, buffer( + "+RESP:GTFRI,5E0100,862061048023666,,,12940,10,1,1,0.0,97,179.8,-90.366478,38.735379,20230616183231,0310,0410,6709,03ADF710,00,6223.7,,,,,110000,,,,202306161834$")); + + verifyAttribute(decoder, buffer( + "+BUFF:GTERI,410502,864802030794634,GV75W,00000001,,10,1,1,0.0,0,3027.8,-78.706612,-0.955699,20230418170736,0740,0002,A08C,2AB72D,00,0.0,,,,100,110000,1,0099,20230418171004,8B98$"), + Position.KEY_FUEL_LEVEL, 153); + + verifyPositions(decoder, false, buffer( + "+BUFF:GTFRI,2E0503,861106050005423,,,0,1,,,,,,,,,,,,0,0,,98,1,0,,,20200101000001,0083$")); + verifyAttribute(decoder, buffer( - "+RESP:GTERI,271002,863457051562823,,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"), + "+RESP:GTERI,271002,863457051562823,GV300,00000002,,10,1,1,0.0,15,28.2,-58.695253,-34.625413,20230119193305,0722,0007,1168,16B3BB,00,0.0,,,,99,210100,2,1,28F8A149F69A3C25,1,0190,20230119193314,07C7$"), Position.PREFIX_TEMP + 1, 25.0); verifyAttribute(decoder, buffer( @@ -129,7 +176,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { "+RESP:GTERI,310603,863286023345490,,00000002,,10,1,2,0.3,0,155.7,8.000000,52.000000,20171215213040,0262,0002,1450,9F13,00,1130.3,00539:27:19,,,110000,2,1,28FFD5239115034E,1,,20171215213041,27C7$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,250C02,868789023691057,,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$")); + "+RESP:GTERI,250C02,868789023691057,GV300,00000019,,10,1,1,0.0,196,2258.0,-99.201807,19.559242,20180214002957,0334,0003,235B,7F8D,00,6786.7,,,,100,110000,1,0394,1,4,100.0,100.0,20180214003006,C72B$")); verifyAttributes(decoder, buffer( "+RESP:GTCAN,310603,863286023335723,gv65,00,1,C03FFFFF,,0,,719601.00,,,,,,,,274.99,179.02,95.98,84761.00,,,0,,0,,,0,0.0,216,29.8,-2.155296,51.899400,20180209172714,0234,0010,53F3,8D38,00,20180211002128,E94E$")); @@ -156,7 +203,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { "+RESP:GTOBD,360701,864251020253807,LSGTC58UX7Y067312,GV500,0,70FFFF,LSGTC58UX7Y067312,1,12309,983A8140,0,0,33,nan,,0,0,0,,10,0,,0,4.4,0,83.7,36.235142,49.967324,20170829112348,0255,0001,2760,9017,00,690.1,20170829112400,3456$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060502,861074023620928,,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$")); + "+RESP:GTERI,060502,861074023620928,GV300,00000002,27822,10,1,1,0.0,84,2870.9,-78.531796,-0.277329,20170825045344,,,,,,0.0,01138:30:24,,,83,220104,2,1,28FF2776A2150308,1,FFAD,0,20170825045348,A88C$")); verifyAttributes(decoder, buffer( "+RESP:GTINF,280500,A1000043D20139,GL300VC,41,,31,0,0,,,3.87,0,1,1,,,20170802150751,70,,48.0,,,20170802112145,03AC$")); @@ -171,7 +218,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { "+RESP:GTTRI,862370030005908,1,0,99,1,0.0,354,18.5,18.821100,-34.084002,20170607152024,0655,0001,00DD,1CAE,00,0103010100,20170607172115,3E7D$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060800,861074023677175,,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$")); + "+RESP:GTERI,060800,861074023677175,GV300,00000002,12351,10,1,1,0.0,0,2862.4,-78.467273,-0.164998,20170529181717,,,,,,0.0,00259:11:50,,,0,210104,2,1,28E17436060000E2,1,015F,0,20170529181723,2824$")); verifyPosition(decoder, buffer( "+RESP:GTSWG,110100,358688000000158,,1,0,2.1,0,27.1,121.390717,31.164424,20110901073917,0460,0000,1878,0873,,20110901154653,0015$")); @@ -192,7 +239,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { "+RESP:GTFRI,060228,862894020180553,,14827,10,1,1,3.4,199,409.6,-63.174466,-17.739317,20170407121823,0000,0000,0000,0000,00,15989.5,01070:43:13,13,180,0,220101,,,,20170407081824,9607$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060502,861074023376992,,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$")); + "+RESP:GTERI,060502,861074023376992,GV300,00000002,27239,10,1,1,0.2,312,183.3,-79.320820,-2.499110,20170401212005,0740,0000,EE4E,C98F,00,0.0,02114:36:35,,,90,220504,2,0,0,20170401212007,9E3D$")); verifyPositions(decoder, buffer( "+RESP:GTFRI,060502,861074023689626,,25202,10,1,1,0.0,0,2744.1,-78.261047,0.023452,20170401211940,,,,,,0.0,00079:19:15,,,51,110000,,,,20170401212003,4DA7$")); @@ -204,7 +251,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { "+RESP:GTERI,06020B,862170010196747,,00000000,,10,1,2,1.8,0,-2.5,117.198440,31.845219,20120802061037,0460,0000,5663,0358,00,0.0,,,,0,410000,20120802061040,0012$")); verifyPositions(decoder, buffer( - "+RESP:GTERI,060502,861074023692562,,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$")); + "+RESP:GTERI,060502,861074023692562,GV300,00000002,14197,10,1,1,0.2,220,491.8,-79.064212,-2.159754,20170401212007,0740,0000,EE49,CE25,00,0.0,01509:10:58,,,87,220104,2,0,0,20170401212010,D14D$")); verifyPositions(decoder, buffer( "+RESP:GTFRI,210102,354524044925825,,1,1,1,29,2.8,0,133.7,-90.203063,32.265473,20170318005208,,,,,10800,4,20170318005208,0002$")); @@ -440,6 +487,11 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest { verifyAttributes(decoder, buffer( "+ACK:GTGEO,1A0102,135790246811220,,0,0008,20100310172830,11F0")); + decoder.setModelOverride("GV355CEU"); + + verifyAttributes(decoder, buffer( + "+RESP:GTCAN,8020050605,867488060270575,,00,1,FFFFFFFF,8LBETF3W4N0001613,,,22.54,0,,,,,,,7.84,4.61,3.24,3.33,,8080,,,00,0.00,0.00,1,14,14,2371,0,001FFFFF,,,,,,,,,7158,9998,0,7.84,0.00,0.00,558,,,,,,,C0,,,,,0,0.0,346,2848.5,-78.592371,-0.968132,20240202083437,0740,0002,526C,00AE7907,00,20240202083440,3F6D$")); + } } diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java index e244a835d..7391fea28 100644 --- a/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GlobalSatProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java index 9001c4bf3..dc00cad44 100644 --- a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GlobalSatProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java index 730d4bb60..995fffad0 100644 --- a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GlobalstarProtocolDecoderTest extends ProtocolTest { @@ -11,6 +11,8 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest { var decoder = inject(new GlobalstarProtocolDecoder(null)); + decoder.setAlternative(true); + verifyNull(decoder, request(HttpMethod.POST, "/", buffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", "<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"16/09/2020 01:33:07 GMT\" messageID=\"567207180ae9100687cef8c81978371a\">\n", @@ -22,6 +24,8 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest { "</stuMessage>\n", "</stuMessages>"))); + decoder.setAlternative(false); + verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"25/03/2020 03:02:32 GMT\" messageID=\"300421a0fd2a100585bdde409d6f601a\">", diff --git a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java index 592c2de91..78722fa17 100644 --- a/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GnxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GnxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java index 71ffbb587..3e1349a80 100644 --- a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -14,6 +14,9 @@ public class GoSafeProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, false, text( "*GS06,357330050846344,RST#")); + verifyPositions(decoder, text( + "*GS06,353218073585128,181255300523,,SYS:Smart Track;V9.31;V1.1.5,GPS:A;5;N31.551856;E74.366920;0;0;;2.15;2.64,COT:0,ADC:10.78;0.02,DTT:4002;E1;0;0;0;1$181325300523,,SYS:Smart Track;V9.31;V1.1.5,GPS:A;6;N31.551856;E74.366920;0;0;;2.05;2.13,COT:0,ADC:10.79;0.02,DTT:4002;E1;0;0;0;1#")); + verifyAttribute(decoder, text( "*GS06,356449068350122,013519070819,,SYS:G6S;V3.37;V1.1.8,GPS:A;12;N23.169866;E113.450728;0;255;54;0.79,COT:18779;,ADC:12.66;0.58,DTT:4084;E1;0;0;0;1,IWD:0;1;ad031652643fff28;23.2;1;1;86031652504fff28;24.3;2;1;e603165252a5ff28;24.2;3;1;bb0416557da6ff28;24.0#"), Position.PREFIX_TEMP + 3, 24.0); diff --git a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java index a6e3f2d69..aea6e7592 100644 --- a/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GotopProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java index cb75b4035..332423645 100644 --- a/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps056FrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Gps056FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java index ac3738644..29cc6b09f 100644 --- a/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps056ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gps056ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java index cf5786d75..c11a5b0d9 100644 --- a/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps103ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java index f6cbe6d17..3df64039c 100644 --- a/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Gps103ProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Gps103ProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java index a1f81b329..000163ba8 100644 --- a/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GpsGateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GpsGateProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java index bc7910779..8a5cb434b 100644 --- a/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GpsMarkerProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; diff --git a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java index d1326515f..73da69d96 100644 --- a/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GpsmtaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GpsmtaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java index a61e708f7..9d6c189f6 100644 --- a/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GranitFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class GranitFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java index d2e181e09..ff45bd99c 100644 --- a/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/GranitProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class GranitProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java index ce2768448..c382b3262 100644 --- a/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gs100ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gs100ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java index 25f59a948..100ef340a 100644 --- a/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt02ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gt02ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java index a9d011277..c74427f90 100644 --- a/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gt06FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index 697908a4c..04d7fafb3 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -18,6 +18,52 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { "78780D01086471700328358100093F040D0A")); verifyAttribute(decoder, binary( + "78782732180214123324ca0162bdf0041f45d900190b0a02d4000bc5270000ec025206040202005e07e10d0a"), + Position.KEY_ALARM, Position.ALARM_POWER_CUT); + + verifyAttribute(decoder, binary( + "78782616170A080C0E24C0027C58AD0C2B8B0100454E0901CC0025030328E7A0005D4B13021EC373170D0A"), + Position.KEY_BATTERY_LEVEL, 93); + + verifyAttribute(decoder, binary( + "787826161709130f3a2dcc02c55f2a089f9af9005c210901360481fe066d9b03413e420102035f92fe0d0a"), + Position.KEY_ALARM, Position.ALARM_SOS); + + verifyAttribute(decoder, binary( + "78782f221709130f3a32cc02c55f2a089f9afc005c2101360481fe066d9b03000000000025d4000000000020002d036049d70d0a"), + Position.PREFIX_TEMP + 1, 0.32); + + verifyAttribute(decoder, binary( + "78785995ffff01170719152013df0163d45f041ee52018be002f00876900004556454e545f3836323739383035303137353131325f30303030303030305f323032335f30375f32355f31385f33325f30355f31342e6d70340119d15a0d0a"), + Position.KEY_EVENT, 0x69); + + verifyAttribute(decoder, binary( + "787829a01707150f2d0ecd01635100041e96d000087c02d4020000912e000000000718798d000e0006ed3ce50d0a"), + Position.KEY_IGNITION, false); + + verifyNotNull(decoder, binary( + "787829a0170704112226cf0163fe7c0420f6f000091302d402000091290000000007186b8f01030001460d010d0a")); + + verifyAttribute(decoder, binary( + "797900109b0344373532304136320d0a000f87f00d0a"), + Position.KEY_RESULT, "D7520A62"); + + verifyAttribute(decoder, binary( + "7878241617070a150e24ca01fba0040780e177005c0001720253360027db6204e40400004bf1e90d0a"), + Position.KEY_ALARM, Position.ALARM_SOS); + + verifyAttribute(decoder, binary( + "78780a130604ea04000006bc8a0d0a"), + Position.KEY_POWER, null); + + verifyAttributes(decoder, binary( + "797900849404414c4d313d43353b414c4d323d43433b414c4d333d35433b535441313d43303b4459443d30313b534f533d303133323838333730302c2c3b43454e5445523d303133323838333730303b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c313b00b79d120d0a")); + + verifyAttribute(decoder, binary( + "78782912170316053b3bcf015b51220af1201105d56100000000000000000000869c0130010000000238d1af0d0a"), + Position.KEY_DRIVING_TIME, 0); + + verifyAttribute(decoder, binary( "78781219012ed042cc00954d00040419000056fe290d0a"), Position.KEY_ALARM, Position.ALARM_LOW_BATTERY); @@ -96,8 +142,9 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { "7878281520000000003c49434349443a38393838323339303030303039373330323635303e00020d446f260d0a"), Position.KEY_ICCID, "89882390000097302650"); - verifyNull(decoder, binary( - "797900099b0380d600046f91e90d0a")); + verifyAttribute(decoder, binary( + "797900099b0380d600046f91e90d0a"), + Position.KEY_RESULT, "80d600"); verifyNull(decoder, binary( "797900a56615010d081f3b012c323131303d30303033643238342c323130353d30303030316332302c323130623d30303030326537632c323130633d30303033643238342c323130663d30303030306331632c323130643d30303030323166632c323161363d30303030303030302c323130343d30303030306531302c323132663d30303030303030302c323134353d30303030303030302ccb03851f5f03c020525514a7003e216a0d0a")); @@ -110,7 +157,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyAttribute(decoder, binary( "7979000E9B0332382E33A1E60D0A0289BE490D0A"), - Position.PREFIX_TEMP + 1, 28.3); + Position.KEY_RESULT, "32382e33a1e60d0a"); verifyPosition(decoder, binary( "7878353714080d05000ac500a886eb0b7522f000100001fe0a05ea004f1b000001002e0400002328003b0217c0003c0401020001002c468a0d0a")); @@ -153,7 +200,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyAttribute(decoder, binary( "797900149b03023539303042343843454238410300139ba40d0a"), - Position.KEY_DRIVER_UNIQUE_ID, "5900B48CEB"); + Position.KEY_RESULT, "0235393030423438434542384103"); verifyPosition(decoder, binary( "787821121303120b2524c70138e363085b549003d43301940057d200cd52c000006aa1ca0d0a")); @@ -186,7 +233,7 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyAttributes(decoder, binary( "79790008940000ed0289d6860d0a")); - verifyNull(decoder, binary( + verifyAttributes(decoder, binary( "797900a59404414c4d313d34353b414c4d323d44353b414c4d333d35353b535441313d34303b4459443d30313b534f533d303538353036313536372c2c3b43454e5445523d3b46454e43453d46656e63652c4f46462c302c302e3030303030302c302e3030303030302c3330302c494e206f72204f55542c303b49434349443d38393937313033313031303038393539303432463b4d4f44453d4d4f44452c312c3138303b0008f65e0d0a")); verifyPosition(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java index 13463ea39..80b50df3a 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java index 48c4306d6..4d7e8699e 100644 --- a/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt30ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Gt30ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java index c61c0f0c9..dc6eec84d 100644 --- a/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/H02FrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class H02FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java index e68f0814c..a63488960 100644 --- a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java index 1be9421bb..2fc7af958 100644 --- a/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/H02ProtocolEncoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; @@ -11,7 +11,7 @@ import java.time.LocalTime; import java.time.ZoneOffset; import java.util.Date; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class H02ProtocolEncoderTest extends ProtocolTest { @@ -19,7 +19,7 @@ public class H02ProtocolEncoderTest extends ProtocolTest { private final Date time = Date.from( LocalDateTime.of(LocalDate.now(), LocalTime.of(1, 2, 3)).atZone(ZoneOffset.systemDefault()).toInstant()); - @Before + @BeforeEach public void before() throws Exception { encoder = inject(new H02ProtocolEncoder(null)); } diff --git a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java index 9d26d56c3..5ad5693be 100644 --- a/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HaicomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HaicomProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java index 8fe4d2c8b..ac814a5f4 100644 --- a/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HomtecsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HomtecsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java index ed7b0534b..8d57bf787 100644 --- a/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HoopoProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java index 991e0b36d..26494f2b9 100644 --- a/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuaShengFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HuaShengFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java index 7002d7e88..d8cc739ab 100644 --- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -21,6 +21,13 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest { "c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0")); verifyAttribute(decoder, binary( + "c0000000bdaa0000000000061d480000083233303132333039323634330000000000000000000000a600140000000100187e02de0a00290372000005951600260000004a0000040009080000004a0005000a0d0000000ad0000900154d414b474d363639484a4e333031383739000f00133836323230353035353338393836320010001031333231322e30303030303000110008000000000014000bf851084f000018001500060000002000153430344030354035363532403130363332c0"), + Position.KEY_ODOMETER, 13212000.0); + + verifyNotNull(decoder, binary( + "c0000000b9aa00000000000013c800001132333035303431343537323600186bc30045e5b8002a008b0077002d000100187f0c4b2600d906ec000005938800000000000e0000040009110000000e0005000a1d0400000079000900154646464646464646464646464646464646000f00133836323230353035353339313733360010000c302e30303030303000110008000000000014000bf81b204901b52a001500060000002000153231394030324030403130343438393139c0")); + + verifyAttribute(decoder, binary( "C00000001CAA120000000000020001001001000200030043008200C100C0"), Position.KEY_DTCS, "P0100 P0200 P0300 C0300 B0200 U0100"); @@ -29,6 +36,9 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest { Position.KEY_HOURS, 58.7); verifyNotNull(decoder, binary( + "c0000000b1aa000000000000050000000031393730303130313134303200000000000000000000000000000000000100180000000000000000000000000000000000c57e000005000a1a000000c569000900155756575a5a5a43445a4e57313139313534000f00133836393733313035313339393231300010000c302e30303030303000110008000000000014000bf800002800000000150006000000200016353035403031403040313337313931363831c0")); + + verifyNotNull(decoder, binary( "c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0")); verifyPosition(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java new file mode 100644 index 000000000..c320d4aa9 --- /dev/null +++ b/src/test/java/org/traccar/protocol/HuaShengProtocolEncoderTest.java @@ -0,0 +1,33 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; + +public class HuaShengProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncode() throws Exception { + + var encoder = inject(new HuaShengProtocolEncoder(null)); + + Command command; + + command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_OUTPUT_CONTROL); + command.set(Command.KEY_INDEX, 1); + command.set(Command.KEY_DATA, "1"); + + verifyCommand(encoder, command, binary("c00000000daa1600000000000101c0")); + + command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POSITION_PERIODIC); + command.set(Command.KEY_FREQUENCY, 60); + + verifyCommand(encoder, command, binary("c000000012aa0400000000000100020006003cc0")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java index 6b902fb46..a76beac20 100644 --- a/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HuabaoFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java index e91c84d79..164635109 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,9 +11,58 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest { var decoder = inject(new HuabaoProtocolDecoder(null)); + verifyAttribute(decoder, binary( + "7e55018c418560090010174701022106242122348476113550490700001c06000000074e0000000000000308100000100102020200000a030000000b0803363839363037650c0600000001000afa7e"), + "unlockResult", 0x65); + + verifyPosition(decoder, binary( + "7e55018c378580120300032a06052117594022348474113550560705981e0400000002370ac30c0c28660308000000100101020200000a0301cc000c0600a2ffa7ff5e1b7e")); + + verifyPosition(decoder, binary( + "7e55028c37850011000200c008052106305122348621113550170700001e080000000aaa0000000000000300000000100100020200140a030000000c06003bffa8ffc3c77e")); + + verifyAttribute(decoder, binary( + "7E020000C2000000001862003F0000000400108042015A322206C869480017007B012E230918081550661D01CC1A0000099DC25727B0130000099DC26B27B00100000C40F89427B0711438393836303436393130323139303037383135336A0114503FF8AF05826BC8CA24E124F732B7C780C5484D6318C0A840412262D4BCEC26CA0234CCB85455D5114F12B650FA841FBEC0B03C67A21F501CAE6C384595028DA76B010060110065000012345678903930303137333833630B0012345678900E5C3080006804000003D46902016F6D7E"), + Position.KEY_DRIVER_UNIQUE_ID, "\u00909001738"); + + verifyAttribute(decoder, binary( + "7e02000072440061018577001b0000100000200066005ffb8b065dc8900000000000002309080513406316cd91fe1314da1080318122c3a19a6f6f3a0fc6318113660b01fe00000a069a011704de690201a16a011f6b010a6c0f35313031303139313539323738373571143839363231303030313931353932373837353546407e"), + "lock2Battery", 4.038); + verifyNull(decoder, binary( "7e010200204f07788ef67601824f4459344f544d314d4459774d4441314d444977626d5633553235536457786cba7e")); + verifyAttribute(decoder, binary( + "7e0200003f014501643822000300020000000c000c0000000000000000000000000000230615143903300111310100530901027f0300456f073e56020900fe02001e57080002000200000000df7e"), + Position.KEY_BATTERY_LEVEL, 90); + + verifyAttribute(decoder, binary( + "7e090000344f07788ef87d0138f02305151230460102020001ffffffff000100001457000000020006503134353700000c000a029dc63004b99a98230515132726787e"), + Position.KEY_DTCS, "P1457"); + + verifyAttribute(decoder, binary( + "7e0200006476806111898300710000000000100046005d3156065f7128000000000000230511165956660b01fe000001031a5d1a8101670831333231343332326902018b6a01166b01006c0f323034303830393230373533363735711438393434343738383030303030323131303030464b7e"), + Position.KEY_BATTERY, 3.95); + + verifyAttribute(decoder, binary( + "7e02000071768060874297002d0000000000208022015a30b006c869f8000000000000230505062034600b0004003930303235343939660b01cc0000000c40f89f27b067083133323134333232690201936a01116b01006c0f34363030383138353937303632343071143839383630343938313032313930353835373430607e"), + Position.KEY_EVENT, 4); + + verifyAttribute(decoder, binary( + "7e0200002f017028775424038d000000000000000a0189dbeb04ca653a00000012014723040700074401040000000030011b31010ee1012dea020001dc7e"), + Position.KEY_BATTERY_LEVEL, 45); + + verifyAttribute(decoder, binary( + "7E02000079013653183645009E00000000000C0C030158BF0006C926670000004000CE22120904274201040000005DBC3244524956494E47204C4943454E53452454455354244D522E0000000000000000000000000000000000000000000000000000BD0F323431393939393935383030313030E3060000050500007102000C30011F310108987E"), + "driver", "DRIVING LICENSE$TEST$MR."); + + verifyAttribute(decoder, binary( + "7e55019c3b8571110003399a07032310302029538631031015370500001a0c000000265700440001233703080000001001020202000a0a04028f000af401040c06ff98ffa8007e707e"), + "tilt", "[-104,-88,126]"); + + verifyPosition(decoder, binary( + "7e0900001f4f07788ef87d000cf0230223150215010203013800000c000b029dc58c04b99b60230223171822507e")); + verifyPosition(decoder, binary( "7e0200004107904226220608ca0000010000000010031dac0d004864f30000000000002212291003220104000179a7300107310100eb17000300e151000300e304000b00d801041edf340000306b007e")); diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java index 6f9c8250a..87cc2b12e 100644 --- a/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/HuabaoProtocolEncoderTest.java @@ -1,13 +1,13 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; public class HuabaoProtocolEncoderTest extends ProtocolTest { - @Ignore + @Disabled @Test public void testEncode() throws Exception { diff --git a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java index 5503ab02c..e932ff7e9 100644 --- a/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/HunterProProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class HunterProProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java index 1bd52ce89..da4e25fda 100644 --- a/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IdplProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class IdplProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java index b6e12aef5..8de96a890 100644 --- a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class IntellitracProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java index c668084a1..c4f01fde4 100644 --- a/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/IotmProtocolDecoderTest.java @@ -2,7 +2,7 @@ package org.traccar.protocol; import io.netty.handler.codec.mqtt.MqttMessageBuilders; import io.netty.handler.codec.mqtt.MqttQoS; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class IotmProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java index 26f28916c..ebdb6fd0f 100644 --- a/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ItsFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java index dd8d3a0d9..86b438c7c 100644 --- a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java index 195fc4258..b9bda0c0a 100644 --- a/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/ItsProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ItsProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java index b08dff4e7..45655898e 100644 --- a/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Ivt401ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Ivt401ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java index b275e6d01..6b60e19cc 100644 --- a/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/JidoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class JidoProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java index a1b0acac5..c8e29d2ec 100644 --- a/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/JpKorjarProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class JpKorjarProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java index 1bc5d8480..f40bcd720 100644 --- a/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class JsonFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java index 8e408e50f..2dce2309d 100644 --- a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Jt600FrameDecoderTest extends ProtocolTest { @@ -11,6 +11,14 @@ public class Jt600FrameDecoderTest extends ProtocolTest { var decoder = inject(new Jt600FrameDecoder()); verifyFrame( + binary("2460201102320112003401010000000422434199114158229e000000000009000000000010e06400000000000020100e0868817043592664000000000000"), + decoder.decode(null, null, binary("2460201102320112003401010000000422434199114158229e000000000009000000000010e06400000000000020100e0868817043592664000000000000"))); + + verifyFrame( + binary("24657060730131001b13111710361906538525079524797f000000000000000003f300036c"), + decoder.decode(null, null, binary("24657060730131001b13111710361906538525079524797f000000000000000003f300036c"))); + + verifyFrame( binary("2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e"), decoder.decode(null, null, binary("2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e"))); diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java index fc52e32e3..32213fda1 100644 --- a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Jt600ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java index fdd73b9ce..5c8c260b7 100644 --- a/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Jt600ProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Jt600ProtocolEncoderTest extends ProtocolTest { Jt600ProtocolEncoder encoder = new Jt600ProtocolEncoder(null); diff --git a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java index a705d8082..1425bc066 100755 --- a/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/KenjiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class KenjiProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java index 155493bea..3097c02e8 100644 --- a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java @@ -1,7 +1,8 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class KhdProtocolDecoderTest extends ProtocolTest { @@ -10,6 +11,14 @@ public class KhdProtocolDecoderTest extends ProtocolTest { var decoder = inject(new KhdProtocolDecoder(null)); + verifyAttribute(decoder, binary( + "2929A300403099934C2004030943310000000000000000000000007B0000007FFF0E0000E70014000000000018050B01303030314330334437312102007B2203140DDA610D"), + Position.KEY_DRIVER_UNIQUE_ID, "0001C03D71"); + + verifyAttribute(decoder, binary( + "2929a3003e1680ba0a2304180759500000000000000000000000007b00000080001914000000000000000000162001641b0b0000249002bc58030001cc46020000e70d"), + Position.KEY_BATTERY_LEVEL, 100); + verifyPosition(decoder, binary( "2929800028258b8c10210731035840031534240542120200000337fb000000ffff5a00000a0000000005005d0d")); diff --git a/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java index 468083f35..1ee652cd9 100644 --- a/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/KhdProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java index 084ae0498..a9e0a26fe 100644 --- a/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/L100FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class L100FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java index 56281cda0..105e1f3f5 100644 --- a/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/L100ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class L100ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java index 45544241f..a8afb8573 100644 --- a/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class LacakProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java index aacf9abc8..4df486d56 100644 --- a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class LaipacProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java index ea31bc99d..dcff59f0c 100644 --- a/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/LeafSpyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class LeafSpyProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java index 674738c82..0ac1ea92f 100644 --- a/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/M2cProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class M2cProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java index 0d812ebfc..c4db6945d 100644 --- a/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/M2mProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class M2mProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java index be0fe502e..237ef45b5 100644 --- a/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MaestroProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MaestroProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java index a77043b9d..560c7967b 100644 --- a/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ManPowerProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ManPowerProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java index 0c74d4772..add137114 100644 --- a/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Mavlink2ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Mavlink2ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java index 854564cd2..996a4c98a 100644 --- a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MegastekFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java index 964c59927..227fb20e0 100644 --- a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java index 379cf28f9..2647c49db 100644 --- a/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeiligaoFrameDecoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class MeiligaoFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java index 4c6eae847..8074636a3 100644 --- a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java index 1f2e5f7e3..f62a9f722 100644 --- a/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java index a55935ed2..f86b676a6 100644 --- a/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MeitrackFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java index ce0a1e922..74a180b11 100644 --- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,6 +11,36 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest { var decoder = inject(new MeitrackProtocolDecoder(null)); + verifyAttribute(decoder, binary( + "24246D3230312C3836393430393036323730323834332C4343452C000000000100A7002A000D05010626070914001502930194009500960097629D209E63A1640E0824000956000A07000B2A001606001704001901001A0E0B91280092280099EF049C52009F1B004023000C0215B9F2FF035855F506041E0F142D0C01708C010D748AC2001C012000009A000000009BD0623E02A0889FF201A2D61A0000A542020000FEF4A3D50900030E0CFE010A007F466FFC0000000049090401000000000000004B0501010232472A44360D0A"), + Position.KEY_HOURS, 644515 * 60000L); + + verifyAttribute(decoder, binary( + "2424593136312c3836323039303035303031363139332c4343452c0000000001007f0017000705010607071714001500fe69601b00070800000971000a13000b19001605001acc0440230006029779570103eb5bcc06041ff0e8290c430100000d780400001c01000000030e0ccc010000922781abb90ca4fffe731e0109746e6873656e736f72ac233f6e219064051b753b00000000000000004b060101034c54452a42380d0a"), + "tagName", "tnhsensor"); + + verifyAttribute(decoder, binary( + "2424683136342C3836363334343035333039353238322C4343452C000000000100820018000505000600070B14001500080800000900000A00000B00001606001A0000402300FE90000006022E79570103E55CCC0604E1FDB32B0CC32C00000D58EB02001C01000000050E0CCC010000B627BF11000000004B1001010D475052532847534D2039303029FEA50601FFFFFF7FFFFEA80701010258023800FEB20501010000002A41360D0A"), + "battery2Level", 88); + + verifyPositions(decoder, binary( + "2424533232312c3836323331313036323737393431362c4343452c000000000100bb001c0006012305000600071f1500fe6961050800000900000a00000b00001aca000702a72c52030340a0c90004f3408b2c0ca80100000d238d08001c01000000fe37000000000a0e0cf00001002700167aa601a0ff1d082abd890a491cd2ff1e0828bd890a491cd2ff1f0842490f526db0caff20083c286d5b082cc9ff21083e286d5b082cc9ff2208ac233fc0d2e0c7ff2308b0411d64d9d5c7ff2408ae233fc0d496c3ff4b150101124c54452845555452414e2d42414e443230292a38420d0a")); + + verifyAttribute(decoder, binary( + "2424593434312c3836353431333035303839313733372c4343452c00000000030088001800050501061607191400150008080000098e000a05000b0c001608001a0000402300fe9000000602c3fe5ffe03e22a1f0904e6688d2b0cd94002000d5f6f03001c01000000050e0cf901010032700298c80899ff4b16010113464444204c5445284c54452042414e44203329fea50601ffffff7ffffea807024d0000000000feb205010000000083001700050501061607191400150008080000098e000a05000b0c001608001a0000405100fe9000000502c3fe5ffe03e22a1f0904e6688d2b0cd94002000d606f0300050e0cf901010032700298c80899ff4b16010113464444204c5445284c54452042414e44203329fea50601ffffff7ffffea807024d0000000000feb205010000000088001800050501061607151400150008080000098e000a05000b0c001607001a0000402300fe9000000602c3fe5ffe03e22a1f0904f0688d2b0cd94002000d696f03001c01000000050e0cf901010032700298c80897ff4b16010113464444204c5445284c54452042414e44203329fea50601ffffff7ffffea807024d0000000000feb20501000000002a36320d0a"), + Position.KEY_BATTERY_LEVEL, 77); + + verifyAttribute(decoder, binary( + "24245b3131342c3836343630363034343939333938372c4343452c0000000001005000130006012305000600070f1b004702060800000900000a00000b0000199d011a00000602d179570103b25ccc0604cf04862b0cc65b01000da4090d001c01000000010e0ccc010000b627be11000000002a41300d0a"), + Position.KEY_LOCK, true); + + verifyAttribute(decoder, buffer( + "$$u28,864606044993987,D82,0*D6"), + Position.KEY_RESULT, "D82,0"); + + verifyPositions(decoder, binary( + "24245B3139312C3836343630363034343939333938372C4343452C010000000200500013000601250500060007111B00470206080000093E000AE7030B0000199E011A850306028D7A570103F35ACC0604F9D06C2B0CB92E00000D3FA40C00250CA2B900010E0CCC010000B6276313000000004B00120006012A0500060007111B00470206080000093E000AE7030B0000199E011A7C0305028D7A570103F35ACC0604F9D06C2B0CB92E00000D3FA40C00010E0CCC010000B6276313000000002A31340D0A")); + verifyPositions(decoder, binary( "24246a3138312c3836343238313034313930383330332c4343452c00000000010093001f000505000600070714001502090800000900000a00000b0000160a001706001904001ad90440230006023279570103305ccc0604f536492b0c510300000d495701001c014000000b0e0ccc010000922781abb90c00002a030034212b03008b082c030053082d03009e082e030034212f030034213003003421310300342149090400000000000000004b07010104574946492a36310d0a")); diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java index cc8847db2..ac9854b8e 100644 --- a/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/MeitrackProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MeitrackProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java index 5e36abe5b..3f790d2f9 100644 --- a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java index 275672554..d14c020d4 100644 --- a/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MilesmateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MilesmateProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java index a8f1be855..712d59dc9 100644 --- a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -21,6 +21,10 @@ public class MiniFinderProtocolDecoderTest extends ProtocolTest { "!1,123456789012345")); verifyAttribute(decoder, text( + "!4,10,040123,,,1.0,110,0,0S,33"), + "phone1", "040123"); + + verifyAttribute(decoder, text( "!5,17,V,50"), Position.KEY_BATTERY_LEVEL, 50); diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java index 502f8e8bf..f61779a38 100644 --- a/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MiniFinderProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java index 0809996f6..126334767 100644 --- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java @@ -1,7 +1,8 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class Minifinder2ProtocolDecoderTest extends ProtocolTest { @@ -10,6 +11,20 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest { var decoder = inject(new Minifinder2ProtocolDecoder(null)); + verifyPositions(decoder, false, binary( + "ab105a0512e19404011001383632333131303632373037333735093743c3ec640000000009374dc3ec6400000000093750c3ec6400000080092455c3ec640203935e0f22a318d6c7baacd6a2546751467bd009246ac3ec640203b35e0f22a318d6c7baacd6a2546751467bd009246cc3ec640203b35e0f22a318d6c7baacd6a2546751467bd009247ec3ec640203b35e0f22a318d6c7baacd6a2546751467bd0092492c3ec640203b35e0f22a318d6c7baacd6a2546751467bd00924a6c3ec640203b35e0f22a318d6c7baacd6a2546751467bd00924bac3ec640203b35e0f22a318d6c7baacd6a2546751467bd00924d2c3ec640203b35e0f22a7083a2f201a83a3f8084f84ae560924e7c3ec640203b35e0f22a7083a2f201a83a3f8084f84ae560924fbc3ec640203b35e0f22a7083a2f201a83a3f8084f84ae5609240fc4ec640203b35e0f22a7083a2f201a83a3f8084f84ae56092423c4ec640203b35d0f22a7083a2f201a83a3f8084f84ae56092437c4ec640203cb5d0f22a7083a2f201a83a3f8084f84ae5609244fc4ec640003cb5d092464c4ec640003cb5d092478c4ec640003cb5d09248cc4ec640003cb5d0924a0c4ec640003cb5d0924b4c4ec640003cb5d0924ccc4ec640003cb5d0924e5c4ec640003cb5d0924fec4ec6400037b5d092413c5ec6400037b5d092427c5ec6400017b5d0924b785ed640003cb530924d085ed640003ab530924e985ed640003ab530924fe85ed640003ab5309241286ed640003ab5309242686ed640003ab5309243a86ed640003ab5309244e86ed640003ab5309246786ed640003ab5309248086ed640003ab5309249986ed6400037b530924b286ed6400037b530924c686ed6400037b530924da86ed6400037b530924ee86ed6400037b5309240287ed6400037b5309241687ed6400037b5309242f87ed6400037b5309244787ed640003835309246187ed640003835309247a87ed640003835309249287ed64000383530924ab87ed64000383530924c487ed64000383530924d987ed64000383530924ed87ed640003835309240188ed640003835309241588ed640003835309242988ed640003d35309243a88ed640003d3530d02000000803788ed640000000009374188ed640400000009244188ed640003d35309244288ed640003d35309374b88ed640500000009244b88ed640003d35309375588ed640500000009245588ed640003d35309245788ed640003d35309375f88ed640700000009245f88ed640003d35309376988ed640800000009246988ed640003d35309246b88ed640203d3530f22a502184a2cfba0a42c768af4ab5009247188ed640203d3530f22a502184a2cfba0a42c768af4ab5009377388ed640a00000009247688ed640203d3530f22a502184a2cfba0a42c768af4ab5009247b88ed640203d3530f22a502184a2cfba0a42c768af4ab5009377d88ed640300000009247e88ed640203d3530f22a502184a2cfba0a42c768af4ab5009248088ed640203d3530f22a502184a2cfba0a42c768af4ab5009248588ed640203d3530f22a502184a2cfba0a42c768af4ab5009378788ed640000000009248a88ed640203d3530f22a502184a2cfba0a42c768af4ab5009248f88ed640203d3530f22a502184a2cfba0a42c768af4ab5009379188ed640000000009379288ed640000008009249288ed640203d3530f22a502184a2cfba0a42c768af4ab5009249488ed640203d3530f22a502184a2cfba0a42c768af4ab5009249988ed640203d3530f22a502184a2cfba0a42c768af4ab5009249e88ed640203d3530f22a502184a2cfba0a42c768af4ab500924a388ed640203d3530f22a502184a2cfba0a42c768af4ab500924a688ed640203d3530f22a502184a2cfba0a42c768af4ab50")); + + verifyAttributes(decoder, binary( + "ab00cc029c9b0000020501040518200502cf290001100338363233313130363534393538393515043839343632303338303735303031383830343034070539eed3f9cec705064f93a7650507010b00002908cf2900010020050000d0000068915b00ae0000004f637420313920323032330031303a35393a3332261b53494d37353030457c4231315630325f3231303330337c50312e30325f3230323231313034050900000000050a00000003070b000000001e01070b010000001e01070b020000001e01070b030000001e01060c0000000000050d6e600580020e04050f0708008002100002110f02126406134d46303758041404a20e08160000000000000008160100000000000008160200000000000008160300000000000008160400000000000008160500000000000008160600000000000008160700000000000008160800000000000008160900000000000020177777772e676f6f676c652e636f6d2f6d6170733f713d252e37662c252e3766211868747470733a2f2f6c6f632e6d696e6966696e6465722e636f6d2f25732f25730519000001fe101a4d4630372e343631302e3233303300021c640a1d802acee6212966cf0803207b3e03217b040230000230010230020230030230040230050230060230070230080230090231000532580214010533820000000e406d326d2e74656c65322e636f6d014101421c4380431468756e7465726465762e6d696e6966696e6465722e636f6d0d440e01008005000000100e0000054505002c01014705500f1400f00d511002e803b84d7f0d0246f6430d511100f40100000000000000000d511200f40100000000000000000d511300f401000000000000000005527800030005532c0100000354500005551e002d400256050b57201c00002c010000ed61025d01055c890a0000057000000000037100001472000000000000000000000000000000000000000568f0000100057500000000074de36000000000")); + + verifyAttribute(decoder, binary( + "ab101c00d6f61e000110013836333932313033393939363038300937efd201640c000000"), + "barkCount", 12L); + + verifyAttribute(decoder, binary( + "ab00030008c700007f0100"), + Position.KEY_RESULT, "0"); + verifyAttribute(decoder, binary( "ab102600080f1400011001383633393231303339393833343736092429b347633003a96409020000008027b34763"), "bark", true); @@ -20,10 +35,10 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, binary( "ab10350015ae59010110013836333932313033333836353231360924723a12610042535a182ac0f6b4f2923100c900af02215c2b9bfb5461736b4c4d53")); - verifyNull(decoder, binary( + verifyPositions(decoder, false, binary( "ab10150076f1320003100133353534363530373130323933303602105a")); - verifyNull(decoder, binary( + verifyPositions(decoder, false, binary( "AB101400594A01000310013836333932323033343437333734350112")); verifyPositions(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java new file mode 100644 index 000000000..32c8a9ce6 --- /dev/null +++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolEncoderTest.java @@ -0,0 +1,28 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Command; +import org.traccar.model.Device; + +import static org.mockito.Mockito.when; + +public class Minifinder2ProtocolEncoderTest extends ProtocolTest { + + @Test + public void testEncodeNano() throws Exception { + + var encoder = inject(new Minifinder2ProtocolEncoder(null)); + + encoder.setModelOverride("Nano"); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_FIRMWARE_UPDATE); + command.set(Command.KEY_DATA, "https://example.com"); + + verifyCommand(encoder, command, binary("ab00160059d2010004143068747470733a2f2f6578616d706c652e636f6d")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java index fea74db7a..b6cc2ed77 100644 --- a/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java index 82781550e..8e51271b6 100644 --- a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MoovboxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java index f3ebb8e5c..9de24d87f 100644 --- a/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MotorProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MotorProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java index 8a5e228c7..86a72cc2d 100644 --- a/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MtxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MtxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java index 68a68c9e8..c1f02f0ae 100644 --- a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class MxtProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java index 8eda687cc..5b5865855 100644 --- a/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavigilProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavigilProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java index 29327f7e1..8678a55ba 100644 --- a/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavisFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java index 960ed1442..56f5a4c9f 100644 --- a/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavisProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java index d15d01cc0..8f25c2127 100644 --- a/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisetFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavisetFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java index d7643b50c..7722560ca 100644 --- a/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavisetProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavisetProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java index 360f92447..1509472a7 100644 --- a/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavtelecomFrameDecoderTest extends ProtocolTest { @@ -24,7 +24,7 @@ public class NavtelecomFrameDecoderTest extends ProtocolTest { } - @Ignore + @Disabled @Test public void testDecodeFull() throws Exception { diff --git a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java index 301a72b2a..d715ea596 100644 --- a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NavtelecomProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java index c63c19a29..67e88cb5a 100644 --- a/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NdtpV6ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NdtpV6ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java index 4e9e55f62..841315895 100644 --- a/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NeosProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NeosProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java index 239d892f8..d208b10c5 100644 --- a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NetProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java index 03aaa49aa..8fa74abd0 100644 --- a/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NiotProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NiotProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java index 3f1ec7aee..3a3461d43 100644 --- a/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NoranProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NoranProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java index d1b28525c..ddcc02418 100644 --- a/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/NoranProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java new file mode 100644 index 000000000..424eaadde --- /dev/null +++ b/src/test/java/org/traccar/protocol/NtoProtocolDecoderTest.java @@ -0,0 +1,19 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class NtoProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new NtoProtocolDecoder(null)); + + verifyPosition(decoder, text( + "^NB,880002023090601,N00,050923,233519,V,N,2236.1994,E,11315.4645,5,0,000000000000,460:00:0:75217090,-04:00,,1693971319,31,2DA5"), + position("2023-09-05 23:35:19.000", false, 22.60332, 113.25774)); + + } + +} diff --git a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java index dd5e1d9b9..d4dcdcc60 100644 --- a/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NvsFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class NvsFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java index 61d050679..0c8b41e4a 100644 --- a/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NvsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NvsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java index b3bd9aca7..9dcfe8a78 100644 --- a/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/NyitechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class NyitechProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java index 8272fe41e..53d910ddb 100644 --- a/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ObdDongleProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ObdDongleProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java index 6015f1d18..4541bf9c0 100644 --- a/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OigoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OigoProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java index 19c96ed9a..d3bd4fde4 100644 --- a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OkoProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java index 8e8d9b1cf..1a4365f3c 100644 --- a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OmnicommFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java index 5b3b08194..9f509718a 100644 --- a/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OmnicommProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OmnicommProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java index 5494301d8..e2db193d1 100644 --- a/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OpenGtsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OpenGtsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java index 408053496..21ccfa56d 100644 --- a/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OrbcommProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java index f5b98574c..5308568fd 100644 --- a/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OrionProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OrionProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java index 3b8a94613..c779e4c6e 100644 --- a/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OsmAndProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OsmAndProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java index edb7d1aad..7347da0fb 100644 --- a/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OutsafeProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OutsafeProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java index 03332e7fe..ba0eaec01 100644 --- a/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/OwnTracksProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class OwnTracksProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java index bde464162..4ae0e6d54 100644 --- a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PacificTrackProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java index 97020343f..afd131e95 100644 --- a/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PathAwayProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PathAwayProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java index 0dd00462d..cb101fa5f 100644 --- a/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PiligrimProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PiligrimProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java index 8b15d70a6..59f624eaa 100644 --- a/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PluginProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java index 8bf109d11..67bac7823 100644 --- a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PolteProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java index 8bc16d373..b86d7340e 100644 --- a/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PortmanProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PortmanProtocolDecoderTest extends ProtocolTest { @@ -11,6 +11,9 @@ public class PortmanProtocolDecoderTest extends ProtocolTest { var decoder = inject(new PortmanProtocolDecoder(null)); verifyPosition(decoder, text( + "%%863922034547720,A,231119031316,N3640.4542E11707.5992,000,000,NA,95000000,NA,254,24,1.00,24")); + + verifyPosition(decoder, text( "$EXT,P0RTMANGRANT,A,210609201710,N0951.6879W08357.0129,0,0,NA,NA,11,25,174700.25,NA,01820000,108")); verifyPosition(decoder, text( diff --git a/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java index b4c334a0c..41e78cf6c 100644 --- a/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PortmanProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PortmanProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java new file mode 100644 index 000000000..bf4f9c765 --- /dev/null +++ b/src/test/java/org/traccar/protocol/PositrexProtocolDecoderTest.java @@ -0,0 +1,18 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class PositrexProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new PositrexProtocolDecoder(null)); + + verifyPosition(decoder, binary( + "8280902e002b81c99fd607033905008b1c000003ae00003c9c000054ee00000079000000000d34d43f0fffffffda0000000000104fb80000204086464717807f8931082622128190980fffff862261047296590fffff")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java index 5dbde7846..ca4d5de07 100644 --- a/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PretraceProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PretraceProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java index d3218d4a8..da18441f5 100644 --- a/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PretraceProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class PretraceProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java index 8c2081641..6988f2f72 100644 --- a/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PricolProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PricolProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java index 9129a3079..ff205ce35 100644 --- a/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ProgressProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ProgressProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java index 172d85df6..45960d783 100644 --- a/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PstFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PstFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java index bc458c398..bbc17dbea 100644 --- a/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java @@ -2,7 +2,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PstFrameEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java index 880caf727..1fe7d7da4 100644 --- a/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/PstProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class PstProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java index 6c3ff71b6..b6530d815 100644 --- a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java index a5f5d7e77..234aff97b 100644 --- a/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt215ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt215ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java index f7b278139..b731b82ad 100644 --- a/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt3000ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt3000ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java index 854c789b8..f007dbb18 100644 --- a/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt502FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java index f310b2227..2fc9c8073 100644 --- a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java index c97093e26..98fe147b2 100644 --- a/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt502ProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Pt502ProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java index b198ac28e..5dca8a0c6 100644 --- a/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Pt60ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Pt60ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java new file mode 100644 index 000000000..1bbf17361 --- /dev/null +++ b/src/test/java/org/traccar/protocol/PuiProtocolDecoderTest.java @@ -0,0 +1,23 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.mqtt.MqttMessageBuilders; +import io.netty.handler.codec.mqtt.MqttQoS; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class PuiProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new PuiProtocolDecoder(null)); + + verifyNull(decoder, MqttMessageBuilders.connect().clientId( + "123456789012345").build()); + + verifyPosition(decoder, MqttMessageBuilders.publish().payload(buffer( + "{ \"id\": \"015262001044848\", \"ts\": \"2023-06-01T03:09:51.362Z\", \"rpt\": \"hf\", \"location\": { \"lat\": 33.91233, \"lon\": -84.20784 }, \"bear\": 70, \"spd\": 2482, \"ign\": \"on\" }")).qos(MqttQoS.EXACTLY_ONCE).messageId(1).build()); + + } + +} diff --git a/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java index a363022f0..4fbc4938e 100644 --- a/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/R12wProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class R12wProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java index ff40c19a3..c8ed2269d 100644 --- a/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RaceDynamicsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RaceDynamicsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java index b5a2555b1..81fdcfd0b 100644 --- a/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RadarProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RadarProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java new file mode 100644 index 000000000..86734a259 --- /dev/null +++ b/src/test/java/org/traccar/protocol/RamacProtocolDecoderTest.java @@ -0,0 +1,26 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; +import org.traccar.model.Position; + +public class RamacProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new RamacProtocolDecoder(null)); + + verifyAttributes(decoder, request(HttpMethod.POST, "/", + buffer("{\"PacketType\": 0,\"SeqNumber\": 4,\"UpdateDate\": \"2022-05-06 12:25:35\",\"Alert\": 42,\"AlertMessage\": \"Low Battery\",\"Mode\": 1,\"ModeText\": \"Help Me\",\"SigfoxTXInterval\": 2,\"GpsFixInterval\": 3,\"SigfoxTXIntervalText\": \"2 Seconds\",\"GpsFixIntervalText\": \"3 Seconds\",\"BatteryPercentage\": 4,\"Battery\": 0.1,\"Temperature\": -22,\"HwVersion\": 7,\"FirmwareVersion\": 8,\"DeviceId\": \"A10001\",\"DeviceType\": \"12\",\"DeviceTypeText\": \"RAMAC P1\"}"))); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("{\"PacketType\": 1,\"SeqNumber\": 4,\"UpdateDate\": \"2022-05-06 12:25:35\",\"Alert\": 0,\"AlertMessage\": \"\",\"Latitude\": -25.87586735939189,\"Longitude\": 28.179579268668846,\"Speed\": 1,\"COG\": 3,\"EstimatedAccuracy\": 3,\"LastLocation\": 0,\"LastLocationText\": \"NEW LOCATION\",\"IsMoving\": 0,\"IsMovingText\": \"STATIONARY\",\"GpsEvent\": 5,\"GpsEventText\": \"Heartbeat\",\"DeviceId\": \"A10001\",\"DeviceType\": \"12\",\"DeviceTypeText\": \"RAMAC P1\"}"))); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("{\"PacketType\": 2,\"SeqNumber\": 4,\"UpdateDate\": \"2022-05-06 12:25:35\",\"Alert\": 19,\"AlertMessage\": \"P1 Panic\",\"Event\": 16,\"DeviceId\": \"A10001\",\"DeviceType\": \"12\",\"DeviceTypeText\": \"RAMAC P1\",\"Latitude\": -25.875867359392,\"Longitude\": 28.179579268669,\"LocationDateTime\": \"2022-05-05 08:48:11\"}"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java index 3da671dbf..b951ef7b4 100644 --- a/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RaveonProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RaveonProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java index 5bdfd6816..defc5e8f9 100644 --- a/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RecodaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RecodaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java index eb591a5f6..d27979d1b 100644 --- a/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RetranslatorProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RetranslatorProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java index df19f01c6..8073c5459 100644 --- a/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RfTrackProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RfTrackProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java index 0d7eeb0df..7fb5f6335 100644 --- a/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RitiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RitiProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java index e4b30538c..f54cf8fcc 100644 --- a/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RoboTrackFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RoboTrackFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java index db1617c9e..5a83ae1b6 100644 --- a/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RoboTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RoboTrackProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java index b301507fb..a750d0311 100644 --- a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,9 +11,21 @@ public class RstProtocolDecoderTest extends ProtocolTest { var decoder = inject(new RstProtocolDecoder(null)); + verifyAttribute(decoder, text( + "RST;A;RST-MINI-4Gv3;V9.10;009521405;1;134;FIM;"), + Position.KEY_RESULT, "134"); + + verifyAttribute(decoder, text( + "RST;A;RST-MINIv5;V9.08;009767055;248;55;14-12-2023 19:34:20;14-12-2023 19:34:21;-12.923640;-38.388313;0;14;17;1;4;15;00;B0;00;1A;02;12.18;4.02;65;21;FE;0000;01;C0;001606017031;0002;FIM;"), + Position.KEY_DRIVER_UNIQUE_ID, "001606017031"); + verifyNull(decoder, text( "RST;A;RST-MINIv2;V7.04;008051261;124;29;04-04-2021 17:27:26;04-04-2021 17:27:26;-1.280811;-47.931755;7353;79;1;14;7315;26;10;0;1855;0;0;0;0;5;5;-1.280821;-47.931747;04-04-2021 17:52:23;6;-1.280863;-47.931770;04-04-2021 18:12:19;5;-1.280844;-47.931763;04-04-2021 17:28:02;5;-1.280900;-47.931770;04-04-2021 19:04:27;4;-1.280843;-47.931747;04-04-2021 18:21:45;04-04-2021 19:29:59;04-04-2021 19:29:59;-1.280770;-47.931595;1;15;0;0;0;0;FIM;")); + verifyAttribute(decoder, text( + "RST;A;RST-MINI-4Gv3;V9.10;009521405;13;1;21-11-2023 20:04:18;21-11-2023 20:04:18;-12.923627;-38.388287;1;165;29;1;5;2;00;B0;00;1A;02;11.89;3.90;73;31;FE;0000;01;40;00800061;0;184;2;4;4;6;434.0000;2;0;-49;1815;37391;724;255;263;00000000;FIM;"), + Position.KEY_IGNITION, true); + verifyPosition(decoder, text( "RST;L;RST-MINIv2;V7.02;008068078;61;1;27-01-2020 21:36:33;27-01-2020 21:36:33;-16.696159;-49.284275;0;67;786;1;15;0;00;B0;00;19;06;12.42;4.16;79;20;FE;0000;01;E0;00800020;0;467;FIM;")); diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java index 89d4a02cc..e98dffdef 100644 --- a/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/RuptelaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class RuptelaProtocolDecoderTest extends ProtocolTest { @@ -13,6 +13,9 @@ public class RuptelaProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "002e000316d53d58d6020f4573303430302e30332e36382e30340000c2b3090d0e950000827b000003e80000003c003c1681")); + verifyPositions(decoder, false, binary( + "03fb0003137ca79f856d01011d386d438b000080000000800000008000ffffffffffff070220211bff011d30c50000386d438b000080000000800000008000ffffffffffff0702201f1bff011d30c30000386d43a9000180000000800000008000ffffffffffffad0320211b16ad01011d30950000386d43c7000080000000800000008000ffffffffffff070220211b00011d30a60000386d4403000080000000800000008000ffffffffffff070220221b00011d30ae0000386d443f000080000000800000008000ffffffffffff070220231b00011d30ae000064c692cb000080000000800000008000ffffffffffff070220231b18011d3091000064c69306000080000000800000008000ffffffffffff070220231b14011d30a7000064c69322000180000000800000008000ffffffffffffad0320241b14ad00011d30a3000064c69342000080000000800000008000ffffffffffff070220241b13011d30ad000064c6934a000180000000800000008000ffffffffffffad0320241b10ad01011d30c3000064c6937e000080000000800000008000ffffffffffff070220241b12011d3092000064c6938b000180000000800000008000ffffffffffffad0320241b12ad00011d30bd000064c69395000180000000800000008000ffffffffffffad0320241b10ad01011d30a6000064c693ba000080000000800000008000ffffffffffff070220251b17011d30a2000064c693d4000180000000800000008000ffffffffffffad0320251b17ad00011d30cc000064c693f6000080000000800000008000ffffffffffff070220251b15011d3090000064c69404000180000000800000008000ffffffffffffad0320251b16ad01011d30a9000064c69432000080000000800000008000ffffffffffff070220261b14011d30be000064c6946d000180000000800000008000ffffffffffffad0320261b15ad00011d30b1000064c6946e000080000000800000008000ffffffffffff070220261b15011d3096000064c694aa000080000000800000008000ffffffffffff070220261b15011d30a8000064c694b2000180000000800000008000ffffffffffffad0320261b15ad01011d30a5000064c694e6000080000000800000008000ffffffffffff070220261b17011d309a000064c694f5000180000000800000008000ffffffffffffad0320261b17ad00011d309c000064c694f6000180000000800000008000ffffffffffffad0320261b17ad01011d3099000064c69522000080000000800000008000ffffffffffff070220261b14011d3094000064c6955e000080000000800000008000ffffffffffff070220261b15011d30b2000064c6959a000080000000800000008000ffffffffffff070220261b14011d30970000ad9e")); + verifyPositions(decoder, binary( "00800003167d765c155d01000160cd0a310000faae43f7176ee45702332b0c12000006070d05007300cfff260082008600870088000f00d7021100d801c900061d0000c500001e0e988300008900008b000002d0000c9bca720c889a0b047e00000000000000007f0000000000000000800000000000000000810000000000000000a341")); diff --git a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java index 0c4fc6767..bb6c098f0 100644 --- a/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/RuptelaProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java index 2b7f40c82..cebae3ef6 100644 --- a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class S168ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java index 15b1d0451..689de29b2 100644 --- a/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SabertekFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SabertekFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java index 6aafa325f..b55681ffd 100644 --- a/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SabertekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SabertekProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java index d0ae6fabf..5e1a24f3b 100644 --- a/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SanavProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SanavProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java index 57398dd84..f01205155 100644 --- a/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SanulProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SanulProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java index 0fe16377d..c58971d02 100644 --- a/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SatsolProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SatsolProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java index 66d5f5e69..c7d0671c0 100644 --- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java @@ -1,10 +1,13 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Device; import org.traccar.model.Position; +import static org.mockito.Mockito.when; + public class SigfoxProtocolDecoderTest extends ProtocolTest { @Test @@ -43,6 +46,11 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest { verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("%7B++%22device%22%3A%222BF839%22%2C++%22time%22%3A1510605882%2C++%22duplicate%22%3Afalse%2C++%22snr%22%3A45.61%2C++%22station%22%3A%2235A9%22%2C++%22data%22%3A%2200bd6475e907398e562d01b9%22%2C++%22avgSnr%22%3A45.16%2C++%22lat%22%3A-38.0%2C++%22lng%22%3A145.0%2C++%22rssi%22%3A-98.00%2C++%22seqNumber%22%3A228+%7D="))); + decoder.setModelOverride("Amber"); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("{ \"deviceId\":\"284019F\", \"timestamp\":\"1707375610\", \"seqNo\":\"42\", \"data\":\"0100b019ffe8645d0019e513\", \"linkQuality\":\"Excellent\", \"operator\":\"SIGFOX_South_Africa_Sqwidnet\", \"country\":\"710\" }"))); + } } diff --git a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java index 0d7eb4f16..e599ff36d 100644 --- a/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SiwiProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SiwiProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java index e59e3c3f0..d2bf0f825 100644 --- a/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SkypatrolProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SkypatrolProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java index 258eb0fce..9bad24865 100644 --- a/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SmartSoleProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SmartSoleProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java index de025c18e..a8a9223c8 100644 --- a/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SmokeyProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SmokeyProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java index 445628f6d..a18c7ee9b 100644 --- a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java index 03d0e97f0..70b645b8f 100644 --- a/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SpotProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.handler.codec.http.HttpMethod; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SpotProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java index 0a6ad0163..f06bb9aac 100644 --- a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java index 84c470970..9f11db2e7 100644 --- a/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StarcomProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class StarcomProtocolDecoderTest extends ProtocolTest { @@ -11,6 +11,9 @@ public class StarcomProtocolDecoderTest extends ProtocolTest { var decoder = inject(new StarcomProtocolDecoder(null)); verifyPosition(decoder, text( + "|unit=579978,unittype=5,address=196.190.61.110,kind=1,pending=0,mileage=127268.864,odometer=339863,logic_state=1,reason=20,eventid=1,response=0,longitude=40.86503,latitude=9.06824,altitude=1809,gps_valid=1,gps_connected=1,satellites=7,velocity=23,heading=130,emergency=0,driver=0,ignition=1,door=1,arm=0,disarm=0,extra1=0,extra2=0,extra3=0,siren=0,lock=0,immobilizer=0,unlock=0,fuel=0,rpm=0,modemsignal=0,main_voltage=14.11,backup_voltage=100.00,analog1=3.38,analog2=0.00,analog3=0.00,datetime_utc=2023/08/24 14:56:29,datetime_actual=2023/08/24 14:56:23,network=TCPIP 6600|\r\n")); + + verifyPosition(decoder, text( "|unit=416307,unittype=5,address=186.167.243.28,kind=14,software_version=14.02.18,hardware_type=17,gps_type=6,longitude=-67.85891,latitude=10.21988,datetime_actual=2019/05/07 21:59:38,network=TCPIP.1|\r\n")); verifyAttributes(decoder, text( diff --git a/src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java new file mode 100644 index 000000000..789126471 --- /dev/null +++ b/src/test/java/org/traccar/protocol/StartekFrameDecoderTest.java @@ -0,0 +1,23 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class StartekFrameDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new StartekFrameDecoder()); + + verifyFrame( + binary("26265c3134332c3836353439313036393537333134302c3030302c302c2c3234303130343136303031362c412c2d362e3239303633382c3130362e3830393537382c31352c302e382c302c3238322c35372c3338382c3531307c31307c303444457c30303030373442452c33312c30303030303033432c30302c30302c303444347c303141327c303030307c303030302c312c2c2c31350d0a"), + decoder.decode(null, null, binary("26265c3134332c3836353439313036393537333134302c3030302c302c2c3234303130343136303031362c412c2d362e3239303633382c3130362e3830393537382c31352c302e382c302c3238322c35372c3338382c3531307c31307c303444457c30303030373442452c33312c30303030303033432c30302c30302c303444347c303141327c303030307c303030302c312c2c2c31350d0a"))); + + verifyFrame( + binary("26265c3534362c3836353439313036313134353937302c3731302c54312c302e302c302e302c302e302c302e302c302c302c302c302c302e302c302c302e302c302c46302c302e302c302e302c302e302c302e302c302e302c302c302e302c302c302c302c302c302c312c302c302e302c30302c302e302c302e302c302e302c300d0a54322c302e3030302c302e302c393232333337323033363835343737353830382e382c393232333337323033363835343737353830382e382c343239343936373239352c343239343936373239352c302c3432393439363732392c302c302c302c302c302c302c302c302c302c302c302e302c32313437343833362e302c393232333337323033363835343737353830382e382c302c30302c302c302c302e30300d0a54352c302c302c302c302c302c302c302c302c302c302c302c302c302e302c302e302c2a2c2a2c300d0a54362c30302c30332c30302c31462c31462c31462c30452c30332c30302c30302c30302c30302c31462c31460d0a54372c302c302c302c3432393439363732392c32313437343833362e3030302c3432393439363732392c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302c302c302e3030300d0a54782c2a2c2a2c2a2c2a2c2a2c302e302c302c302c302c302c2d3132352c302c302c302c302c302c302c300d0a46330d0a"), + decoder.decode(null, null, binary("26265c3534362c3836353439313036313134353937302c3731302c54312c302e302c302e302c302e302c302e302c302c302c302c302c302e302c302c302e302c302c46302c302e302c302e302c302e302c302e302c302e302c302c302e302c302c302c302c302c302c312c302c302e302c30302c302e302c302e302c302e302c300d0a54322c302e3030302c302e302c393232333337323033363835343737353830382e382c393232333337323033363835343737353830382e382c343239343936373239352c343239343936373239352c302c3432393439363732392c302c302c302c302c302c302c302c302c302c302c302e302c32313437343833362e302c393232333337323033363835343737353830382e382c302c30302c302c302c302e30300d0a54352c302c302c302c302c302c302c302c302c302c302c302c302c302e302c302e302c2a2c2a2c300d0a54362c30302c30332c30302c31462c31462c31462c30452c30332c30302c30302c30302c30302c31462c31460d0a54372c302c302c302c3432393439363732392c32313437343833362e3030302c3432393439363732392c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302e3030302c302c302c302c302e3030300d0a54782c2a2c2a2c2a2c2a2c2a2c302e302c302c302c302c302c2d3132352c302c302c302c302c302c302c300d0a46330d0a"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java index 072c19942..0d9516256 100644 --- a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,42 +11,58 @@ public class StartekProtocolDecoderTest extends ProtocolTest { var decoder = inject(new StartekProtocolDecoder(null)); + verifyAttributes(decoder, text( + "&&\\546,865491061145970,710,T1,0.0,0.0,0.0,0.0,0,0,0,0,0.0,0,0.0,0,F0,0.0,0.0,0.0,0.0,0.0,0,0.0,0,0,0,0,0,1,0,0.0,00,0.0,0.0,0.0,0\r\n", + "T2,0.000,0.0,9223372036854775808.8,9223372036854775808.8,4294967295,4294967295,0,429496729,0,0,0,0,0,0,0,0,0,0,0.0,21474836.0,9223372036854775808.8,0,00,0,0,0.00\r\n", + "T5,0,0,0,0,0,0,0,0,0,0,0,0,0.0,0.0,*,*,0\r\n", + "T6,00,03,00,1F,1F,1F,0E,03,00,00,00,00,1F,1F\r\n", + "T7,0,0,0,429496729,21474836.000,429496729,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0,0,0.000\r\n", + "Tx,*,*,*,*,*,0.0,0,0,0,0,-125,0,0,0,0,0,0,0\r\n", + "F3\r\n")); + + verifyPosition(decoder, text( + "&&l141,863911061945394,000,0,,230918072531,A,22.678598,114.045970,26,0.6,0,0,74,2286304571,460|0|249F|00001093,20,001C,00,00,04A7|019C|0000|0000,1,C0\r\n")); + + verifyAttribute(decoder, text( + "&&s148,868703050178631,000,37,,230704040211,A,22.678565,114.046011,31,0.5,0,339,77,8,460|0|249F|0AC2620D,27,0000001D,02,00,04F2|01A1|0000|0000,129,,,,949037\r\n"), + Position.KEY_HOURS, 9490000L); + verifyAttribute(decoder, text( - "&&x164,869926040743375,000,0,,220705205955,A,33.326001,44.445318,10,1.2,0,57,8,925,418|40|038C|000083CD,31,00000015,00,00,0016|016A|0000|0000,1,,,686|33||44|99|14|124|11|8D"), + "&&x164,869926040743375,000,0,,220705205955,A,33.326001,44.445318,10,1.2,0,57,8,925,418|40|038C|000083CD,31,00000015,00,00,0016|016A|0000|0000,1,,,686|33||44|99|14|124|11|8D\r\n"), Position.KEY_FUEL_CONSUMPTION, 1.1); verifyAttribute(decoder, text( - "&&R187,860294046453690,000,0,,220105160656,A,22.994986,72.499711,15,0.9,2,222,55,121135784,404|98|147B|0000376A,24,0000001F,02,00,052E|01A3|0000|0000,1,010000|020000,,853|6|10|105|73|41|125|34|52"), + "&&R187,860294046453690,000,0,,220105160656,A,22.994986,72.499711,15,0.9,2,222,55,121135784,404|98|147B|0000376A,24,0000001F,02,00,052E|01A3|0000|0000,1,010000|020000,,853|6|10|105|73|41|125|34|52\r\n"), Position.KEY_FUEL_LEVEL, null); verifyPosition(decoder, text( - "&&o142,860262050066062,000,27,,211111070826,V,28.653435,-106.077455,0,0.0,0,151,1412,918,0|0|4708|01402D19,6,0000001A,02,00,04C0|016C|0000|0000,1,,,BB")); + "&&o142,860262050066062,000,27,,211111070826,V,28.653435,-106.077455,0,0.0,0,151,1412,918,0|0|4708|01402D19,6,0000001A,02,00,04C0|016C|0000|0000,1,,,BB\r\n")); verifyPosition(decoder, text( - "&&W149,865429043319537,000,0,,211103013512,A,22.679003,114.045085,16,1.1,0,271,76,109075,460|0|249F|000010C5,19,0000003E,00,00,0A57|0168|0000|0000,1,0100000C")); + "&&W149,865429043319537,000,0,,211103013512,A,22.679003,114.045085,16,1.1,0,271,76,109075,460|0|249F|000010C5,19,0000003E,00,00,0A57|0168|0000|0000,1,0100000C\r\n")); verifyAttribute(decoder, text( - "&&:23,860262050015424,129,OKA2"), - Position.KEY_RESULT, "129,OK"); + "&&:23,860262050015424,129,OKA2\r\n"), + Position.KEY_RESULT, "OK"); verifyPosition(decoder, text( - "&&X152,861157040151686,000,18,,210907163833,A,10.232715,-67.880423,11,1.4,0,275,437,34804,734|2|3EE4|00579406,28,00000015,00,00,0000|017D|0000|0000,1,010000,,9A")); + "&&X152,861157040151686,000,18,,210907163833,A,10.232715,-67.880423,11,1.4,0,275,437,34804,734|2|3EE4|00579406,28,00000015,00,00,0000|017D|0000|0000,1,010000,,9A\r\n")); verifyPosition(decoder, text( - "&&o125,861157040554384,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0")); + "&&o125,861157040554384,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0\r\n")); verifyAttribute(decoder, text( - "&&a152,860262050010565,000,53,8F5300,210528015706,A,-38.229746,145.043446,6,1.5,0,285,84,2102994,505|1|306E|082D6101,31,0000003D,02,02,04C0|01A0|0000|0000,1,,DC"), + "&&a152,860262050010565,000,53,8F5300,210528015706,A,-38.229746,145.043446,6,1.5,0,285,84,2102994,505|1|306E|082D6101,31,0000003D,02,02,04C0|01A0|0000|0000,1,,DC\r\n"), Position.KEY_DRIVER_UNIQUE_ID, "8F5300"); verifyPosition(decoder, text( - "&&>141,860262050010565,000,36,,210407094323,V,-38.229711,145.043161,0,0.0,0,0,0,14222,505|1|306E|082D6115,24,00000039,00,00,04C0|0164|0000|0000,1,,41")); + "&&>141,860262050010565,000,36,,210407094323,V,-38.229711,145.043161,0,0.0,0,0,0,14222,505|1|306E|082D6115,24,00000039,00,00,04C0|0164|0000|0000,1,,41\r\n")); verifyPosition(decoder, text( - "&&A147,021104023195429,000,0,,180106093046,A,22.646430,114.065730,8,0.9,54,86,76,326781,460|0|27B3|0EA7,27,0000000F,02,01,04E2|018C|01C8|0000,1,0104B0,01013D|02813546")); + "&&A147,021104023195429,000,0,,180106093046,A,22.646430,114.065730,8,0.9,54,86,76,326781,460|0|27B3|0EA7,27,0000000F,02,01,04E2|018C|01C8|0000,1,0104B0,01013D|02813546\r\n")); verifyPosition(decoder, text( - "&&y139,860262050009146,000,0,,210323131512,A,22.678655,114.046223,14,1.1,0,231,71,5,460|0|249F|0099C257,28,0000003D,00,00,0493|0199|0000|0000,1,,33")); + "&&y139,860262050009146,000,0,,210323131512,A,22.678655,114.046223,14,1.1,0,231,71,5,460|0|249F|0099C257,28,0000003D,00,00,0493|0199|0000|0000,1,,33\r\n")); } diff --git a/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java index f04d0cb67..c87e421f2 100644 --- a/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/StartekProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class StartekProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java index c618ac21c..dbdeee4d7 100644 --- a/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class StbProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java index 3ef4b1901..353fe4317 100644 --- a/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Stl060ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Stl060ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java index 6d0351a8e..0db6e756c 100644 --- a/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SuntechFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SuntechFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java index 107c03d36..d656bba13 100644 --- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -12,6 +12,9 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { var decoder = inject(new SuntechProtocolDecoder(null)); + verifyAttributes(decoder, buffer( + "ST410STT;109815653;445;03;16531;724;10;-77;6011;7;16273;724;10;7511;0;0;13903;724;10;6011;0;0;7671;724;10;7111;0;0;16533;724;10;6011;0;0;0;0;0;0;0;0;0;0;0;0;0;0;3.86;0;0098;1;003;;;;;;;;;")); + verifyPosition(decoder, buffer( "ALT;0840037569;FFFFFF;84;1.0.6;0;20221228;11:33:05;00004490;724;11;05D3;33;-22.845935;-46.322000;0.00;0.00;18;0;00000001;00000000;99;;;;08E3800F;4.1;12.37;0;0;0;0;4;;;;")); @@ -209,7 +212,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { } - @Ignore + @Disabled @Test public void testDecodeCrash() throws Exception { @@ -230,13 +233,13 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { decoder.setIncludeAdc(true); verifyAttribute(decoder, buffer( - "ST600STT;008594432;20;492;20200212;18:58:30;060bb0e1;334;20;36bb;45;+19.337897;-099.064489;000.398;000.00;12;1;5049883;13.61;100100;2;1198;013762;4.2;1;4.68"), + "ST600STT;008594432;20;492;20200212;18:58:30;060bb0e1;334;20;36bb;45;+19.337897;-099.064489;000.398;000.00;12;1;5049883;13.61;100100;2;1198;013762;4.2;1;4.68"), Position.PREFIX_ADC + 1, 4.68); decoder.setIncludeTemp(true); verifyAttribute(decoder, buffer( - "ST600STT;008350848;35;523;20191102;13:49:46;0bf14fdb;334;20;2f19;57;+20.466737;-100.825455;000.006;000.00;11;1;10274175;11.36;00000000;1;0300;018353;4.2;1;0.00;;;;00000000000000;0;28EE56B911160234:+13.7;:;:"), + "ST600STT;008350848;35;523;20191102;13:49:46;0bf14fdb;334;20;2f19;57;+20.466737;-100.825455;000.006;000.00;11;1;10274175;11.36;00000000;1;0300;018353;4.2;1;0.00;;;;00000000000000;0;28EE56B911160234:+13.7;:;:"), Position.PREFIX_TEMP + 2, 13.7); verifyPosition(decoder, buffer( @@ -259,7 +262,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { decoder.setIncludeRpm(true); verifyAttribute(decoder, buffer( - "ST300STT;907131077;04;706;20190227;23:59:34;cc719;-12.963490;-038.499587;000.067;000.00;7;1;57095;12.50;000000;1;0337;000207;0.0;1;0;012E717F010000;1"), + "ST300STT;907131077;04;706;20190227;23:59:34;cc719;-12.963490;-038.499587;000.067;000.00;7;1;57095;12.50;000000;1;0337;000207;0.0;1;0;012E717F010000;1"), Position.KEY_RPM, 0); } @@ -272,7 +275,7 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { decoder.setHbm(true); verifyAttribute(decoder, buffer( - "ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"), + "ST300ALT;007239104;40;313;20190112;01:07:16;c99139;+04.703287;-074.148897;000.000;189.72;21;1;425512;12.61;100000;33;003188;4.1;1"), Position.KEY_HOURS, 3188 * 60000L); } @@ -283,13 +286,19 @@ public class SuntechProtocolDecoderTest extends ProtocolTest { var decoder = inject(new SuntechProtocolDecoder(null)); verifyAttribute(decoder, buffer( - "ST300HTE;511050566;45;308;20200909;13:38:38;0;12.50;001354;0.0;1;0;1;1;0;-27.636632;-052.277933;-27.636675;-052.277947;000.000;002.296;0;00000000000000"), + "ST300HTE;511050566;45;308;20200909;13:38:38;0;12.50;001354;0.0;1;0;1;1;0;-27.636632;-052.277933;-27.636675;-052.277947;000.000;002.296;0;00000000000000"), Position.KEY_DRIVER_UNIQUE_ID, "00000000000000"); verifyAttribute(decoder, buffer( - "ST300HTE;100850001;04;248;20110101;00:13:52;167559;12.28;004005;0.0;1;0;3;3;0;-22.881018;-047.070831;-22.881018;-047.070831;000.000;000.000;0;0;3;0;0;0;01E04D44160000"), + "ST300HTE;100850001;04;248;20110101;00:13:52;167559;12.28;004005;0.0;1;0;3;3;0;-22.881018;-047.070831;-22.881018;-047.070831;000.000;000.000;0;0;3;0;0;0;01E04D44160000"), Position.KEY_DRIVER_UNIQUE_ID, "01E04D44160000"); + decoder.setHbm(true); + + verifyAttribute(decoder, buffer( + "ST300STT;807469112;45;315;20231215;15:25:03;104147;-16.030168;-047.989150;000.000;000.00;19;1;8600;12.14;000010;1;0456;000373;4.1;1;01B54221010000;0"), + Position.KEY_DRIVER_UNIQUE_ID, "01B54221010000"); + } } diff --git a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java index e96c9b62d..4fa209fb7 100755 --- a/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SupermateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SupermateProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java index fb9053706..694a178f4 100644 --- a/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SviasProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SviasProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java index 923b7abfb..6b473b38c 100644 --- a/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/SwiftechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class SwiftechProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java index 1622f64f2..91020714c 100644 --- a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,10 +11,22 @@ public class T55ProtocolDecoderTest extends ProtocolTest { var decoder = inject(new T55ProtocolDecoder(null)); + verifyAttributes(decoder, text( + "$GPTXT,NET,1003,A1,-53,232 01*77")); + verifyPosition(decoder, text( "$PUBX,00,130209.00,3650.51159,N,01346.10602,E,785.947,D3,4.1,5.2,0.163,87.43,-0.054,7.0,0.88,1.21,0.88,24,01012,0*6D")); verifyPosition(decoder, text( + "$GNRMC,164414.90,A,4650.5156500,N,01246.1059604,E,0.018,,091123,,,A,V*15")); + + verifyPosition(decoder, text( + "$GNGGA,164414.90,4650.5156500,N,01246.1059604,E,1,12,0.84,740.729,M,44.804,M,,*4E")); + + verifyNull(decoder, text( + "$GNGLL,4650.5156500,N,01246.1059604,E,164414.90,A,A*77")); + + verifyPosition(decoder, text( "QZE,868994033976700,35,28062020,113553,22.13673,114.57263,0,22,A,0")); verifyNull(decoder, text( diff --git a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java index 40d5bc9e9..1e39298fa 100644 --- a/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T57FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class T57FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java index 2e097562b..e0fd2e800 100644 --- a/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T57ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class T57ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java new file mode 100644 index 000000000..ff5d5b0a0 --- /dev/null +++ b/src/test/java/org/traccar/protocol/T622IridiumProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class T622IridiumProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new T622IridiumProtocolDecoder(null)); + + decoder.setFormat("01,02,03,04,05,08"); + + verifyPosition(decoder, binary( + "01003501001c68b2cb1733303034333430363735343836353000016e000064b5f497020013234c5ea0ff1c365d0600b1482c010000cf0004"), + position("2023-07-18 02:10:08.000", true, -6.26732, 106.77200)); + + } + +} diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java index 10d0aad59..468751e1b 100644 --- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -12,6 +12,12 @@ public class T800xProtocolDecoderTest extends ProtocolTest { var decoder = inject(new T800xProtocolDecoder(null)); verifyAttributes(decoder, binary( + "25251300594a1b0869738060144917003c0e101e03e85a2dc8c00005070000410000000000000000000000005b000003a5b45e00230919102252e3a5094288fabfc0e98b15420000010403921352ffff0000001cffffffffff25251300594a1c0869738060144917003c0e101e03e85a2ac7c00005070000410000000000000000000000002d000003a5b48b002309191023522d320642abfebfc0e98b15420000010c03921345ffff0000001bffffffffff25251300594a1d0869738060144917003c0e101e03e85a1ac9c000050700004100000000000000000000000024000003a5b4af00230919102452b81ef9410002c0c0ec8b15420108011403911345ffff0000001dffffffffff25251300594a1e0869738060144917003c0e101e03e85a3ec7c00005070000410000000000000000000000000e000003a5b4bd002309191025060ad7ec41da02c0c0058c15420084016303921345ffff0000001cffffffffff25251300594a1f0869738060144917003c0e101e03e85a3ec7c020050700004100000000000000000000000005000003a5b4c2002309191025090e2deb410203c0c0108c15420089014303921338ffff0000001dffffffffff25251300594a200869738060144917003c0e101e03e85a1ec5c000050700004100000000000000000000000020000003a5b4e20023091910260948e1bc412205c0c0458c15420040013603921355ffff0000001bffffffffff25251300594a210869738060144917003c0e101e03e85a00c5c020050700004100000000000000000000000000000003a5b4e20023091910270948e1bc412205c0c0458c15420040013603911332ffff0000001dffffffffff")); + + verifyAttributes(decoder, binary( + "272704004901380864112055585747c612230321220006000036435fc8acc2ee600f420000000000000000909019003900001356a18000012c0000a8c00000001e20d4800000c00000")); + + verifyAttributes(decoder, binary( "2525110055000208677300508924902206262035310c540045004c00430045004c0004454447450847534d20313930300f323134303734323036373835323839143839333430373131373930303936383037363846")); verifyAttributes(decoder, binary( diff --git a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java index b2d7c57a2..4741f5c92 100644 --- a/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/T800xProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java index 36c9d9148..197e30c15 100644 --- a/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TaipProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,6 +11,18 @@ public class TaipProtocolDecoderTest extends ProtocolTest { var decoder = inject(new TaipProtocolDecoder(null)); + verifyAttributes(decoder, text( + ">RUS00,010170000000+0000000+000000000000001009999000011060074755268EF,0001139503871486,01,ZZZZZZZZZZ;ID=11817;#LOG:6AE4;*2C<")); + + verifyPosition(decoder, text( + ">RPI041220132203-2683525-065204060150001050000101511140022118857EF27;ID=0000;#LOG:DECB;*07<")); + + verifyPosition(decoder, text( + ">RCQ00151123235718-2782354-06407582055121FF0013501CDCC6313011100001514;#0805;ID=SIA056;*15<")); + + verifyNull(decoder, text( + ">RTT151123153149-4330468-06503640000009300DF2101 04101203 000 00000000130000040414;ID=8803;#1ABD;*2B<")); + verifyAttribute(decoder, text( ">RUS00,111220124402-3138067-06417623000012200FF,000000000000000000000000000,0000000111,15640422,00000,+25.5,00000,51;ID=CST3G0443;#IP1:089F;*34<"), Position.PREFIX_TEMP + 1, 25.5); diff --git a/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java index b4617cb61..28a917096 100644 --- a/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TechTltProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TechTltProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java index 36c3b578b..1e1f8d431 100644 --- a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TechtoCruzFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java index 459401469..b512f9968 100644 --- a/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TechtoCruzProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java index 98b2b80c4..fa6a490a4 100644 --- a/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TekFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TekFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java index 17910a8d6..9976b9bac 100644 --- a/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TekProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java index 9f36b3f96..8770e9451 100644 --- a/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TelemaxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TelemaxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java index dc6cc58c6..ba3808534 100644 --- a/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TelicFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TelicFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java index 214bb06c7..44e63b8a6 100644 --- a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TelicProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java index adc768460..8d0cafcd9 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TeltonikaFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java index ba64642f2..2a8ff87b6 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java @@ -1,8 +1,9 @@ package org.traccar.protocol; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TeltonikaProtocolDecoderTest extends ProtocolTest { @@ -15,6 +16,17 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { "000F313233343536373839303132333435")); verifyPositions(decoder, binary( + "00000000000000728e010000018b23dd796300fbf7263c24f9e11a0000000000000002240001000000000000000000010224004501210001e50110cde39f7e42bb55aa788e4a29ed650055020ab70a8f264c6000ffff6b210001b00110f89b907e42bb55aaa3463b29ed650055020ab708bb2600ae0500096c01000051d4")); + + verifyAttribute(decoder, binary( + "000000000000004b8e010000018368952793000f0e54fc209ab05800b300b40e00002a4f0001000000000000000000012a4f001e011c0001a40110eb47706aa38255aa96f21a154e2d00550d01000e020bd6010000823f"), + "tag1Battery", 3030); + + verifyAttribute(decoder, binary( + "00000000000000240d01060000001c642b3ad14754534c7c367c317c307c31323734393838347c317c0d0a010000ec11"), + Position.KEY_DRIVER_UNIQUE_ID, "12749884"); + + verifyPositions(decoder, binary( "00000000000000a28e0100000183ac617e3001123eb99b1e142db4000000000000000000001d000900f000005000001503004500011e1801212d01242a012722012a18001100b5000000b600000018000000cd151000431c2d011f6981012047d701226981012347d901256981012647d8012869810129e6f304b0000304b1000304b2000304b30003000100f10000639d0002000b0000000214bf12fe000e0000000029d18c95000001000051b6")); verifyPositions(decoder, binary( @@ -158,7 +170,7 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest { } - @Ignore + @Disabled @Test public void testDecodeConnectionless() throws Exception { diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java index d7e1149aa..04e8afe0b 100644 --- a/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java index fc66f53bb..674db0ad5 100644 --- a/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TeraTrackProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TeraTrackProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java index 2085112ec..0271bf3a2 100644 --- a/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ThinkPowerProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ThinkPowerProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java index 859dd4f89..73e40ce55 100644 --- a/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ThinkRaceProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ThinkRaceProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java index 90431fa24..b9bc1dbc2 100644 --- a/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/ThurayaProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class ThurayaProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java index 3bcc9994a..2c495d09d 100644 --- a/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk102ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tk102ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java index 87c3d9317..a3cfa24cf 100644 --- a/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk103FrameDecoderTest.java @@ -1,7 +1,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tk103FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java index 8b3177136..a3b3fa86b 100644 --- a/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk103ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -12,6 +12,13 @@ public class Tk103ProtocolDecoderTest extends ProtocolTest { var decoder = inject(new Tk103ProtocolDecoder(null)); verifyAttributes(decoder, text( + "(007030201454BS5190:02150000753001DC,91:0EE8060EDC0A01DC,92:42014201DC0A01DC,93:00010127000037C8,94:0E01000002000000,95:020EE10EE20EE800030EE40EE00EE700040EDD0EE40EE400050EDC0EDF0EE400,96:0142000000000000,97:0000000000000000,98:0000000000000000)")); + + verifyAttribute(decoder, text( + "(352602014867BS500064FF0EF10FF10FF00FF20FF30FF20FF20FF40FF20FF40FF40FF20FF30FF20F0000000000000000000000000000000000000000000000001663000000010004000000000000000002444444420000000000A00FA000000000000000200000000315E2000000)"), + "batteryTemp2", 26); + + verifyAttributes(decoder, text( "(027046434858BZ00,{460,0,20949,58711}\n{460,0,20494,54003}\n{460,0,20951,19569}\n,01000000)")); verifyAttributes(decoder, text( diff --git a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java index 359e432c7..57ebbaec1 100644 --- a/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Tk103ProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Tk103ProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java index a6a9f836e..085d6fc5b 100644 --- a/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tlt2hProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -11,7 +11,28 @@ public class Tlt2hProtocolDecoderTest extends ProtocolTest { var decoder = inject(new Tlt2hProtocolDecoder(null)); - verifyNull(decoder, text( + verifyPositions(decoder, false, text( + "#862255061752835#MT710#0000#AUTO#1\r\n" + + "#4106#$GPRMC,151410.00,A,3010.4103,N,08146.2728,W,,214.90,010324,,,A*58\r\n")); + + verifyPositions(decoder, text( + "#868105044690301#MT600+#0000#0#0#143#40#0#AUTO#1\r\n", + "#072030fcf21$GPRMC,155616.00,A,4931.9210,N,09652.5290,W,53.80,90.00,150224,,,A*48\r\n")); + + verifyAttribute(decoder, text( + "#867665041689485#MT700N#0000#HT#1\r\n", + "#5065$GPRMC,148996.00,A,2485.2458,N,01258.4535,E,,,151348,,,A*5D\r\n"), + Position.KEY_BATTERY, 5.065); + + verifyPositions(decoder, false, text( + "#862255061983166#MT700NW#0000#TOWED#1\r\n", + "#4502$WIFI,051550.00,A,-50,7683C2CBC0B0,-51,7683C29BC0B0,-51,7683C2BBC0B0,-51,7483C2DBC0B0,-51,7683C2ABC0B0,221123*78\r\n")); + + verifyPositions(decoder, false, text( + "#862255061825896#MT710#0000#TOWED#1\r\n", + "#39#$WIFI,015259.00,A,-47,7483C2DBC0B0,-48,7683C2ABC0B0,-48,7683C29BC0B0,-48,7683C2CBC0B0,-48,7683C2BBC0B0,151123*74\r\n")); + + verifyPositions(decoder, false, text( "#860517049471362#MT700#0000#AUTO#1\r\n", "#36$GPRMC,,V,,,,,,,,,,A*5C\r\n")); diff --git a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java index d6d1b7275..547014752 100644 --- a/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TlvProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TlvProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java index 9cdcb6169..fe9ac26f1 100644 --- a/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TmgFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TmgFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java index 6d3c36005..12402e068 100644 --- a/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TmgProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TmgProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java index b49345a42..67b04446e 100644 --- a/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TopflytechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TopflytechProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java index 5e7ec1136..b55e12e9d 100644 --- a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -17,6 +17,9 @@ public class TopinProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "78780d0103593390754169634d0d0a")); + verifyNotNull(decoder, binary( + "787803181604130318491475905bd30e25001e10bbf7635d14759006e626560401cc00000028660090df425f000028660090df576c00002866009487566700002866009ca15667000d0a")); + verifyAttribute(decoder, binary( "7878006921120412565802010601071e4a9764071e4a9864010d0a"), Position.KEY_ALARM, Position.ALARM_VIBRATION); diff --git a/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java index a69f389ac..b6ba9b0eb 100644 --- a/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java index 175c32848..5eff60e51 100644 --- a/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TotemFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TotemFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java index df5734568..949e69275 100644 --- a/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TotemProtocolDecoderTest.java @@ -1,7 +1,8 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TotemProtocolDecoderTest extends ProtocolTest { @@ -10,6 +11,10 @@ public class TotemProtocolDecoderTest extends ProtocolTest { var decoder = inject(new TotemProtocolDecoder(null)); + verifyAttribute(decoder, text( + "$$0494E2123456789012345|150425223945,113.925525,22.55814,1122334455|38"), + Position.KEY_DRIVER_UNIQUE_ID, "1122334455"); + verifyPosition(decoder, text( "$$0111AA353081090067318|0804400022070722520240400005B364ED5003107300001.700000002245.3919N10231.6952W000001860E")); diff --git a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java index 97a044b51..795f75842 100644 --- a/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/TotemProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TotemProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java index e4917c872..1323691c6 100644 --- a/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tr20ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tr20ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java index 96ddf4175..636f2101a 100644 --- a/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tr900ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tr900ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java index 10603db1c..514ef2647 100644 --- a/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TrackboxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TrackboxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java index 7542b3456..55ccd2f0b 100644 --- a/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TrakMateProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TrakMateProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java index a093d94e9..8c88f0ebb 100644 --- a/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TramigoFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TramigoFrameDecoderTest extends ProtocolTest { @@ -11,6 +11,10 @@ public class TramigoFrameDecoderTest extends ProtocolTest { var decoder = inject(new TramigoFrameDecoder()); verifyFrame( + binary("0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265"), + decoder.decode(null, null, binary("0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265"))); + + verifyFrame( binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46"), decoder.decode(null, null, binary("8000ed2bb0009c000101bee000050b09633d925b5472616d69676f3a205472697020737461727465642c2053686f636b2053656e736f722c206174204b696e6720437265656b20526f61642d46726565746f776e205374726565742c20506f727420486172636f7572742c205269766572732c204e472c20342e37363336312c20372e30313836382c2030373a31383a333620536570203320454f46"))); diff --git a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java index c2d13d199..dc0ca2b73 100644 --- a/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TramigoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TramigoProtocolDecoderTest extends ProtocolTest { @@ -10,6 +10,12 @@ public class TramigoProtocolDecoderTest extends ProtocolTest { var decoder = inject(new TramigoProtocolDecoder(null)); + verifyNull(decoder, binary( + "04d000a9e45c1b69101a023ef34f00549eec63047795ec63000000004eff5c0062c2e4ffbf612f00ce9aec63000000007700960180c0e4ff5f542f004c1200007b004a023d0200000001430000000000000000001f000b0006005a57436f63612d436f6c6120426f74746c696e6720506c616e74204861726172654772616e69746573696465486172617265014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265")); + + verifyPosition(decoder, binary( + "0480001df35b1b69101a023ef34f0090436d38003200380e0000850081c0e4ff6d542f00000015000000050000000000007600a20100008f436d3800014400000000000000000021000a0006005a574a6169726f7320486972692043656e747265205072696d617279205363686f6f6c536f7574686572746f6e486172617265")); + verifyAttributes(decoder, binary( "8000c426b000a6000101c557037598050d5c8a595472616d69676f3a204d6f76696e672c20302e3132206b6d2045206f66204c617275742054696e2049736c616d6963205072696d617279205363686f6f6c2c2054616970696e672c20506572616b2c204d592c20342e38333134392c203130302e37333038352c204e572077697468207370656564203130206b6d2f682c2030303a34393a30382041756720392020454f46")); diff --git a/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java new file mode 100644 index 000000000..27f53b574 --- /dev/null +++ b/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java @@ -0,0 +1,21 @@ +package org.traccar.protocol; + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class TranSyncProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new TranSyncProtocolDecoder(null)); + + verifyPosition(decoder, binary( + "3a3a2b583f086065705154043900801017050b11190f01623ef40887dff00000c25e9ff707000007152a2d0000000105004794916902050000100000050252ee060200822323")); + + verifyAttributes(decoder, binary( + "3a3a2b583f086065705154043900801017050b11190f01623ef40887dff00000c25e9ff707000007152a2d0000000105004794916902050000000000050252ee060200822323")); + + } + +} diff --git a/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java index 6dc28d89e..370775735 100644 --- a/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TrvProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TrvProtocolDecoderTest extends ProtocolTest { @@ -14,6 +14,9 @@ public class TrvProtocolDecoderTest extends ProtocolTest { "TRVAP00352121088015548")); verifyPosition(decoder, text( + "TRVYP14080524A2232.9806N11404.9355E000.1061830323.870600090800010200011,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97&Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97& Home3|74-DE-2B-44-88-8C|97")); + + verifyPosition(decoder, text( "TRVYP03190805A1828.9242N07353.9423E000.0150716029.0010000810020201112,404,27,184,10229")); verifyNotNull(decoder, text( @@ -66,7 +69,7 @@ public class TrvProtocolDecoderTest extends ProtocolTest { "TRVAP10080524A2232.9806N11404.9355E000.1061830323.8706000908000502,460,0,9520,3671,00,zh-cn,00")); verifyPosition(decoder, text( - "TRVYP14220217A5235.7885N00724.1840E000.0130919177.561000050660000200004,262,01,14635,52789,FritzBox7|DC-39-8F-7E-94-73|-89&FritzBox7|24-4E-5D-71-C3-9C|-90&MY_IOT|80-B4-F7-77-9C-7C|-81&MYAP|44-D4-F7-77-9C-7C|-80#")); + "TRVYP14220217A5235.7885N00724.1840E000.0130919177.561000050660000200004,262,01,14635,52789,FritzBox7|DC-39-8F-7E-94-73|-89&FritzBox7|24-4E-5D-71-C3-9C|-90&MY_IOT|80-B4-F7-77-9C-7C|-81&MYAP|44-D4-F7-77-9C-7C|-80#")); } diff --git a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java index 3f5c60daa..e471671f8 100644 --- a/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Tt8850ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Tt8850ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java index de6f3a6ff..d42a249e8 100644 --- a/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TytanProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class TytanProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java index 4a861fb06..d90e5e07e 100644 --- a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java @@ -1,7 +1,8 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; +import org.traccar.model.Position; public class TzoneProtocolDecoderTest extends ProtocolTest { @@ -10,6 +11,10 @@ public class TzoneProtocolDecoderTest extends ProtocolTest { var decoder = inject(new TzoneProtocolDecoder(null)); + verifyAttribute(decoder, binary( + "545a00d424240153011300000863835029944118170316023b180016040485c73d2479187e170316023b1800000000060c000000000d1cc0406303019904aa00000000008a012520205e544f4e474c4f4d245049544f4f4e244d522e5e5e3f3b363030373634333132303130303134323234323d3139303631393538313032363d3f2b2020202020202020202020202032322020202020202020202020203120202020202020202020202030303234363238202031303730302020202020202020202020202020202020202020203f00030080000006e80e0d0a"), + Position.KEY_CARD, "% ^TONGLOM$PITOON$MR.^^?;6007643120100142242=190619581026=?+ 22 1 0024628 10700 ?"); + verifyAttributes(decoder, binary( "545a003724240407020200000180322000001610160b151019100000000c010a07320101088600007dca000baa102837016a0114025500000169e80d0a")); diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java index 534287e0a..9b10328f6 100644 --- a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class UlbotechFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java index 52f2520cc..5d2692a8a 100644 --- a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class UlbotechProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java index 50e9321ce..0ee1516ba 100644 --- a/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/UlbotechProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java index 8d32eebb1..f070c6201 100644 --- a/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UproProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; @@ -83,6 +83,14 @@ public class UproProtocolDecoderTest extends ProtocolTest { verifyPosition(decoder, buffer( "*AI2000905300036,AD1&A1703054913231101844949860000251115&B0500000000&C0;4?72:9&F0000")); + verifyAttribute(decoder, buffer( + "*HQ200862312328000001,AD1&A1520441548253003503696640017270124&B0000000000&C00000117&F0000&R2118&N01&V0125&X(J01E0)&K00300&Z000&d01286"), + Position.PREFIX_ADC + 1, 12.86); + + verifyAttribute(decoder, buffer( + "*HQ200862312328000001,BA&A1520461548253003503696640017270124&B0000000000&C00000117&F0000&R2218&N01&V0125&X(J01E0)&K00300&Z000&d01287"), + Position.PREFIX_ADC + 1, 12.87); + } } diff --git a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java index 40776278d..492f241e5 100644 --- a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class UuxProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java index 105dc8339..d8e3f1f53 100644 --- a/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/V680ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class V680ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.java new file mode 100644 index 000000000..d526ea850 --- /dev/null +++ b/src/test/java/org/traccar/protocol/ValtrackProtocolDecoderTest.java @@ -0,0 +1,19 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class ValtrackProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new ValtrackProtocolDecoder(null)); + + verifyPositions(decoder, request(HttpMethod.POST, "/", buffer( + "{\"resource\":[{\"devid\":\"869731054075783\",\"etype\":\"G_PING\",\"lat\":\"0.000000\",\"lon\":\"0.000000\",\"vbat\":\"12.263848\",\"speed\":\"\",\"nlat\":\"4255.364258\",\"nlon\":\"176.867203\",\"ncsq\":\"16,99\"},{\"devid\":\"869731054075783\",\"etype\":\"G_PING\",\"lat\":\"0.000000\",\"lon\":\"0.000000\",\"vbat\":\"12.263848\",\"speed\":\"\",\"nlat\":\"4255.364258\",\"nlon\":\"176.867203\",\"ncsq\":\"16,99\"},{\"devid\":\"869731054075783\",\"etype\":\"G_PING\",\"lat\":\"0.000000\",\"lon\":\"0.000000\",\"vbat\":\"12.263848\",\"speed\":\"\",\"nlat\":\"4255.364258\",\"nlon\":\"176.867203\",\"ncsq\":\"16,99\"}]}"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java index 042b66cae..ff43a94ab 100644 --- a/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VisiontekProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class VisiontekProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java new file mode 100644 index 000000000..e0e88b324 --- /dev/null +++ b/src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java @@ -0,0 +1,22 @@ +package org.traccar.protocol; + +import io.netty.handler.codec.http.HttpMethod; +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class VltProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new VltProtocolDecoder(null)); + + verifyPosition(decoder, request(HttpMethod.POST, "/", + buffer("vltdata=NRM12345678901234501L1060418102230023.125503N080.068033E4041231234123456789070.48120.5025273011M"))); + + verifyPositions(decoder, request(HttpMethod.POST, "/", + buffer("vltdata=BTH86123004167306301301L1240323181909009.226018N076.794980E404x19601d000037596000.00198.7013011401S02H1240323181807009.226018N076.794980E404x72090a000000000000.00198.7013011101S02H1240323181707009.226018N076.794980E404x72090a000014598000.00198.7013011101S02H1240323181605009.226018N076.794982E404x72090a000014596000.00198.7013011101S02H1240323181504009.226018N076.794982E404x72090a000014596000.00198.7013010901S02H1240323181402009.226018N076.794980E404x72090a000014596001.67198.0013021301S02H1240323181306009.226010N076.794980E404x72090a000014596000.00174.0013021401S02H1240323181155009.226010N076.794980E404x72090a088511008000.00174.0013011201S02H1240323181057009.226010N076.794980E404x72090a000014596000.00174.0013011201S02H1240323180958009.226010N076.794980E404x72090a000014596000.00174.0013021401S02H1240323180858009.226010N076.794980E404x72090a000014596001.48174.0013021201S02H1240323180755009.226005N076.794982E404x72090a000014598000.00164.4013011301S02H1240323180652009.226005N076.794982E404x72090a000014598000.00164.4013021101S"))); + + } + +} diff --git a/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java index 25cc03781..ead1624d8 100644 --- a/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VnetProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class VnetProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java index 7d039dc8f..a869e7dc1 100644 --- a/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Vt200FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Vt200FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java index 25ce5550a..c4533aa11 100644 --- a/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Vt200ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Position; diff --git a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java index e381153a2..18baf75cb 100644 --- a/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VtfmsFrameDecoderTest.java @@ -1,9 +1,9 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class VtfmsFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java index 4925d9769..8ca1b9df5 100644 --- a/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/VtfmsProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class VtfmsProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java index 42464c6fe..cb20e1b4d 100644 --- a/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WatchFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WatchFrameDecoderTest extends ProtocolTest { @@ -11,6 +11,14 @@ public class WatchFrameDecoderTest extends ProtocolTest { var decoder = inject(new WatchFrameDecoder()); verifyFrame( + binary("5b33472a393730353134313734302a303030392a4c4b2c302c302c35335d"), + decoder.decode(null, null, binary("5b33472a393730353134313734302a303030392a4c4b2c302c302c35335d5b33472a393730353134313734302a303035412a55442c3139303732332c3139303730372c412c33362e3831353130392c4e2c31302e313739323331322c452c382e32342c3132372e392c32312e302c352c3130302c35332c302c302c30303030303030302c302c302c35382e305d5b33472a393730353134313734302a303030332a544b515d5b33472a393730353134313734302a303030392a4c4b2c302c302c35335d"))); + + verifyFrame( + binary("5b33472a3838303930303234322a303133442a55442c3132303632332c3134303032302c412c34382e3934393237332c4e2c20342e333738333036302c452c31382e35362c34332e382c302e302c31322c3130302c37362c3232363132302c302c30303030303030302c322c3235352c3230342c382c333131302c35353032352c3134362c333133302c34393239372c3132342c352c42616e67696e67576966692c33343a61313a65643a65313a39313a34662c2d37312c42415220576946692c33363a61323a65313a65643a61313a64652c2d37322c4e6574776f726b576966692c32363a64653a61313a65643a65313a61302c2d37332c46696265722c33363a61313a65643a65313a39313a34662c2d37352c5b4c475f57616c6c2d4d6f756e7420412f435d653732352c36363a61313a65643a65313a65373a32352c2d38322c31352e305d"), + decoder.decode(null, null, binary("5b33472a3838303930303234322a303133442a55442c3132303632332c3134303032302c412c34382e3934393237332c4e2c20342e333738333036302c452c31382e35362c34332e382c302e302c31322c3130302c37362c3232363132302c302c30303030303030302c322c3235352c3230342c382c333131302c35353032352c3134362c333133302c34393239372c3132342c352c42616e67696e67576966692c33343a61313a65643a65313a39313a34662c2d37312c42415220576946692c33363a61323a65313a65643a61313a64652c2d37322c4e6574776f726b576966692c32363a64653a61313a65643a65313a61302c2d37332c46696265722c33363a61313a65643a65313a39313a34662c2d37352c5b4c475f57616c6c2d4d6f756e7420412f435d653732352c36363a61313a65643a65313a65373a32352c2d38322c31352e305d"))); + + verifyFrame( binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d"), decoder.decode(null, null, binary("5b33472a3335323636313039303134333135302a303030412a4c4b2c302c302c3130305d"))); diff --git a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java index 37fab7e40..5fd0ede44 100644 --- a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.database.MediaManager; import org.traccar.model.Position; @@ -17,6 +17,13 @@ public class WatchProtocolDecoderTest extends ProtocolTest { var decoder = inject(new WatchProtocolDecoder(null)); + verifyAttribute(decoder, buffer( + "[3G*9705141740*000B*oxygen,0,98]"), + "bloodOxygen", 98); + + verifyPosition(decoder, buffer( + "[3G*9705141740*00C2*UD_LTE,260723,185105,V,00.000000,,00.0000000,,0.00,0.0,0.0,0,100,67,0,0,00000000,2,0,605,1,10006,65799,14,10020,4104,4,3,,34:60:f9:ec:19:f8,-82,,98:48:27:55:18:20,-96,,34:e8:94:e4:06:18,-104,0.0]")); + verifyPosition(decoder, buffer( "[SG*9059011020*0067*AL,240123,181628,V,54.427538,N,6.409275,W,0.00,0,0,0,19,90,0,0,00000000,1,1,234,10,55C0,3B882A2,132,,10]")); diff --git a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java index c83c103c3..649c0016b 100644 --- a/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/WatchProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java index 6953784fb..b7c422456 100644 --- a/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WialonProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WialonProtocolDecoderTest extends ProtocolTest { @@ -13,6 +13,10 @@ public class WialonProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, text( "#L#2.0;42001300083;;CE45")); + verifyAttribute(decoder, text( + "#D#220323;114150;2234.80479;N;11354.87786;E;0;NA;59;11;NA;NA;NA;;NA;d_battr:1:94,d_csq:1:21,di_light:1:1;E7C9"), + "di_light", 1.0); + verifyAttributes(decoder, text( "#D#NA;NA;5429.681944502211763;N;02654.60403650999069;E;NA;NA;NA;NA;NA;NA;NA;1.0;NA;m1:1:9196679,d1:1:15397,t1:1:20,b1:1:162,fuel1:2:21588.0,pv1:2:35.98,finish:1:1;0x9b0")); @@ -75,6 +79,9 @@ public class WialonProtocolDecoderTest extends ProtocolTest { verifyPositions(decoder, text( "#B#110315;045857;5364.0167;N;06127.8262;E;0;155;965;7;2.40;4;0;14.77,0.02,3.6;AB45DF01145;")); + verifyAttribute(decoder, text( + "#D#120319;112003;NA;NA;NA;NA;0.000;NA;NA;0;NA;NA;NA;NA;NA;motion:3:false"), + "motion", false); } } diff --git a/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java index 45c86ae1a..405fc340e 100644 --- a/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WliFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WliFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java index e74b1df06..482c15da0 100644 --- a/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WliProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WliProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java index a1cbfe737..f1f65cb2c 100644 --- a/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WondexFrameDecoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class WondexFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java index 72ba8e163..8f86263c4 100644 --- a/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WondexProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WondexProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java index f482871dd..2e1a45c8c 100644 --- a/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/WondexProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class WondexProtocolEncoderTest extends ProtocolTest { @Test diff --git a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java index b901820fe..ba40486a6 100644 --- a/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/WristbandProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class WristbandProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java index 34437862c..9e909f1ca 100644 --- a/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xexun2FrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java index d327930b5..27e894705 100644 --- a/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Xexun2FrameEncoderTest.java @@ -2,7 +2,7 @@ package org.traccar.protocol; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xexun2FrameEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java index 48ba1a691..b373c8283 100644 --- a/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xexun2ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java index 483bc85fa..62d511c0b 100644 --- a/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Xexun2ProtocolEncoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java index 9bc39fc97..82287f6ad 100644 --- a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class XexunFrameDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java index f7599b4c3..7331827f0 100644 --- a/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XexunProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class XexunProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java index db9c829aa..8f912d9e2 100644 --- a/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/XirgoProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class XirgoProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java index 76e2f960d..2efedf51a 100644 --- a/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/XirgoProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class XirgoProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java index 8ed175a74..fae163a56 100644 --- a/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xrb28ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java index a66efecc2..eaa29a833 100644 --- a/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java +++ b/src/test/java/org/traccar/protocol/Xrb28ProtocolEncoderTest.java @@ -1,10 +1,10 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; import org.traccar.model.Command; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class Xrb28ProtocolEncoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java index 007af984e..43c1f0676 100644 --- a/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xt013ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xt013ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java index 35cb3c3fa..881ff1ee9 100644 --- a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class Xt2400ProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java index 81afe53a3..a5a2a11d1 100644 --- a/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/YwtProtocolDecoderTest.java @@ -1,6 +1,6 @@ package org.traccar.protocol; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.traccar.ProtocolTest; public class YwtProtocolDecoderTest extends ProtocolTest { diff --git a/src/test/java/org/traccar/reports/ReportUtilsTest.java b/src/test/java/org/traccar/reports/ReportUtilsTest.java index 4bf668064..0f14577fd 100644 --- a/src/test/java/org/traccar/reports/ReportUtilsTest.java +++ b/src/test/java/org/traccar/reports/ReportUtilsTest.java @@ -1,16 +1,16 @@ package org.traccar.reports; import org.apache.velocity.app.VelocityEngine; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.traccar.BaseTest; import org.traccar.api.security.PermissionsService; import org.traccar.config.Config; +import org.traccar.config.Keys; import org.traccar.helper.model.PositionUtil; import org.traccar.model.Device; import org.traccar.model.Position; import org.traccar.reports.common.ReportUtils; -import org.traccar.reports.common.TripsConfig; import org.traccar.reports.model.StopReportItem; import org.traccar.reports.model.TripReportItem; import org.traccar.storage.Storage; @@ -20,17 +20,18 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; -import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.TimeZone; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -38,10 +39,10 @@ public class ReportUtilsTest extends BaseTest { private Storage storage; - @Before + @BeforeEach public void init() throws StorageException { storage = mock(Storage.class); - when(storage.getObject(any(), any())).thenReturn(mock(Device.class)); + when(storage.getObject(eq(Device.class), any())).thenReturn(mock(Device.class)); } private Date date(String time) throws ParseException { @@ -62,6 +63,19 @@ public class ReportUtilsTest extends BaseTest { return position; } + + private Device mockDevice( + double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration, + long minimalNoDataDuration, boolean useIgnition) { + Device device = mock(Device.class); + when(device.getAttributes()).thenReturn(Map.of( + Keys.REPORT_TRIP_MINIMAL_TRIP_DISTANCE.getKey(), minimalTripDistance, + Keys.REPORT_TRIP_MINIMAL_TRIP_DURATION.getKey(), minimalTripDuration, + Keys.REPORT_TRIP_MINIMAL_PARKING_DURATION.getKey(), minimalParkingDuration, + Keys.REPORT_TRIP_MINIMAL_NO_DATA_DURATION.getKey(), minimalNoDataDuration, + Keys.REPORT_TRIP_USE_IGNITION.getKey(), useIgnition)); + return device; + } @Test public void testCalculateDistance() { @@ -78,8 +92,7 @@ public class ReportUtilsTest extends BaseTest { @Test public void testCalculateSpentFuel() { ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - mock(TripsConfig.class), mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); Position startPosition = new Position(); Position endPosition = new Position(); assertEquals(reportUtils.calculateFuel(startPosition, endPosition), 0.0, 0.01); @@ -98,15 +111,15 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:03:00.000", 10, 1000), position("2016-01-01 00:04:00.000", 10, 2000), position("2016-01-01 00:05:00.000", 0, 3000), - position("2016-01-01 00:06:00.000", 0, 3000), - position("2016-01-01 00:07:00.000", 0, 3000)); + position("2016-01-01 00:15:00.000", 0, 3000), + position("2016-01-01 00:25:00.000", 0, 3000)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 180, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var trips = reportUtils.detectTripsAndStops(mock(Device.class), data, false, TripReportItem.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); @@ -120,7 +133,7 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(3000, itemTrip.getDistance(), 0.01); - var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); @@ -136,8 +149,8 @@ public class ReportUtilsTest extends BaseTest { itemStop = iterator.next(); assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime()); - assertEquals(120000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:25:00.000"), itemStop.getEndTime()); + assertEquals(1200000, itemStop.getDuration()); } @@ -151,17 +164,17 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:03:00.000", 10, 1000), position("2016-01-01 00:04:00.000", 10, 2000), position("2016-01-01 00:05:00.000", 0, 3000), - position("2016-01-01 00:06:00.000", 0, 3000), - position("2016-01-01 00:07:00.000", 0, 3000)); + position("2016-01-01 00:15:00.000", 0, 3000), + position("2016-01-01 00:25:00.000", 0, 3000)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); data.get(5).set(Position.KEY_IGNITION, false); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, true, false, 0.01); + Device device = mockDevice(500, 300, 180, 900, true); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); @@ -175,7 +188,7 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(3000, itemTrip.getDistance(), 0.01); - trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class); + trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); @@ -189,7 +202,7 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(3000, itemTrip.getDistance(), 0.01); - var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); @@ -205,8 +218,8 @@ public class ReportUtilsTest extends BaseTest { itemStop = iterator.next(); assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:07:00.000"), itemStop.getEndTime()); - assertEquals(120000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:25:00.000"), itemStop.getEndTime()); + assertEquals(1200000, itemStop.getDuration()); } @@ -224,15 +237,15 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:07:00.000", 0, 5000), position("2016-01-01 00:08:00.000", 10, 6000), position("2016-01-01 00:09:00.000", 0, 7000), - position("2016-01-01 00:10:00.000", 0, 7000), - position("2016-01-01 00:11:00.000", 0, 7000)); + position("2016-01-01 00:19:00.000", 0, 7000), + position("2016-01-01 00:29:00.000", 0, 7000)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 180000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 180, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); @@ -246,7 +259,7 @@ public class ReportUtilsTest extends BaseTest { assertEquals(10, itemTrip.getMaxSpeed(), 0.01); assertEquals(7000, itemTrip.getDistance(), 0.01); - var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); @@ -262,28 +275,28 @@ public class ReportUtilsTest extends BaseTest { itemStop = iterator.next(); assertEquals(date("2016-01-01 00:09:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:11:00.000"), itemStop.getEndTime()); - assertEquals(120000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:29:00.000"), itemStop.getEndTime()); + assertEquals(1200000, itemStop.getDuration()); } @Test public void testDetectStopsOnly() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), position("2016-01-01 00:01:00.000", 0, 0), position("2016-01-01 00:02:00.000", 1, 0), position("2016-01-01 00:03:00.000", 0, 0), position("2016-01-01 00:04:00.000", 1, 0), position("2016-01-01 00:05:00.000", 0, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertFalse(result.isEmpty()); @@ -299,20 +312,20 @@ public class ReportUtilsTest extends BaseTest { @Test public void testDetectStopsWithTripCut() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 0, 0), position("2016-01-01 00:01:00.000", 0, 0), position("2016-01-01 00:02:00.000", 0, 0), position("2016-01-01 00:03:00.000", 0, 0), position("2016-01-01 00:04:00.000", 1, 0), position("2016-01-01 00:05:00.000", 2, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertFalse(result.isEmpty()); @@ -328,20 +341,20 @@ public class ReportUtilsTest extends BaseTest { @Test public void testDetectStopsStartedFromTrip() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 2, 0), position("2016-01-01 00:01:00.000", 1, 0), position("2016-01-01 00:02:00.000", 0, 0), - position("2016-01-01 00:03:00.000", 0, 0), - position("2016-01-01 00:04:00.000", 0, 0), - position("2016-01-01 00:05:00.000", 0, 0)); + position("2016-01-01 00:12:00.000", 0, 0), + position("2016-01-01 00:22:00.000", 0, 0), + position("2016-01-01 00:32:00.000", 0, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertFalse(result.isEmpty()); @@ -349,28 +362,28 @@ public class ReportUtilsTest extends BaseTest { StopReportItem itemStop = result.iterator().next(); assertEquals(date("2016-01-01 00:02:00.000"), itemStop.getStartTime()); - assertEquals(date("2016-01-01 00:05:00.000"), itemStop.getEndTime()); - assertEquals(180000, itemStop.getDuration()); + assertEquals(date("2016-01-01 00:32:00.000"), itemStop.getEndTime()); + assertEquals(1800000, itemStop.getDuration()); } @Test public void testDetectStopsMoving() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 5, 0), position("2016-01-01 00:01:00.000", 5, 0), position("2016-01-01 00:02:00.000", 3, 0), position("2016-01-01 00:03:00.000", 5, 0), position("2016-01-01 00:04:00.000", 5, 0), position("2016-01-01 00:05:00.000", 5, 0)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 300000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 300, 200, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var result = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var result = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(result); assertTrue(result.isEmpty()); @@ -380,7 +393,7 @@ public class ReportUtilsTest extends BaseTest { @Test public void testDetectTripAndStopByGap() throws Exception { - Collection<Position> data = Arrays.asList( + var data = Arrays.asList( position("2016-01-01 00:00:00.000", 7, 100), position("2016-01-01 00:01:00.000", 7, 300), position("2016-01-01 00:02:00.000", 5, 500), @@ -389,13 +402,13 @@ public class ReportUtilsTest extends BaseTest { position("2016-01-01 00:23:00.000", 2, 700), position("2016-01-01 00:24:00.000", 5, 800), position("2016-01-01 00:25:00.000", 5, 900)); + when(storage.getObjects(eq(Position.class), any())).thenReturn(data); - TripsConfig tripsConfig = new TripsConfig(500, 200000, 200000, 900000, false, false, 0.01); + Device device = mockDevice(500, 200, 200, 900, false); ReportUtils reportUtils = new ReportUtils( - mock(Config.class), storage, mock(PermissionsService.class), - tripsConfig, mock(VelocityEngine.class), null); + mock(Config.class), storage, mock(PermissionsService.class), mock(VelocityEngine.class), null); - var trips = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, TripReportItem.class); + var trips = reportUtils.slowTripsAndStops(device, new Date(), new Date(), TripReportItem.class); assertNotNull(trips); assertFalse(trips.isEmpty()); @@ -409,7 +422,7 @@ public class ReportUtilsTest extends BaseTest { assertEquals(7, itemTrip.getMaxSpeed(), 0.01); assertEquals(600, itemTrip.getDistance(), 0.01); - var stops = reportUtils.detectTripsAndStops(mock(Device.class) ,data, false, StopReportItem.class); + var stops = reportUtils.slowTripsAndStops(device, new Date(), new Date(), StopReportItem.class); assertNotNull(stops); assertFalse(stops.isEmpty()); diff --git a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java index 202983f1e..dbdf85420 100644 --- a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java +++ b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java @@ -1,22 +1,24 @@ package org.traccar.speedlimit; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import org.traccar.config.Config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class OverpassSpeedLimitProviderTest { private final Client client = ClientBuilder.newClient(); - @Ignore + @Disabled @Test public void testOverpass() throws Exception { - SpeedLimitProvider provider = new OverpassSpeedLimitProvider(client, "http://8.8.8.8/api/interpreter"); + var config = new Config(); + SpeedLimitProvider provider = new OverpassSpeedLimitProvider(config, client, "http://8.8.8.8/api/interpreter"); provider.getSpeedLimit(34.74767, -82.48098, new SpeedLimitProvider.SpeedLimitProviderCallback() { @Override diff --git a/src/test/java/org/traccar/web/WebServerTest.java b/src/test/java/org/traccar/web/WebServerTest.java index ba4124e44..694dab18a 100644 --- a/src/test/java/org/traccar/web/WebServerTest.java +++ b/src/test/java/org/traccar/web/WebServerTest.java @@ -1,6 +1,6 @@ package org.traccar.web; -import org.junit.Test; +import org.junit.jupiter.api.Test; import javax.naming.Context; import javax.naming.InitialContext; diff --git a/swagger.json b/swagger.json index 3eab9b522..b2209a20e 100644 --- a/swagger.json +++ b/swagger.json @@ -2,7 +2,7 @@ "openapi": "3.0.1", "info": { "title": "Traccar", - "version": "5.5", + "version": "5.12", "description": "Traccar GPS tracking server API documentation. To use the API you need to have a server instance. For testing purposes you can use one of free [demo servers](https://www.traccar.org/demo-server/). For production use you can install your own server or get a [subscription service](https://www.traccar.org/product/tracking-server/).", "contact": { "name": "Traccar Support", @@ -764,15 +764,9 @@ "required": true }, "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Permission" - } - } - } + "204": { + "description": "No Content", + "content": {} }, "400": { "description": "No permission", @@ -879,6 +873,54 @@ } } } + }, + "delete": { + "summary": "Deletes all the Positions of a device in the time span specified", + "description": "", + "tags": [ + "Positions" + ], + "parameters": [ + { + "name": "deviceId", + "in": "query", + "description": "", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "name": "from", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "schema": { + "type": "string", + "format": "date-time" + }, + "required": true + }, + { + "name": "to", + "in": "query", + "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`", + "schema": { + "type": "string", + "format": "date-time" + }, + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "content": {} + }, + "400": { + "description": "Bad Request", + "content": {} + } + } } }, "/server": { @@ -1019,6 +1061,42 @@ } } }, + "/session/openid/auth": { + "get": { + "summary": "Fetch Session information", + "tags": [ + "Session" + ], + "parameters": [ + { + } + ], + "responses": { + "303": { + "description": "Redirect to OpenID Connect identity provider", + "content": { } + } + } + } + }, + "/session/openid/callback": { + "get": { + "summary": "OpenID Callback", + "tags": [ + "Session" + ], + "parameters": [ + { + } + ], + "responses": { + "303": { + "description": "Successful authentication, redirect to homepage", + "content": { } + } + } + } + }, "/users": { "get": { "summary": "Fetch a list of Users", @@ -2647,6 +2725,12 @@ "type": "object", "properties": {} }, + "geofenceIds": { + "type": "array", + "items": { + "type": "integer" + } + }, "attributes": { "type": "object", "properties": {} @@ -2715,6 +2799,9 @@ "limitCommands": { "type": "boolean" }, + "fixedEmail": { + "type": "boolean" + }, "poiLayer": { "type": "string" }, @@ -2775,6 +2862,12 @@ "coordinateFormat": { "type": "string" }, + "openIdEnabled": { + "type": "boolean" + }, + "openIdForce": { + "type": "boolean" + }, "attributes": { "type": "object", "properties": {} @@ -2843,12 +2936,6 @@ "category": { "type": "string" }, - "geofenceIds": { - "type": "array", - "items": { - "type": "integer" - } - }, "attributes": { "type": "object", "properties": {} @@ -2878,39 +2965,43 @@ "properties": { "userId": { "type": "integer", - "description": "User Id, can be only first parameter" + "description": "User id, can be only first parameter" }, "deviceId": { "type": "integer", - "description": "Device Id, can be first parameter or second only in combination with userId" + "description": "Device id, can be first parameter or second only in combination with userId" }, "groupId": { "type": "integer", - "description": "Group Id, can be first parameter or second only in combination with userId" + "description": "Group id, can be first parameter or second only in combination with userId" }, "geofenceId": { "type": "integer", - "description": "Geofence Id, can be second parameter only" + "description": "Geofence id, can be second parameter only" }, "notificationId": { "type": "integer", - "description": "Notification Id, can be second parameter only" + "description": "Notification id, can be second parameter only" }, "calendarId": { "type": "integer", - "description": "Calendar Id, can be second parameter only and only in combination with userId" + "description": "Calendar id, can be second parameter only and only in combination with userId" }, "attributeId": { "type": "integer", - "description": "Computed Attribute Id, can be second parameter only" + "description": "Computed attribute id, can be second parameter only" }, "driverId": { "type": "integer", - "description": "Driver Id, can be second parameter only" + "description": "Driver id, can be second parameter only" }, "managedUserId": { "type": "integer", - "description": "User Id, can be second parameter only and only in combination with userId" + "description": "User id, can be second parameter only and only in combination with userId" + }, + "commandId": { + "type": "integer", + "description": "Saved command id, can be second parameter only" } }, "description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }" @@ -3487,4 +3578,4 @@ } } } -}
\ No newline at end of file +} diff --git a/templates/export/devices.xlsx b/templates/export/devices.xlsx Binary files differnew file mode 100644 index 000000000..ab6d9d5be --- /dev/null +++ b/templates/export/devices.xlsx diff --git a/templates/full/alarm.vm b/templates/full/alarm.vm index 9fae1f13b..fb596ecde 100644 --- a/templates/full/alarm.vm +++ b/templates/full/alarm.vm @@ -1,9 +1,87 @@ #set($subject = "$device.name: alarm!") +#set($alarmName = $position.getString("alarm")) +#if( $alarmName == "general") + #set($alarmName = "General") +#elseif($alarmName == "sos") + #set($alarmName = "SOS") +#elseif($alarmName == "vibration") + #set($alarmName = "Vibration") +#elseif($alarmName == "movement") + #set($alarmName = "Movement") +#elseif($alarmName == "lowspeed") + #set($alarmName = "Low Speed") +#elseif($alarmName == "overspeed") + #set($alarmName = "Overspeed") +#elseif($alarmName == "fallDown") + #set($alarmName = "Fall Down") +#elseif($alarmName == "lowPower") + #set($alarmName = "Low Power") +#elseif($alarmName == "lowBattery") + #set($alarmName = "Low Battery") +#elseif($alarmName == "fault") + #set($alarmName = "Fault") +#elseif($alarmName == "powerOff") + #set($alarmName = "Power Off") +#elseif($alarmName == "powerOn") + #set($alarmName = "Power On") +#elseif($alarmName == "door") + #set($alarmName = "Door") +#elseif($alarmName == "lock") + #set($alarmName = "Lock") +#elseif($alarmName == "unlock") + #set($alarmName = "Unlock") +#elseif($alarmName == "geofence") + #set($alarmName = "Geofence") +#elseif($alarmName == "geofenceEnter") + #set($alarmName = "Geofence Enter") +#elseif($alarmName == "geofenceExit") + #set($alarmName = "Geofence Exit") +#elseif($alarmName == "gpsAntennaCut") + #set($alarmName = "GPS Antenna Cut") +#elseif($alarmName == "accident") + #set($alarmName = "Accident") +#elseif($alarmName == "tow") + #set($alarmName = "Tow") +#elseif($alarmName == "idle") + #set($alarmName = "Idle") +#elseif($alarmName == "highRpm") + #set($alarmName = "High RPM") +#elseif($alarmName == "hardAcceleration") + #set($alarmName = "Hard Acceleration") +#elseif($alarmName == "hardBraking") + #set($alarmName = "Hard Braking") +#elseif($alarmName == "hardCornering") + #set($alarmName = "Hard Cornering") +#elseif($alarmName == "laneChange") + #set($alarmName = "Lane Change") +#elseif($alarmName == "fatigueDriving") + #set($alarmName = "Fatigue Driving") +#elseif($alarmName == "powerCut") + #set($alarmName = "Power Cut") +#elseif($alarmName == "powerRestored") + #set($alarmName = "Power Restored") +#elseif($alarmName == "jamming") + #set($alarmName = "Jamming") +#elseif($alarmName == "temperature") + #set($alarmName = "Temperature") +#elseif($alarmName == "parking") + #set($alarmName = "Parking") +#elseif($alarmName == "bonnet") + #set($alarmName = "Bonnet") +#elseif($alarmName == "footBrake") + #set($alarmName = "Foot Brake") +#elseif($alarmName == "fuelLeak") + #set($alarmName = "Fuel Leak") +#elseif($alarmName == "tampering") + #set($alarmName = "Tampering") +#elseif($alarmName == "removing") + #set($alarmName = "Removing") +#end <!DOCTYPE html> <html> <body> Device: $device.name<br> -Alarm: $position.getString("alarm")<br> +Alarm: $alarmName<br> Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br> Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br> <br> diff --git a/templates/full/deviceExpiration.vm b/templates/full/deviceExpiration.vm new file mode 100644 index 000000000..879b31778 --- /dev/null +++ b/templates/full/deviceExpiration.vm @@ -0,0 +1,7 @@ +#set($subject = "Device expiration") +<!DOCTYPE html> +<html> +<body> +Your device $device.name has expired. +</body> +</html> diff --git a/templates/full/deviceExpirationReminder.vm b/templates/full/deviceExpirationReminder.vm new file mode 100644 index 000000000..aa47ac0ed --- /dev/null +++ b/templates/full/deviceExpirationReminder.vm @@ -0,0 +1,7 @@ +#set($subject = "Device expiration reminder") +<!DOCTYPE html> +<html> +<body> +Your device $device.name will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone). +</body> +</html> diff --git a/templates/full/deviceOffline.vm b/templates/full/deviceOffline.vm index 8f2c515b2..6d2122624 100644 --- a/templates/full/deviceOffline.vm +++ b/templates/full/deviceOffline.vm @@ -5,7 +5,6 @@ Device: $device.name<br> Offline<br> Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br> -Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a><br> <br> <a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a> </body> diff --git a/templates/full/deviceOnline.vm b/templates/full/deviceOnline.vm index 81a4ccbc8..02260c4fb 100644 --- a/templates/full/deviceOnline.vm +++ b/templates/full/deviceOnline.vm @@ -5,7 +5,6 @@ Device: $device.name<br> Online<br> Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br> -Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a><br> <br> <a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a> </body> diff --git a/templates/full/deviceUnknown.vm b/templates/full/deviceUnknown.vm index e012845e6..e99981069 100644 --- a/templates/full/deviceUnknown.vm +++ b/templates/full/deviceUnknown.vm @@ -5,7 +5,6 @@ Device: $device.name<br> Status is unknown<br> Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br> -Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a><br> <br> <a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a> </body> diff --git a/templates/full/queuedCommandSent.vm b/templates/full/queuedCommandSent.vm new file mode 100644 index 000000000..148dd2094 --- /dev/null +++ b/templates/full/queuedCommandSent.vm @@ -0,0 +1,11 @@ +#set($subject = "$device.name: queued command sent") +<!DOCTYPE html> +<html> +<body> +Device: $device.name<br> +Queued command sent<br> +Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)<br> +<br> +<a href="$webUrl/settings/notifications?token=$token">Unsubscribe</a> +</body> +</html> diff --git a/templates/full/unknown.vm b/templates/full/unknown.vm deleted file mode 100644 index 566ce0d42..000000000 --- a/templates/full/unknown.vm +++ /dev/null @@ -1,7 +0,0 @@ -#set($subject = "Unknown type") -<!DOCTYPE html> -<html> -<body> -Unknown type -</body> -</html> diff --git a/templates/full/userExpiration.vm b/templates/full/userExpiration.vm new file mode 100644 index 000000000..43fe2563e --- /dev/null +++ b/templates/full/userExpiration.vm @@ -0,0 +1,7 @@ +#set($subject = "Account expiration") +<!DOCTYPE html> +<html> +<body> +Your user account has expired. +</body> +</html> diff --git a/templates/full/userExpirationReminder.vm b/templates/full/userExpirationReminder.vm new file mode 100644 index 000000000..ecdee0588 --- /dev/null +++ b/templates/full/userExpirationReminder.vm @@ -0,0 +1,7 @@ +#set($subject = "Account expiration reminder") +<!DOCTYPE html> +<html> +<body> +Your user account will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone). +</body> +</html> diff --git a/templates/short/alarm.vm b/templates/short/alarm.vm index 15970dab8..effcb8f15 100644 --- a/templates/short/alarm.vm +++ b/templates/short/alarm.vm @@ -1,2 +1,80 @@ #set($subject = "$device.name: alarm!") -$device.name alarm: $position.getString("alarm") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +#set($alarmName = $position.getString("alarm")) +#if( $alarmName == "general") + #set($alarmName = "General") +#elseif($alarmName == "sos") + #set($alarmName = "SOS") +#elseif($alarmName == "vibration") + #set($alarmName = "Vibration") +#elseif($alarmName == "movement") + #set($alarmName = "Movement") +#elseif($alarmName == "lowspeed") + #set($alarmName = "Low Speed") +#elseif($alarmName == "overspeed") + #set($alarmName = "Overspeed") +#elseif($alarmName == "fallDown") + #set($alarmName = "Fall Down") +#elseif($alarmName == "lowPower") + #set($alarmName = "Low Power") +#elseif($alarmName == "lowBattery") + #set($alarmName = "Low Battery") +#elseif($alarmName == "fault") + #set($alarmName = "Fault") +#elseif($alarmName == "powerOff") + #set($alarmName = "Power Off") +#elseif($alarmName == "powerOn") + #set($alarmName = "Power On") +#elseif($alarmName == "door") + #set($alarmName = "Door") +#elseif($alarmName == "lock") + #set($alarmName = "Lock") +#elseif($alarmName == "unlock") + #set($alarmName = "Unlock") +#elseif($alarmName == "geofence") + #set($alarmName = "Geofence") +#elseif($alarmName == "geofenceEnter") + #set($alarmName = "Geofence Enter") +#elseif($alarmName == "geofenceExit") + #set($alarmName = "Geofence Exit") +#elseif($alarmName == "gpsAntennaCut") + #set($alarmName = "GPS Antenna Cut") +#elseif($alarmName == "accident") + #set($alarmName = "Accident") +#elseif($alarmName == "tow") + #set($alarmName = "Tow") +#elseif($alarmName == "idle") + #set($alarmName = "Idle") +#elseif($alarmName == "highRpm") + #set($alarmName = "High RPM") +#elseif($alarmName == "hardAcceleration") + #set($alarmName = "Hard Acceleration") +#elseif($alarmName == "hardBraking") + #set($alarmName = "Hard Braking") +#elseif($alarmName == "hardCornering") + #set($alarmName = "Hard Cornering") +#elseif($alarmName == "laneChange") + #set($alarmName = "Lane Change") +#elseif($alarmName == "fatigueDriving") + #set($alarmName = "Fatigue Driving") +#elseif($alarmName == "powerCut") + #set($alarmName = "Power Cut") +#elseif($alarmName == "powerRestored") + #set($alarmName = "Power Restored") +#elseif($alarmName == "jamming") + #set($alarmName = "Jamming") +#elseif($alarmName == "temperature") + #set($alarmName = "Temperature") +#elseif($alarmName == "parking") + #set($alarmName = "Parking") +#elseif($alarmName == "bonnet") + #set($alarmName = "Bonnet") +#elseif($alarmName == "footBrake") + #set($alarmName = "Foot Brake") +#elseif($alarmName == "fuelLeak") + #set($alarmName = "Fuel Leak") +#elseif($alarmName == "tampering") + #set($alarmName = "Tampering") +#elseif($alarmName == "removing") + #set($alarmName = "Removing") +#end +$device.name alarm: $alarmName at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) diff --git a/templates/short/queuedCommandSent.vm b/templates/short/queuedCommandSent.vm new file mode 100644 index 000000000..67f031280 --- /dev/null +++ b/templates/short/queuedCommandSent.vm @@ -0,0 +1,2 @@ +#set($subject = "$device.name: queued command sent") +Queued command sent to $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) diff --git a/templates/short/unknown.vm b/templates/short/unknown.vm deleted file mode 100644 index 2f9d5e3af..000000000 --- a/templates/short/unknown.vm +++ /dev/null @@ -1,2 +0,0 @@ -#set($subject = "Unknown type") -Unknown type diff --git a/tools/test-commands.py b/tools/test-commands.py index 6e310051a..7efd963b4 100755 --- a/tools/test-commands.py +++ b/tools/test-commands.py @@ -6,9 +6,9 @@ import binascii s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("localhost", 5001)) #s.send(binascii.unhexlify('68680f0504035889905831401700df1a00000d0a')) -s.send("imei:123456789012345,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;") +s.send(b"imei:123456789012345,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;") while True: - print s.recv(1024) + print(s.recv(1024)) s.close() diff --git a/tools/test-integration.py b/tools/test-integration.py index 89f1c76ed..f31ad7a82 100755 --- a/tools/test-integration.py +++ b/tools/test-integration.py @@ -4,7 +4,7 @@ import sys import os import xml.etree.ElementTree import urllib -import urllib2 +import urllib.request as urllib2 import json import socket import time @@ -18,10 +18,9 @@ messages = { 't55' : '$PGID,123456789012345*0F\r\n$GPRMC,120500.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,*33\r\n', 'xexun' : '111111120009,+436763737552,GPRMC,120600.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*68,F,, imei:123456789012345,04,481.2,F:4.15V,0,139,2689,232,03,2725,0576\n', 'totem' : '$$B3123456789012345|AA$GPRMC,120700.000,A,6000.0000,N,13000.0000,E,0.00,,010112,,,A*74|01.8|01.0|01.5|000000000000|20120403234603|14251914|00000000|0012D888|0000|0.0000|3674|940B\r\n', - 'meiligao' : '$$\x00\x60\x12\x34\x56\xFF\xFF\xFF\xFF\x99\x55120900.000,A,6000.0000,N,13000.0000,E,0.00,,010112,,*1C|11.5|194|0000|0000,0000\x69\x62\x0D\x0A', 'suntech' : 'SA200STT;123456;042;20120101;12:11:00;16d41;-15.618767;-056.083214;000.011;000.00;11;1;41557;12.21;000000;1;3205\r', 'h02' : '*HQ,123456789012345,V1,121300,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,ffffffff,000000,000000,000000,000000#', - 'jt600' : '$\x00\x00\x12\x34\x56\x11\x00\x1B\x01\x01\x12\x12\x14\x00\x60\x00\x00\x00\x13\x00\x00\x00\x0F\x00\x00\x07\x50\x00\x00\x00\x2B\x91\x04\x4D\x1F\xA1', + 'jt600' : '(1234567890,P45,290322,132412,25.28217,S,57.54683,W,A,0,0,5,0,0000000000,0,0,9,0)', 'v680' : '#123456789012345#1000#0#1000#AUT#1#66830FFB#13000.0000,E,6000.0000,N,001.41,259#010112#121600##', 'pt502' : '$POS,123456,121700.000,A,6000.0000,N,13000.0000,E,0.0,0.0,010112,,,A/00000,00000/0/23895000//\r\n', 'tr20' : '%%123456789012345,A,120101121800,N6000.0000E13000.0000,0,000,0,01034802,150,[Message]\r\n', @@ -119,6 +118,15 @@ messages = { 'mobilogix': '[2020-10-25 20:45:09,T9,1,V1.2.3,123456789012,59,10.50,701,-25.236860,-45.708530,0,314]', 'swiftech': '@@123456789012345,,0,102040,1023.9670,N,07606.8160,E,2.26,151220,A,0127,1,1,03962,00000,#', 'ennfu': 'Ennfu:123456789012345,041504.00,A,3154.86654,N,11849.08737,E,0.053,,080121,20,3.72,21.4,V0.01$', + 'startek': '&&o125,123456789012345,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0\r\n', + 'hoopo': '{ "deviceId": "123456789012345", "assetName": "123456789012345", "assetType": "test", "eventData": { "latitude": 31.97498, "longitude": 34.80802, "locationName": "", "accuracyLevel": "High", "eventType": "Arrival", "batteryLevel": 100, "receiveTime": "2021-09-20T18:52:32Z" }, "eventTime": "2021-09-20T08:52:02Z", "serverReportTime": "0001-01-01T00:00:00Z" }', + 'techtocruz': '$$A120,123456789012345,211005105836,A,FLEX,KCB 947C,000.0,0,-1.38047,S,36.93951,E,1648.4,243.140,21,28,12.1,3.7,0,1,0,0,0,*F6', + 'flexapi': '${"topic":"v1/123456789012345/motion/info","payload":{"motion.ts":1641885877,"motion.ax":0.006344,"motion.ay":0.289384,"motion.az":-0.939156,"motion.gx":0.420000,"motion.gy":0.420000,"motion.gz":-0.280000}}xx\r\n', + 'jido': '*123456789012345,03,130517,160435,1820.5845,N,07833.2478,E,1,58#', + 'armoli': '[M123456789012345210122125205N38.735641E035.4727751E003340000000C00000E9E07FF:106AG505283H60E];', + 'teratrack': '{"MDeviceID":"022043756090","DiviceType":"1","DataType":"1","DataLength":"69","DateTime":"2022-03-09 10:56:01","Latitude":"-6.846451","Longitude":"39.316324","LongitudeState":"1","LatitudeState":"0","Speed":"90","Mileage":"0","FenceAlarm":"0","AreaAlarmID":"0","LockCutOff":"0","SealTampered":"0","MessageAck":"1","LockRope":"1","LockStatus":"1","LockOpen":"0","PasswordError":"0","CardNo":"60000644","IllegalCard":"0","LowPower":"0","UnCoverBack":"0","CoverStatus":"1","LockStuck":"0","Power":"79","GSM":"16","IMEI":"123456789012345","Index":"20","Slave":[]}', + 'envotech': '$80SLM,02,F,123456,130410155921,431750216,000040,0000,,00000000,\'13041015592110476673N10111459E001281*2A#', + 'bstpl': 'BSTPL$1,123456789012345,V,200722,045113,00.000000,0,00.00000,0,0,0,000,00,0,17,1,1,0,0,00.01,0,04.19,15B_190821,8991000907387031196F,12.27#', } baseUrl = 'http://localhost:8082' @@ -135,14 +143,14 @@ def load_ports(): if key.endswith('.port'): ports[key[:-5]] = int(entry.text) if debug: - print '\nports: %s\n' % repr(ports) + print('\nports: {ports!r}\n') return ports def login(): request = urllib2.Request(baseUrl + '/api/session') - response = urllib2.urlopen(request, urllib.parse.urlencode(user)) + response = urllib2.urlopen(request, urllib.parse.urlencode(user).encode()) if debug: - print '\nlogin: %s\n' % repr(json.load(response)) + print(f'\nlogin: {json.load(response)!r}\n') return response.headers.get('Set-Cookie') def remove_devices(cookie): @@ -151,7 +159,7 @@ def remove_devices(cookie): response = urllib2.urlopen(request) data = json.load(response) if debug: - print '\ndevices: %s\n' % repr(data) + print(f'\ndevices: {data!r}\n') for device in data: request = urllib2.Request(baseUrl + '/api/devices/' + str(device['id'])) request.add_header('Cookie', cookie) @@ -163,15 +171,15 @@ def add_device(cookie, unique_id): request.add_header('Cookie', cookie) request.add_header('Content-Type', 'application/json') device = { 'name' : unique_id, 'uniqueId' : unique_id } - response = urllib2.urlopen(request, json.dumps(device)) + response = urllib2.urlopen(request, json.dumps(device).encode()) data = json.load(response) return data['id'] def send_message(port, message): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', port)) - s.send(message) - time.sleep(0.5) + s.send(message.encode('ascii')) + time.sleep(1.0) s.close() def get_protocols(cookie, device_id): @@ -203,9 +211,9 @@ if __name__ == "__main__": all = set(ports.keys()) protocols = set(messages.keys()) - print 'Total: %d' % len(all) - print 'Missing: %d' % len(all - protocols) - print 'Covered: %d' % len(protocols) + print(f'Total: {len(all)}') + print(f'Missing: {len(all - protocols)}') + print(f'Covered: {len(protocols)}') for protocol in messages: send_message(ports[protocol], messages[protocol]) @@ -213,8 +221,8 @@ if __name__ == "__main__": for device in devices: protocols -= set(get_protocols(cookie, devices[device])) - print 'Success: %d' % (len(messages) - len(protocols)) - print 'Failed: %d' % len(protocols) + print(f'Success: {len(messages) - len(protocols)}') + print(f'Failed:{len(protocols)}') if protocols: - print '\nFailed: %s' % repr(list(protocols)) + print(f'\nFailed: {list(protocols)!r}') diff --git a/tools/test-map.py b/tools/test-map.py index 362c95878..664917eff 100755 --- a/tools/test-map.py +++ b/tools/test-map.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import urllib -import urllib2 +import urllib.request as urllib2 import http.client as httplib import time import random @@ -14,7 +14,7 @@ devices = 500 def login(): request = urllib2.Request(baseUrl + '/api/session') - response = urllib2.urlopen(request, urllib.parse.urlencode(user)) + response = urllib2.urlopen(request, urllib.parse.urlencode(user).encode()) return response.headers.get('Set-Cookie') def add_device(cookie, unique_id): @@ -23,7 +23,7 @@ def add_device(cookie, unique_id): request.add_header('Content-Type', 'application/json') device = { 'name' : unique_id, 'uniqueId' : unique_id } try: - response = urllib2.urlopen(request, json.dumps(device)) + response = urllib2.urlopen(request, json.dumps(device).encode()) except urllib2.HTTPError: pass diff --git a/traccar-web b/traccar-web -Subproject 610ce1e5447d47c316f87887b64c4c459bad855 +Subproject 42db1a41cbf733ea4f82c86e5d45a6fbccc8b8f |