aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans van den Elsen <hans.elsen@esds.nl>2016-03-11 22:16:25 +0100
committerHans van den Elsen <hans.elsen@esds.nl>2016-03-11 22:16:25 +0100
commit4606737cc07b736f9c8f98ae680b928c94c082c8 (patch)
treebdd29b24a16403b1e8a3d7cd4325391063b34b8a
parent3ced99384b1967f5cde9abdf30c1c0184a5ae3f6 (diff)
parentab5d21e16ac44957e04da0f06daf148af95e96be (diff)
downloadtraccar-server-4606737cc07b736f9c8f98ae680b928c94c082c8.tar.gz
traccar-server-4606737cc07b736f9c8f98ae680b928c94c082c8.tar.bz2
traccar-server-4606737cc07b736f9c8f98ae680b928c94c082c8.zip
Merge remote-tracking branch 'refs/remotes/tananaev/master'
-rw-r--r--database/changelog-3.3.xml (renamed from database/db.changelog-3.0.xml)128
-rw-r--r--database/changelog-3.5.xml37
-rw-r--r--database/changelog-master.xml10
-rw-r--r--database/db.changelog-3.1.xml131
-rw-r--r--database/db.changelog-3.2.xml63
-rw-r--r--database/db.changelog-3.3.xml32
-rw-r--r--database/db.changelog-master.xml13
-rw-r--r--database/db.changelog-normalize.xml114
-rw-r--r--debug.xml46
-rw-r--r--pom.xml18
-rwxr-xr-xsetup/package.sh1
-rwxr-xr-xsetup/unix/setup.sh19
-rw-r--r--setup/unix/traccar.xml10
-rw-r--r--setup/windows/traccar.iss2
-rw-r--r--setup/windows/traccar.xml8
-rw-r--r--src/org/traccar/DistanceHandler.java11
-rw-r--r--src/org/traccar/MainEventHandler.java5
-rw-r--r--src/org/traccar/api/AsyncSocket.java6
-rw-r--r--src/org/traccar/api/AsyncSocketServlet.java11
-rw-r--r--src/org/traccar/api/CorsResponseFilter.java14
-rw-r--r--src/org/traccar/api/SecurityRequestFilter.java5
-rw-r--r--src/org/traccar/api/resource/DevicePermissionResource.java (renamed from src/org/traccar/api/resource/PermissionResource.java)12
-rw-r--r--src/org/traccar/api/resource/GroupPermissionResource.java52
-rw-r--r--src/org/traccar/api/resource/GroupResource.java85
-rw-r--r--src/org/traccar/api/resource/PositionResource.java11
-rw-r--r--src/org/traccar/database/DataManager.java101
-rw-r--r--src/org/traccar/database/GroupTree.java153
-rw-r--r--src/org/traccar/database/PermissionsManager.java56
-rw-r--r--src/org/traccar/helper/Checksum.java13
-rw-r--r--src/org/traccar/helper/ObdDecoder.java39
-rw-r--r--src/org/traccar/helper/Parser.java7
-rw-r--r--src/org/traccar/model/Device.java12
-rw-r--r--src/org/traccar/model/DevicePermission.java (renamed from src/org/traccar/model/Permission.java)4
-rw-r--r--src/org/traccar/model/Event.java1
-rw-r--r--src/org/traccar/model/Group.java50
-rw-r--r--src/org/traccar/model/GroupPermission.java40
-rw-r--r--src/org/traccar/protocol/AplicomProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/AstraProtocol.java43
-rw-r--r--src/org/traccar/protocol/AstraProtocolDecoder.java119
-rw-r--r--src/org/traccar/protocol/BoxProtocol.java2
-rw-r--r--src/org/traccar/protocol/CarscopProtocol.java2
-rw-r--r--src/org/traccar/protocol/CastelProtocolDecoder.java40
-rw-r--r--src/org/traccar/protocol/CityeasyProtocol.java2
-rw-r--r--src/org/traccar/protocol/FlextrackProtocol.java2
-rw-r--r--src/org/traccar/protocol/Gl100Protocol.java4
-rw-r--r--src/org/traccar/protocol/Gl200Protocol.java8
-rw-r--r--src/org/traccar/protocol/Gl200ProtocolDecoder.java414
-rw-r--r--src/org/traccar/protocol/GlobalSatProtocol.java2
-rw-r--r--src/org/traccar/protocol/GoSafeProtocol.java2
-rw-r--r--src/org/traccar/protocol/Gps103Protocol.java8
-rw-r--r--src/org/traccar/protocol/Gps103ProtocolDecoder.java9
-rw-r--r--src/org/traccar/protocol/GpsGateProtocol.java2
-rw-r--r--src/org/traccar/protocol/GpsGateProtocolDecoder.java60
-rw-r--r--src/org/traccar/protocol/GpsmtaProtocol.java2
-rw-r--r--src/org/traccar/protocol/Gt06FrameDecoder.java12
-rw-r--r--src/org/traccar/protocol/Gt06Protocol.java2
-rw-r--r--src/org/traccar/protocol/HomtecsProtocol.java43
-rw-r--r--src/org/traccar/protocol/HomtecsProtocolDecoder.java83
-rw-r--r--src/org/traccar/protocol/IntellitracProtocol.java2
-rwxr-xr-xsrc/org/traccar/protocol/KenjiProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/KhdProtocol.java2
-rw-r--r--src/org/traccar/protocol/LaipacProtocol.java2
-rw-r--r--src/org/traccar/protocol/ManPowerProtocol.java2
-rw-r--r--src/org/traccar/protocol/MegastekProtocol.java2
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocol.java4
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolDecoder.java19
-rw-r--r--src/org/traccar/protocol/MeitrackProtocol.java8
-rw-r--r--src/org/traccar/protocol/MeitrackProtocolDecoder.java16
-rw-r--r--src/org/traccar/protocol/MeitrackProtocolEncoder.java23
-rw-r--r--src/org/traccar/protocol/Mta6Protocol.java2
-rw-r--r--src/org/traccar/protocol/MtxProtocol.java2
-rw-r--r--src/org/traccar/protocol/MxtProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/OsmAndProtocol.java2
-rw-r--r--src/org/traccar/protocol/PathAwayProtocol.java2
-rw-r--r--src/org/traccar/protocol/PiligrimProtocol.java2
-rw-r--r--src/org/traccar/protocol/Pt3000Protocol.java2
-rw-r--r--src/org/traccar/protocol/SuntechProtocol.java4
-rw-r--r--src/org/traccar/protocol/T55Protocol.java2
-rw-r--r--src/org/traccar/protocol/T55ProtocolDecoder.java22
-rw-r--r--src/org/traccar/protocol/TaipProtocol.java3
-rw-r--r--src/org/traccar/protocol/TelicFrameDecoder.java54
-rw-r--r--src/org/traccar/protocol/TelicProtocol.java47
-rw-r--r--src/org/traccar/protocol/TelicProtocolDecoder.java (renamed from src/org/traccar/protocol/TelikProtocolDecoder.java)26
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocol.java9
-rw-r--r--src/org/traccar/protocol/Tk102Protocol.java2
-rw-r--r--src/org/traccar/protocol/Tk103Protocol.java4
-rw-r--r--src/org/traccar/protocol/Tlt2hProtocol.java2
-rw-r--r--src/org/traccar/protocol/TotemProtocol.java4
-rw-r--r--src/org/traccar/protocol/Tr20Protocol.java2
-rw-r--r--src/org/traccar/protocol/Tr900Protocol.java4
-rw-r--r--src/org/traccar/protocol/TrackboxProtocol.java2
-rw-r--r--src/org/traccar/protocol/TrvProtocol.java2
-rw-r--r--src/org/traccar/protocol/TrvProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Tt8850Protocol.java (renamed from src/org/traccar/protocol/TelikProtocol.java)12
-rw-r--r--src/org/traccar/protocol/Tt8850ProtocolDecoder.java101
-rw-r--r--src/org/traccar/protocol/UlbotechProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/WatchProtocol.java2
-rw-r--r--src/org/traccar/protocol/WialonProtocol.java2
-rw-r--r--src/org/traccar/protocol/XexunProtocolDecoder.java6
-rw-r--r--src/org/traccar/protocol/XirgoProtocol.java2
-rw-r--r--src/org/traccar/protocol/YwtProtocol.java2
-rw-r--r--src/org/traccar/web/AsyncServlet.java2
-rw-r--r--src/org/traccar/web/BaseServlet.java2
-rw-r--r--src/org/traccar/web/WebServer.java7
-rw-r--r--test/org/traccar/ProtocolTest.java2
-rw-r--r--test/org/traccar/database/GroupTreeTest.java55
-rw-r--r--test/org/traccar/helper/ObdDecoderTest.java10
-rw-r--r--test/org/traccar/protocol/AplicomProtocolDecoderTest.java9
-rw-r--r--test/org/traccar/protocol/AstraProtocolDecoderTest.java18
-rw-r--r--test/org/traccar/protocol/AutoFonProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/Gl100ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/Gl200ProtocolDecoderTest.java93
-rw-r--r--test/org/traccar/protocol/Gps103ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/GpsGateProtocolDecoderTest.java6
-rw-r--r--test/org/traccar/protocol/Gt06FrameDecoderTest.java12
-rw-r--r--test/org/traccar/protocol/HomtecsProtocolDecoderTest.java18
-rw-r--r--test/org/traccar/protocol/MeiligaoProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/MeitrackProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/MeitrackProtocolEncoderTest.java23
-rw-r--r--test/org/traccar/protocol/T55ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/TelicFrameDecoderTest.java30
-rw-r--r--test/org/traccar/protocol/TelicProtocolDecoderTest.java37
-rw-r--r--test/org/traccar/protocol/TelikProtocolDecoderTest.java25
-rw-r--r--test/org/traccar/protocol/TotemProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/Tt8850ProtocolDecoderTest.java33
-rw-r--r--test/org/traccar/protocol/UlbotechProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/XexunProtocolDecoderTest.java6
-rwxr-xr-xtools/test-integration.py5
-rw-r--r--web/app.css4
-rw-r--r--web/app/Application.js3
-rw-r--r--web/app/controller/Root.js3
-rw-r--r--web/app/model/Device.js5
-rw-r--r--web/app/model/Group.js31
-rw-r--r--web/app/model/Position.js9
-rw-r--r--web/app/store/AllGroups.js28
-rw-r--r--web/app/store/Groups.js28
-rw-r--r--web/app/view/DeviceDialog.js9
-rw-r--r--web/app/view/Devices.js47
-rw-r--r--web/app/view/DevicesController.js66
-rw-r--r--web/app/view/GroupDialog.js43
-rw-r--r--web/app/view/Groups.js44
-rw-r--r--web/app/view/GroupsController.js63
-rw-r--r--web/app/view/MapController.js44
-rw-r--r--web/app/view/Register.js2
-rw-r--r--web/app/view/SettingsMenu.js3
-rw-r--r--web/app/view/SettingsMenuController.js11
-rw-r--r--web/app/view/UserDevices.js8
-rw-r--r--web/app/view/UserDevicesController.js4
-rw-r--r--web/app/view/UserDialog.js2
-rw-r--r--web/app/view/UserGroups.js44
-rw-r--r--web/app/view/UserGroupsController.js79
-rw-r--r--web/app/view/Users.js7
-rw-r--r--web/app/view/UsersController.js13
-rw-r--r--web/arrowstyle.js41
-rw-r--r--web/debug.html7
-rw-r--r--web/l10n/en.json6
-rw-r--r--web/l10n/es.json14
-rw-r--r--web/l10n/fa.json83
-rw-r--r--web/l10n/fi.json83
-rw-r--r--web/l10n/lt.json6
-rw-r--r--web/l10n/ml.json83
-rw-r--r--web/l10n/ms.json83
-rw-r--r--web/l10n/no.json2
-rw-r--r--web/l10n/tr.json83
-rw-r--r--web/locale.js8
-rw-r--r--web/release.html4
166 files changed, 3177 insertions, 944 deletions
diff --git a/database/db.changelog-3.0.xml b/database/changelog-3.3.xml
index 453f6f00b..1893a0200 100644
--- a/database/db.changelog-3.0.xml
+++ b/database/changelog-3.3.xml
@@ -5,28 +5,28 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">
- <changeSet author="author" id="changelog-3.0">
+ <changeSet author="author" id="changelog-3.3">
<preConditions onFail="MARK_RAN">
<not>
- <tableExists tableName="traccar" />
+ <tableExists tableName="server" />
</not>
</preConditions>
- <createTable tableName="user">
+ <createTable tableName="users">
<column name="id" type="INT" autoIncrement="true">
<constraints primaryKey="true" />
</column>
- <column name="name" type="VARCHAR(1024)">
+ <column name="name" type="VARCHAR(128)">
<constraints nullable="false" />
</column>
- <column name="email" type="VARCHAR(256)">
+ <column name="email" type="VARCHAR(128)">
<constraints nullable="false" />
</column>
- <column name="hashedPassword" type="VARCHAR(1024)">
+ <column name="hashedpassword" type="VARCHAR(128)">
<constraints nullable="false" />
</column>
- <column name="salt" type="VARCHAR(1024)" defaultValue="">
+ <column name="salt" type="VARCHAR(128)">
<constraints nullable="false" />
</column>
<column name="readonly" type="BOOLEAN" defaultValueBoolean="false">
@@ -35,16 +35,16 @@
<column name="admin" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
- <column name="map" type="VARCHAR(1024)" defaultValue="osm">
+ <column name="map" type="VARCHAR(128)" defaultValue="osm">
<constraints nullable="false" />
</column>
- <column name="language" type="VARCHAR(1024)" defaultValue="en">
+ <column name="language" type="VARCHAR(128)" defaultValue="en">
<constraints nullable="false" />
</column>
- <column name="distanceUnit" type="VARCHAR(1024)" defaultValue="km">
+ <column name="distanceunit" type="VARCHAR(128)" defaultValue="km">
<constraints nullable="false" />
</column>
- <column name="speedUnit" type="VARCHAR(1024)" defaultValue="kmh">
+ <column name="speedunit" type="VARCHAR(128)" defaultValue="kmh">
<constraints nullable="false" />
</column>
<column name="latitude" type="DOUBLE" defaultValueNumeric="0">
@@ -58,63 +58,56 @@
</column>
</createTable>
- <addUniqueConstraint tableName="user" columnNames="email" constraintName="uk_user_email" />
+ <addUniqueConstraint tableName="users" columnNames="email" constraintName="uk_user_email" />
- <createTable tableName="device">
+ <createTable tableName="devices">
<column name="id" type="INT" autoIncrement="true">
<constraints primaryKey="true" />
</column>
- <column name="name" type="VARCHAR(1024)">
+ <column name="name" type="VARCHAR(128)">
<constraints nullable="false" />
</column>
- <column name="uniqueId" type="VARCHAR(256)">
+ <column name="uniqueid" type="VARCHAR(128)">
<constraints nullable="false" />
</column>
- <column name="status" type="VARCHAR(1024)" />
- <column name="lastUpdate" type="TIMESTAMP" />
- <column name="positionId" type="INT" />
- <column name="dataId" type="INT" />
+ <column name="status" type="VARCHAR(128)" />
+ <column name="lastupdate" type="TIMESTAMP" />
+ <column name="positionid" type="INT" />
</createTable>
- <addUniqueConstraint tableName="device" columnNames="uniqueId" constraintName="uk_device_uniqueId" />
+ <addUniqueConstraint tableName="devices" columnNames="uniqueid" constraintName="uk_device_uniqueid" />
<createTable tableName="user_device">
- <column name="userId" type="INT">
+ <column name="userid" type="INT">
<constraints nullable="false" />
</column>
- <column name="deviceId" type="INT">
- <constraints nullable="false" />
- </column>
- <column name="read" type="BOOLEAN" defaultValueBoolean="true">
- <constraints nullable="false" />
- </column>
- <column name="write" type="BOOLEAN" defaultValueBoolean="true">
+ <column name="deviceid" type="INT">
<constraints nullable="false" />
</column>
</createTable>
- <addForeignKeyConstraint baseTableName="user_device" baseColumnNames="userId" constraintName="fk_user_device_userId" referencedTableName="user" referencedColumnNames="id" onDelete="CASCADE" />
- <addForeignKeyConstraint baseTableName="user_device" baseColumnNames="deviceId" constraintName="fk_user_device_deviceId" referencedTableName="device" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="user_device" baseColumnNames="userid" constraintName="fk_user_device_userid" referencedTableName="users" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="user_device" baseColumnNames="deviceid" constraintName="fk_user_device_deviceid" referencedTableName="devices" referencedColumnNames="id" onDelete="CASCADE" />
<createIndex tableName="user_device" indexName="user_device_user_id">
- <column name="userId" />
+ <column name="userid" />
</createIndex>
- <createTable tableName="position">
+ <createTable tableName="positions">
<column name="id" type="INT" autoIncrement="true">
<constraints primaryKey="true" />
</column>
- <column name="protocol" type="VARCHAR(1024)" />
- <column name="deviceId" type="int">
+ <column name="protocol" type="VARCHAR(128)" />
+ <column name="deviceid" type="int">
<constraints nullable="false" />
</column>
- <column name="serverTime" type="TIMESTAMP">
+ <column name="servertime" type="TIMESTAMP">
<constraints nullable="false" />
</column>
- <column name="deviceTime" type="TIMESTAMP">
+ <column name="devicetime" type="TIMESTAMP">
<constraints nullable="false" />
</column>
- <column name="fixTime" type="TIMESTAMP">
+ <column name="fixtime" type="TIMESTAMP">
<constraints nullable="false" />
</column>
<column name="valid" type="BOOLEAN">
@@ -126,65 +119,52 @@
<column name="longitude" type="DOUBLE">
<constraints nullable="false" />
</column>
- <column name="altitude" type="DOUBLE">
+ <column name="altitude" type="FLOAT">
<constraints nullable="false" />
</column>
- <column name="speed" type="DOUBLE">
+ <column name="speed" type="FLOAT">
<constraints nullable="false" />
</column>
- <column name="course" type="DOUBLE">
+ <column name="course" type="FLOAT">
<constraints nullable="false" />
</column>
- <column name="address" type="VARCHAR(1024)" />
- <column name="other" type="VARCHAR(8192)">
+ <column name="address" type="VARCHAR(512)" />
+ <column name="attributes" type="VARCHAR(4096)">
<constraints nullable="false" />
</column>
</createTable>
- <addForeignKeyConstraint baseTableName="position" baseColumnNames="deviceId" constraintName="fk_position_deviceId" referencedTableName="device" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="positions" baseColumnNames="deviceid" constraintName="fk_position_deviceid" referencedTableName="devices" referencedColumnNames="id" onDelete="CASCADE" />
- <createTable tableName="data">
- <column name="id" type="INT" autoIncrement="true">
- <constraints primaryKey="true" />
- </column>
- <column name="protocol" type="VARCHAR(1024)" />
- <column name="deviceId" type="int">
- <constraints nullable="false" />
- </column>
- <column name="serverTime" type="TIMESTAMP">
- <constraints nullable="false" />
- </column>
- <column name="deviceTime" type="TIMESTAMP">
- <constraints nullable="false" />
- </column>
- <column name="other" type="VARCHAR(8192)">
- <constraints nullable="false" />
- </column>
- </createTable>
-
- <addForeignKeyConstraint baseTableName="data" baseColumnNames="deviceId" constraintName="fk_data_deviceId" referencedTableName="device" referencedColumnNames="id" onDelete="CASCADE" />
+ <createIndex tableName="positions" indexName="position_deviceid_fixtime">
+ <column name="deviceid" />
+ <column name="fixtime" />
+ </createIndex>
<createTable tableName="server">
<column name="id" type="INT" autoIncrement="true">
<constraints primaryKey="true" />
</column>
- <column name="registration" type="BOOLEAN">
+ <column name="registration" type="BOOLEAN" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
- <column name="latitude" type="DOUBLE">
+ <column name="latitude" type="DOUBLE" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
- <column name="longitude" type="DOUBLE">
+ <column name="longitude" type="DOUBLE" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
- <column name="zoom" type="INT">
+ <column name="zoom" type="INT" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
- </createTable>
-
- <createTable tableName="traccar">
- <column name="id" type="INT" autoIncrement="true">
- <constraints primaryKey="true" />
+ <column name="map" type="VARCHAR(128)" />
+ <column name="language" type="VARCHAR(128)" />
+ <column name="distanceunit" type="VARCHAR(128)" />
+ <column name="speedunit" type="VARCHAR(128)" />
+ <column name="bingkey" type="VARCHAR(128)" />
+ <column name="mapurl" type="VARCHAR(128)" />
+ <column name="readonly" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false" />
</column>
</createTable>
@@ -195,10 +175,10 @@
<column name="zoom" valueNumeric="0" />
</insert>
- <insert tableName="user">
+ <insert tableName="users">
<column name="name" value="admin" />
<column name="email" value="admin" />
- <column name="hashedPassword" value="D33DCA55ABD4CC5BC76F2BC0B4E603FE2C6F61F4C1EF2D47" />
+ <column name="hashedpassword" value="D33DCA55ABD4CC5BC76F2BC0B4E603FE2C6F61F4C1EF2D47" />
<column name="salt" value="000000000000000000000000000000000000000000000000" />
<column name="admin" valueBoolean="true" />
</insert>
diff --git a/database/changelog-3.5.xml b/database/changelog-3.5.xml
new file mode 100644
index 000000000..f1af6ef10
--- /dev/null
+++ b/database/changelog-3.5.xml
@@ -0,0 +1,37 @@
+<?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">
+
+ <changeSet author="author" id="changelog-3.5">
+
+ <createTable tableName="groups">
+ <column name="id" type="INT" autoIncrement="true">
+ <constraints primaryKey="true" />
+ </column>
+ <column name="name" type="VARCHAR(128)">
+ <constraints nullable="false" />
+ </column>
+ <column name="groupid" type="INT" />
+ </createTable>
+
+ <createTable tableName="user_group">
+ <column name="userid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="groupid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="user_group" baseColumnNames="userid" constraintName="fk_user_group_userid" referencedTableName="users" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="user_group" baseColumnNames="groupid" constraintName="fk_user_group_groupid" referencedTableName="groups" referencedColumnNames="id" onDelete="CASCADE" />
+
+ <addColumn tableName="devices">
+ <column name="groupid" type="INT" />
+ </addColumn>
+
+ </changeSet>
+</databaseChangeLog>
diff --git a/database/changelog-master.xml b/database/changelog-master.xml
new file mode 100644
index 000000000..269a0da1a
--- /dev/null
+++ b/database/changelog-master.xml
@@ -0,0 +1,10 @@
+<?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">
+
+ <include file="changelog-3.3.xml" relativeToChangelogFile="true" />
+ <include file="changelog-3.5.xml" relativeToChangelogFile="true" />
+</databaseChangeLog>
diff --git a/database/db.changelog-3.1.xml b/database/db.changelog-3.1.xml
deleted file mode 100644
index 740da4e2f..000000000
--- a/database/db.changelog-3.1.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-<?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">
-
- <changeSet author="author" id="changelog-3.1">
-
- <preConditions onFail="MARK_RAN">
- <not>
- <columnExists tableName="traccar" columnName="version" />
- </not>
- </preConditions>
-
- <dropNotNullConstraint tableName="user" columnName="name" columnDataType="VARCHAR(1024)" />
- <modifyDataType tableName="user" columnName="name" newDataType="VARCHAR(128)" />
- <addNotNullConstraint tableName="user" columnName="name" columnDataType="VARCHAR(128)" />
- <dropUniqueConstraint tableName="user" uniqueColumns="email" constraintName="uk_user_email" />
- <dropNotNullConstraint tableName="user" columnName="email" columnDataType="VARCHAR(256)" />
- <modifyDataType tableName="user" columnName="email" newDataType="VARCHAR(128)" />
- <addNotNullConstraint tableName="user" columnName="email" columnDataType="VARCHAR(128)" />
- <addUniqueConstraint tableName="user" columnNames="email" constraintName="uk_user_email" />
- <dropNotNullConstraint tableName="user" columnName="hashedPassword" columnDataType="VARCHAR(1024)" />
- <modifyDataType tableName="user" columnName="hashedPassword" newDataType="VARCHAR(128)" />
- <addNotNullConstraint tableName="user" columnName="hashedPassword" columnDataType="VARCHAR(128)" />
- <dropDefaultValue tableName="user" columnName="salt" />
- <dropNotNullConstraint tableName="user" columnName="salt" columnDataType="VARCHAR(1024)" />
- <modifyDataType tableName="user" columnName="salt" newDataType="VARCHAR(128)" />
- <addNotNullConstraint tableName="user" columnName="salt" columnDataType="VARCHAR(128)" />
- <addDefaultValue tableName="user" columnName="salt" defaultValue="" />
- <dropDefaultValue tableName="user" columnName="map" />
- <dropNotNullConstraint tableName="user" columnName="map" columnDataType="VARCHAR(128)" />
- <modifyDataType tableName="user" columnName="map" newDataType="VARCHAR(128)" />
- <dropDefaultValue tableName="user" columnName="language" />
- <dropNotNullConstraint tableName="user" columnName="language" columnDataType="VARCHAR(128)" />
- <modifyDataType tableName="user" columnName="language" newDataType="VARCHAR(128)" />
- <dropDefaultValue tableName="user" columnName="distanceUnit" />
- <dropNotNullConstraint tableName="user" columnName="distanceUnit" columnDataType="VARCHAR(128)" />
- <modifyDataType tableName="user" columnName="distanceUnit" newDataType="VARCHAR(128)" />
- <dropDefaultValue tableName="user" columnName="speedUnit" />
- <dropNotNullConstraint tableName="user" columnName="speedUnit" columnDataType="VARCHAR(128)" />
- <modifyDataType tableName="user" columnName="speedUnit" newDataType="VARCHAR(128)" />
- <dropDefaultValue tableName="user" columnName="latitude" />
- <dropNotNullConstraint tableName="user" columnName="latitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="user" columnName="latitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="user" columnName="latitude" columnDataType="FLOAT" />
- <addDefaultValue tableName="user" columnName="latitude" defaultValueNumeric="0" />
- <dropDefaultValue tableName="user" columnName="longitude" />
- <dropNotNullConstraint tableName="user" columnName="longitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="user" columnName="longitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="user" columnName="longitude" columnDataType="FLOAT" />
- <addDefaultValue tableName="user" columnName="longitude" defaultValueNumeric="0" />
-
- <dropNotNullConstraint tableName="device" columnName="name" columnDataType="VARCHAR(1024)" />
- <modifyDataType tableName="device" columnName="name" newDataType="VARCHAR(128)" />
- <addNotNullConstraint tableName="device" columnName="name" columnDataType="VARCHAR(128)" />
- <dropUniqueConstraint tableName="device" uniqueColumns="uniqueId" constraintName="uk_device_uniqueId" />
- <dropNotNullConstraint tableName="device" columnName="uniqueId" columnDataType="VARCHAR(256)" />
- <modifyDataType tableName="device" columnName="uniqueId" newDataType="VARCHAR(128)" />
- <addNotNullConstraint tableName="device" columnName="uniqueId" columnDataType="VARCHAR(128)" />
- <addUniqueConstraint tableName="device" columnNames="uniqueId" constraintName="uk_device_uniqueId" />
- <modifyDataType tableName="device" columnName="status" newDataType="VARCHAR(128)" />
-
- <modifyDataType tableName="position" columnName="protocol" newDataType="VARCHAR(128)" />
- <dropNotNullConstraint tableName="position" columnName="latitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="position" columnName="latitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="position" columnName="latitude" columnDataType="FLOAT" />
- <dropNotNullConstraint tableName="position" columnName="longitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="position" columnName="longitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="position" columnName="longitude" columnDataType="FLOAT" />
- <dropNotNullConstraint tableName="position" columnName="altitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="position" columnName="altitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="position" columnName="altitude" columnDataType="FLOAT" />
- <dropNotNullConstraint tableName="position" columnName="speed" columnDataType="DOUBLE" />
- <modifyDataType tableName="position" columnName="speed" newDataType="FLOAT" />
- <addNotNullConstraint tableName="position" columnName="speed" columnDataType="FLOAT" />
- <dropNotNullConstraint tableName="position" columnName="course" columnDataType="DOUBLE" />
- <modifyDataType tableName="position" columnName="course" newDataType="FLOAT" />
- <addNotNullConstraint tableName="position" columnName="course" columnDataType="FLOAT" />
- <modifyDataType tableName="position" columnName="address" newDataType="VARCHAR(512)" />
- <dropNotNullConstraint tableName="position" columnName="other" columnDataType="VARCHAR(8192)" />
- <modifyDataType tableName="position" columnName="other" newDataType="VARCHAR(4096)" />
- <addNotNullConstraint tableName="position" columnName="other" columnDataType="VARCHAR(4096)" />
-
- <createIndex tableName="position" indexName="position_deviceId_fixTime">
- <column name="deviceId" />
- <column name="fixTime" />
- </createIndex>
-
- <modifyDataType tableName="data" columnName="protocol" newDataType="VARCHAR(128)" />
- <dropNotNullConstraint tableName="data" columnName="other" columnDataType="VARCHAR(8192)" />
- <modifyDataType tableName="data" columnName="other" newDataType="VARCHAR(4096)" />
- <addNotNullConstraint tableName="data" columnName="other" columnDataType="VARCHAR(4096)" />
-
- <addColumn tableName="server">
- <column name="map" type="VARCHAR(128)" />
- </addColumn>
- <addColumn tableName="server">
- <column name="language" type="VARCHAR(128)" />
- </addColumn>
- <addColumn tableName="server">
- <column name="distanceUnit" type="VARCHAR(128)" />
- </addColumn>
- <addColumn tableName="server">
- <column name="speedUnit" type="VARCHAR(128)" />
- </addColumn>
- <dropNotNullConstraint tableName="server" columnName="latitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="server" columnName="latitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="server" columnName="latitude" columnDataType="FLOAT" />
- <addDefaultValue tableName="server" columnName="latitude" defaultValueNumeric="0" />
- <dropNotNullConstraint tableName="server" columnName="longitude" columnDataType="DOUBLE" />
- <modifyDataType tableName="server" columnName="longitude" newDataType="FLOAT" />
- <addNotNullConstraint tableName="server" columnName="longitude" columnDataType="FLOAT" />
- <addDefaultValue tableName="server" columnName="longitude" defaultValueNumeric="0" />
- <addDefaultValue tableName="server" columnName="zoom" defaultValueNumeric="0" />
-
- <dropTable tableName="traccar" />
-
- <createTable tableName="traccar">
- <column name="version" type="INT" defaultValueNumeric="0">
- <constraints nullable="false" />
- </column>
- </createTable>
-
- <insert tableName="traccar">
- <column name="version" valueNumeric="301" />
- </insert>
-
- </changeSet>
-</databaseChangeLog>
diff --git a/database/db.changelog-3.2.xml b/database/db.changelog-3.2.xml
deleted file mode 100644
index fdeabfb68..000000000
--- a/database/db.changelog-3.2.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?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">
-
- <changeSet author="author" id="changelog-3.2">
-
- <preConditions onFail="MARK_RAN">
- <not>
- <columnExists tableName="position" columnName="attributes" />
- </not>
- </preConditions>
-
- <dropDefaultValue tableName="user" columnName="latitude" />
- <dropNotNullConstraint tableName="user" columnName="latitude" columnDataType="FLOAT" />
- <modifyDataType tableName="user" columnName="latitude" newDataType="DOUBLE" />
- <addNotNullConstraint tableName="user" columnName="latitude" columnDataType="DOUBLE" />
- <addDefaultValue tableName="user" columnName="latitude" defaultValueNumeric="0" />
- <dropDefaultValue tableName="user" columnName="longitude" />
- <dropNotNullConstraint tableName="user" columnName="longitude" columnDataType="FLOAT" />
- <modifyDataType tableName="user" columnName="longitude" newDataType="DOUBLE" />
- <addNotNullConstraint tableName="user" columnName="longitude" columnDataType="DOUBLE" />
- <addDefaultValue tableName="user" columnName="longitude" defaultValueNumeric="0" />
-
- <dropColumn tableName="device" columnName="dataId" />
-
- <dropNotNullConstraint tableName="position" columnName="latitude" columnDataType="FLOAT" />
- <modifyDataType tableName="position" columnName="latitude" newDataType="DOUBLE" />
- <addNotNullConstraint tableName="position" columnName="latitude" columnDataType="DOUBLE" />
- <dropNotNullConstraint tableName="position" columnName="longitude" columnDataType="FLOAT" />
- <modifyDataType tableName="position" columnName="longitude" newDataType="DOUBLE" />
- <addNotNullConstraint tableName="position" columnName="longitude" columnDataType="DOUBLE" />
- <dropNotNullConstraint tableName="position" columnName="other" columnDataType="VARCHAR(4096)" />
- <renameColumn tableName="position" oldColumnName="other" newColumnName="attributes" columnDataType="VARCHAR(4096)" />
- <addNotNullConstraint tableName="position" columnName="attributes" columnDataType="VARCHAR(4096)" />
-
- <dropTable tableName="data" />
-
- <addColumn tableName="server">
- <column name="bingKey" type="VARCHAR(128)" />
- </addColumn>
- <addColumn tableName="server">
- <column name="mapUrl" type="VARCHAR(128)" />
- </addColumn>
- <dropDefaultValue tableName="server" columnName="latitude" />
- <dropNotNullConstraint tableName="server" columnName="latitude" columnDataType="FLOAT" />
- <modifyDataType tableName="server" columnName="latitude" newDataType="DOUBLE" />
- <addNotNullConstraint tableName="server" columnName="latitude" columnDataType="DOUBLE" />
- <addDefaultValue tableName="server" columnName="latitude" defaultValueNumeric="0" />
- <dropDefaultValue tableName="server" columnName="longitude" />
- <dropNotNullConstraint tableName="server" columnName="longitude" columnDataType="FLOAT" />
- <modifyDataType tableName="server" columnName="longitude" newDataType="DOUBLE" />
- <addNotNullConstraint tableName="server" columnName="longitude" columnDataType="DOUBLE" />
- <addDefaultValue tableName="server" columnName="longitude" defaultValueNumeric="0" />
-
- <update tableName="traccar">
- <column name="version" valueNumeric="302" />
- </update>
-
- </changeSet>
-</databaseChangeLog>
diff --git a/database/db.changelog-3.3.xml b/database/db.changelog-3.3.xml
deleted file mode 100644
index 22fcd6f5c..000000000
--- a/database/db.changelog-3.3.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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">
-
- <changeSet author="author" id="changelog-3.3">
-
- <dropDefaultValue tableName="user" columnName="salt" />
-
- <addDefaultValue tableName="server" columnName="registration" defaultValueBoolean="true" />
-
- <addColumn tableName="server">
- <column name="readonly" type="BOOLEAN" defaultValueBoolean="false">
- <constraints nullable="false" />
- </column>
- </addColumn>
-
- <dropTable tableName="traccar" />
-
- <renameTable oldTableName="user" newTableName="users" />
- <renameTable oldTableName="device" newTableName="devices" />
- <renameTable oldTableName="position" newTableName="positions" />
-
- <dropDefaultValue tableName="user_device" columnName="read" />
- <dropColumn tableName="user_device" columnName="read" />
- <dropDefaultValue tableName="user_device" columnName="write" />
- <dropColumn tableName="user_device" columnName="write" />
-
- </changeSet>
-</databaseChangeLog>
diff --git a/database/db.changelog-master.xml b/database/db.changelog-master.xml
deleted file mode 100644
index 06fc7a695..000000000
--- a/database/db.changelog-master.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?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">
-
- <include file="db.changelog-normalize.xml" relativeToChangelogFile="true" />
- <include file="db.changelog-3.0.xml" relativeToChangelogFile="true" />
- <include file="db.changelog-3.1.xml" relativeToChangelogFile="true" />
- <include file="db.changelog-3.2.xml" relativeToChangelogFile="true" />
- <include file="db.changelog-3.3.xml" relativeToChangelogFile="true" />
-</databaseChangeLog>
diff --git a/database/db.changelog-normalize.xml b/database/db.changelog-normalize.xml
deleted file mode 100644
index 7621846ca..000000000
--- a/database/db.changelog-normalize.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?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">
-
- <changeSet author="author" id="changelog-normalize-user">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- <not>
- <columnExists tableName="user" columnName="id"/>
- </not>
- </preConditions>
-
- <renameTable oldTableName="quotedUser" newTableName="user" />
-
- <modifySql>
- <replace replace="quotedUser" with="&quot;user&quot;" />
- </modifySql>
-
- </changeSet>
-
- <changeSet author="author" id="changelog-normalize-user-device">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- <not>
- <columnExists tableName="user_device" columnName="read"/>
- </not>
- </preConditions>
-
- <renameColumn tableName="user_device" oldColumnName="quotedRead" newColumnName="read" columnDataType="BOOLEAN" />
- <renameColumn tableName="user_device" oldColumnName="quotedWrite" newColumnName="write" columnDataType="BOOLEAN" />
-
- <modifySql>
- <replace replace="quotedRead" with="&quot;read&quot;" />
- <replace replace="quotedWrite" with="&quot;write&quot;" />
- </modifySql>
-
- </changeSet>
-
- <changeSet author="author" id="changelog-normalize-fk-h2">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- <dbms type="h2" />
- </preConditions>
-
- <sql>CREATE ALIAS IF NOT EXISTS EXECUTE AS $$ void executeSql(Connection conn, String sql) throws SQLException { try { conn.createStatement().executeUpdate(sql); } catch (Exception e) {} } $$;</sql>
-
- <sql>CALL EXECUTE('ALTER TABLE POSITION DROP CONSTRAINT ' || (SELECT DISTINCT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE CONSTRAINT_TYPE = 'REFERENTIAL' AND TABLE_NAME = 'POSITION' AND COLUMN_LIST = 'DEVICEID'));</sql>
- <sql>CALL EXECUTE('ALTER TABLE DEVICE DROP CONSTRAINT ' || (SELECT DISTINCT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE CONSTRAINT_TYPE = 'REFERENTIAL' AND TABLE_NAME = 'DEVICE' AND COLUMN_LIST = 'POSITIONID'));</sql>
- <sql>CALL EXECUTE('ALTER TABLE DEVICE DROP CONSTRAINT ' || (SELECT DISTINCT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE CONSTRAINT_TYPE = 'REFERENTIAL' AND TABLE_NAME = 'DEVICE' AND COLUMN_LIST = 'DATAID'));</sql>
- <sql>CALL EXECUTE('ALTER TABLE USER_DEVICE DROP CONSTRAINT ' || (SELECT DISTINCT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE CONSTRAINT_TYPE = 'REFERENTIAL' AND TABLE_NAME = 'USER_DEVICE' AND COLUMN_LIST = 'USERID'));</sql>
- <sql>CALL EXECUTE('ALTER TABLE USER_DEVICE DROP CONSTRAINT ' || (SELECT DISTINCT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE CONSTRAINT_TYPE = 'REFERENTIAL' AND TABLE_NAME = 'USER_DEVICE' AND COLUMN_LIST = 'DEVICEID'));</sql>
- <sql>CALL EXECUTE('ALTER TABLE DATA DROP CONSTRAINT ' || (SELECT DISTINCT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS WHERE CONSTRAINT_TYPE = 'REFERENTIAL' AND TABLE_NAME = 'DATA' AND COLUMN_LIST = 'DEVICEID'));</sql>
-
- </changeSet>
-
- <changeSet author="author" id="changelog-normalize-fk-other">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- <not>
- <dbms type="h2" />
- </not>
- </preConditions>
-
- <dropAllForeignKeyConstraints baseTableName="position" />
- <dropAllForeignKeyConstraints baseTableName="device" />
- <dropAllForeignKeyConstraints baseTableName="user_device" />
-
- </changeSet>
-
- <changeSet author="author" id="changelog-normalize-fk">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- </preConditions>
-
- <addForeignKeyConstraint baseTableName="user_device" baseColumnNames="userId" constraintName="fk_user_device_userId" referencedTableName="user" referencedColumnNames="id" onDelete="CASCADE" />
- <addForeignKeyConstraint baseTableName="user_device" baseColumnNames="deviceId" constraintName="fk_user_device_deviceId" referencedTableName="device" referencedColumnNames="id" onDelete="CASCADE" />
-
- <addForeignKeyConstraint baseTableName="position" baseColumnNames="deviceId" constraintName="fk_position_deviceId" referencedTableName="device" referencedColumnNames="id" onDelete="CASCADE" />
-
- </changeSet>
-
- <changeSet author="author" id="changelog-normalize-fk-data-other">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- <tableExists tableName="data" />
- <not>
- <dbms type="h2" />
- </not>
- </preConditions>
-
- <dropAllForeignKeyConstraints baseTableName="data" />
-
- </changeSet>
-
- <changeSet author="author" id="changelog-normalize-fk-data">
-
- <preConditions onFail="MARK_RAN">
- <tableExists tableName="traccar" />
- <tableExists tableName="data" />
- </preConditions>
-
- <addForeignKeyConstraint baseTableName="data" baseColumnNames="deviceId" constraintName="fk_data_deviceId" referencedTableName="device" referencedColumnNames="id" onDelete="CASCADE" />
-
- </changeSet>
-</databaseChangeLog>
diff --git a/debug.xml b/debug.xml
index e641bda56..8e0b70fbf 100644
--- a/debug.xml
+++ b/debug.xml
@@ -14,6 +14,7 @@
<entry key='web.path'>web</entry>
<entry key='web.debug'>true</entry>
<entry key='web.console'>true</entry>
+ <entry key='web.timeout'>30000</entry>
<entry key='geocoder.enable'>true</entry>
<entry key='geocoder.type'>nominatim</entry>
@@ -48,7 +49,7 @@
<entry key='database.user'>sa</entry>
<entry key='database.password'></entry>
<entry key='database.xml'>false</entry>
- <entry key='database.changelog'>./database/db.changelog-master.xml</entry>
+ <entry key='database.changelog'>./database/changelog-master.xml</entry>
<entry key='database.selectServers'>
SELECT * FROM server;
@@ -112,24 +113,24 @@
DELETE FROM users WHERE id = :id;
</entry>
- <entry key='database.getPermissionsAll'>
+ <entry key='database.selectDevicePermissions'>
SELECT userId, deviceId FROM user_device;
</entry>
- <entry key='database.selectDevicesAll'>
- SELECT * FROM devices;
+ <entry key='database.selectGroupPermissions'>
+ SELECT userId, groupId FROM user_group;
</entry>
- <entry key='database.selectDevices'>
- SELECT * FROM devices d INNER JOIN user_device ud ON d.id = ud.deviceId WHERE ud.userId = :userId;
+ <entry key='database.selectDevicesAll'>
+ SELECT * FROM devices;
</entry>
<entry key='database.insertDevice'>
- INSERT INTO devices (name, uniqueId) VALUES (:name, :uniqueId);
+ INSERT INTO devices (name, uniqueId, groupId) VALUES (:name, :uniqueId, :groupId);
</entry>
<entry key='database.updateDevice'>
- UPDATE devices SET name = :name, uniqueId = :uniqueId WHERE id = :id;
+ UPDATE devices SET name = :name, uniqueId = :uniqueId, groupId = :groupId WHERE id = :id;
</entry>
<entry key='database.updateDeviceStatus'>
@@ -148,6 +149,30 @@
DELETE FROM user_device WHERE userId = :userId AND deviceId = :deviceId;
</entry>
+ <entry key='database.selectGroupsAll'>
+ SELECT * FROM groups;
+ </entry>
+
+ <entry key='database.insertGroup'>
+ INSERT INTO groups (name) VALUES (:name);
+ </entry>
+
+ <entry key='database.updateGroup'>
+ UPDATE groups SET name = :name, groupId = :groupId WHERE id = :id;
+ </entry>
+
+ <entry key='database.deleteGroup'>
+ DELETE FROM groups WHERE id = :id;
+ </entry>
+
+ <entry key='database.linkGroup'>
+ INSERT INTO user_group (userId, groupId) VALUES (:userId, :groupId);
+ </entry>
+
+ <entry key='database.unlinkGroup'>
+ DELETE FROM user_group WHERE userId = :userId AND groupId = :groupId;
+ </entry>
+
<entry key='database.selectPositions'>
SELECT * FROM positions WHERE deviceId = :deviceId AND fixTime BETWEEN :from AND :to ORDER BY fixTime;
</entry>
@@ -235,7 +260,7 @@
<entry key='eelink.port'>5064</entry>
<entry key='box.port'>5065</entry>
<entry key='freedom.port'>5066</entry>
- <entry key='telik.port'>5067</entry>
+ <entry key='telic.port'>5067</entry>
<entry key='trackbox.port'>5068</entry>
<entry key='visiontek.port'>5069</entry>
<entry key='orion.port'>5070</entry>
@@ -247,6 +272,7 @@
<entry key='xt013.port'>5076</entry>
<entry key='autofon.port'>5077</entry>
<entry key='gosafe.port'>5078</entry>
+ <entry key='tt8850.port'>5079</entry>
<entry key='bce.port'>5080</entry>
<entry key='xirgo.port'>5081</entry>
<entry key='calamp.port'>5082</entry>
@@ -270,5 +296,7 @@
<entry key='arnavi.port'>5100</entry>
<entry key='nvs.port'>5101</entry>
<entry key='kenji.port'>5102</entry>
+ <entry key='astra.port'>5103</entry>
+ <entry key='homtecs.port'>5104</entry>
</properties>
diff --git a/pom.xml b/pom.xml
index bfe2a886b..9949fafcf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,15 +4,15 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.traccar</groupId>
<artifactId>traccar</artifactId>
- <version>3.3-SNAPSHOT</version>
+ <version>3.4-SNAPSHOT</version>
<name>traccar</name>
<url>https://www.traccar.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <jetty.version>9.2.14.v20151106</jetty.version> <!-- Jetty 9.3+ requires Java 8 -->
- <jersey.version>2.22.1</jersey.version>
+ <jetty.version>9.2.15.v20160210</jetty.version> <!-- Jetty 9.3+ requires Java 8 -->
+ <jersey.version>2.22.2</jersey.version>
</properties>
<dependencies>
@@ -30,12 +30,12 @@
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
- <version>2.9.1</version>
+ <version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
- <version>1.4.190</version>
+ <version>1.4.191</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
@@ -45,7 +45,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
- <version>9.4-1206-jdbc41</version>
+ <version>9.4.1208.jre7</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
@@ -60,12 +60,12 @@
<dependency>
<groupId>com.ning</groupId> <!-- org.asynchttpclient starting from version 2.0 -->
<artifactId>async-http-client</artifactId>
- <version>1.9.31</version>
+ <version>1.9.33</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
- <version>1.7.13</version>
+ <version>1.7.18</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
@@ -164,7 +164,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.3</version>
+ <version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
diff --git a/setup/package.sh b/setup/package.sh
index 8014836bb..fa5fbd277 100755
--- a/setup/package.sh
+++ b/setup/package.sh
@@ -15,7 +15,6 @@ fi
VERSION=$1
check_requirement () {
- eval $1 &>/dev/null
if ! eval $1 &>/dev/null
then
echo $2
diff --git a/setup/unix/setup.sh b/setup/unix/setup.sh
index 902139a1a..478966ea0 100755
--- a/setup/unix/setup.sh
+++ b/setup/unix/setup.sh
@@ -2,13 +2,18 @@
UNIX_PATH="/opt/traccar"
-if [ $(java -version 2>&1 | grep -i version | sed 's/.*version "\(.*\)\.\(.*\)\..*"/\1\2/; 1q') -lt 17 ]
+if which java &>/dev/null
then
- echo "Please install Java version 7 or higher"
+ if [ $(java -version 2>&1 | grep -i version | sed 's/.*version "\(.*\)\.\(.*\)\..*"/\1\2/; 1q') -lt 17 ]
+ then
+ echo "Java 7 or higher required"
+ else
+ mkdir -p $UNIX_PATH
+ cp -rf * $UNIX_PATH
+ chmod -R go+rX $UNIX_PATH
+ $UNIX_PATH/bin/traccar install
+ rm $UNIX_PATH/setup.sh
+ fi
else
- mkdir -p $UNIX_PATH
- cp -rf * $UNIX_PATH
- chmod -R go+rX $UNIX_PATH
- $UNIX_PATH/bin/traccar install
- rm $UNIX_PATH/setup.sh
+ echo "Java runtime is required"
fi
diff --git a/setup/unix/traccar.xml b/setup/unix/traccar.xml
index 5eb64e7b0..c43fd25fd 100644
--- a/setup/unix/traccar.xml
+++ b/setup/unix/traccar.xml
@@ -24,7 +24,7 @@
<entry key='database.user'>sa</entry>
<entry key='database.password'></entry>
- <entry key='database.changelog'>/opt/traccar/data/db.changelog-master.xml</entry>
+ <entry key='database.changelog'>/opt/traccar/data/changelog-master.xml</entry>
<entry key='database.selectServers'>
SELECT * FROM server;
@@ -210,7 +210,7 @@
<entry key='eelink.port'>5064</entry>
<entry key='box.port'>5065</entry>
<entry key='freedom.port'>5066</entry>
- <entry key='telik.port'>5067</entry>
+ <entry key='telic.port'>5067</entry>
<entry key='trackbox.port'>5068</entry>
<entry key='visiontek.port'>5069</entry>
<entry key='orion.port'>5070</entry>
@@ -222,7 +222,7 @@
<entry key='xt013.port'>5076</entry>
<entry key='autofon.port'>5077</entry>
<entry key='gosafe.port'>5078</entry>
- <entry key='autofon45.port'>5079</entry>
+ <entry key='tt8850.port'>5079</entry>
<entry key='bce.port'>5080</entry>
<entry key='xirgo.port'>5081</entry>
<entry key='calamp.port'>5082</entry>
@@ -244,5 +244,9 @@
<entry key='thinkrace.port'>5098</entry>
<entry key='pathaway.port'>5099</entry>
<entry key='arnavi.port'>5100</entry>
+ <entry key='nvs.port'>5101</entry>
+ <entry key='kenji.port'>5102</entry>
+ <entry key='astra.port'>5103</entry>
+ <entry key='homtecs.port'>5104</entry>
</properties>
diff --git a/setup/windows/traccar.iss b/setup/windows/traccar.iss
index 298c27200..891cc47c3 100644
--- a/setup/windows/traccar.iss
+++ b/setup/windows/traccar.iss
@@ -83,6 +83,6 @@ begin
StringChangeEx(S, '[WEB]', ExpandConstant('{app}\web'), true);
StringChangeEx(S, '[LOG]', ExpandConstant('{app}\logs\tracker-server.log'), true);
StringChangeEx(S, '[DATABASE]', ExpandConstant('{app}\data\database'), true);
- StringChangeEx(S, '[CHANGELOG]', ExpandConstant('{app}\data\db.changelog-master.xml'), true);
+ StringChangeEx(S, '[CHANGELOG]', ExpandConstant('{app}\data\changelog-master.xml'), true);
SaveStringToFile(ExpandConstant(CurrentFileName), S, false);
end;
diff --git a/setup/windows/traccar.xml b/setup/windows/traccar.xml
index c1e675254..40b6fe20e 100644
--- a/setup/windows/traccar.xml
+++ b/setup/windows/traccar.xml
@@ -210,7 +210,7 @@
<entry key='eelink.port'>5064</entry>
<entry key='box.port'>5065</entry>
<entry key='freedom.port'>5066</entry>
- <entry key='telik.port'>5067</entry>
+ <entry key='telic.port'>5067</entry>
<entry key='trackbox.port'>5068</entry>
<entry key='visiontek.port'>5069</entry>
<entry key='orion.port'>5070</entry>
@@ -222,7 +222,7 @@
<entry key='xt013.port'>5076</entry>
<entry key='autofon.port'>5077</entry>
<entry key='gosafe.port'>5078</entry>
- <entry key='autofon45.port'>5079</entry>
+ <entry key='tt8850.port'>5079</entry>
<entry key='bce.port'>5080</entry>
<entry key='xirgo.port'>5081</entry>
<entry key='calamp.port'>5082</entry>
@@ -244,5 +244,9 @@
<entry key='thinkrace.port'>5098</entry>
<entry key='pathaway.port'>5099</entry>
<entry key='arnavi.port'>5100</entry>
+ <entry key='nvs.port'>5101</entry>
+ <entry key='kenji.port'>5102</entry>
+ <entry key='astra.port'>5103</entry>
+ <entry key='homtecs.port'>5104</entry>
</properties>
diff --git a/src/org/traccar/DistanceHandler.java b/src/org/traccar/DistanceHandler.java
index 1586d116b..c737457f6 100644
--- a/src/org/traccar/DistanceHandler.java
+++ b/src/org/traccar/DistanceHandler.java
@@ -1,5 +1,6 @@
/*
* Copyright 2015 Amila Silva
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,11 +41,13 @@ public class DistanceHandler extends BaseDataHandler {
distance = ((Number) last.getAttributes().get(Event.KEY_DISTANCE)).doubleValue();
}
- distance += DistanceCalculator.distance(
- position.getLatitude(), position.getLongitude(),
- last.getLatitude(), last.getLongitude());
+ if (position.getValid()) {
+ distance += DistanceCalculator.distance(
+ position.getLatitude(), position.getLongitude(),
+ last.getLatitude(), last.getLongitude());
- distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue();
+ distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue();
+ }
}
position.set(Event.KEY_DISTANCE, distance);
diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java
index f0ef36e5b..f99765de4 100644
--- a/src/org/traccar/MainEventHandler.java
+++ b/src/org/traccar/MainEventHandler.java
@@ -48,7 +48,10 @@ public class MainEventHandler extends IdleStateAwareChannelHandler {
s.append("course: ").append(String.format("%.1f", position.getCourse()));
Log.info(s.toString());
- Context.getConnectionManager().updatePosition(position);
+ Position lastPosition = Context.getConnectionManager().getLastPosition(position.getDeviceId());
+ if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) > 0) {
+ Context.getConnectionManager().updatePosition(position);
+ }
}
}
diff --git a/src/org/traccar/api/AsyncSocket.java b/src/org/traccar/api/AsyncSocket.java
index 7de182d7e..c289367ea 100644
--- a/src/org/traccar/api/AsyncSocket.java
+++ b/src/org/traccar/api/AsyncSocket.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
private Collection<Long> devices;
public AsyncSocket(long userId) {
- devices = Context.getPermissionsManager().allowedDevices(userId);
+ devices = Context.getPermissionsManager().getDevicePermissions(userId);
}
@Override
@@ -66,7 +66,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
}
private void sendData(String key, Collection<?> data) {
- if (!data.isEmpty()) {
+ if (!data.isEmpty() && isConnected()) {
JsonObjectBuilder json = Json.createObjectBuilder();
json.add(key, JsonConverter.arrayToJson(data));
getRemote().sendString(json.build().toString(), null);
diff --git a/src/org/traccar/api/AsyncSocketServlet.java b/src/org/traccar/api/AsyncSocketServlet.java
index fbfe248e5..ef6cef732 100644
--- a/src/org/traccar/api/AsyncSocketServlet.java
+++ b/src/org/traccar/api/AsyncSocketServlet.java
@@ -20,6 +20,7 @@ import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
+import org.traccar.Context;
import org.traccar.api.resource.SessionResource;
public class AsyncSocketServlet extends WebSocketServlet {
@@ -28,12 +29,16 @@ public class AsyncSocketServlet extends WebSocketServlet {
@Override
public void configure(WebSocketServletFactory factory) {
- factory.getPolicy().setIdleTimeout(ASYNC_TIMEOUT);
+ factory.getPolicy().setIdleTimeout(Context.getConfig().getLong("web.timeout", ASYNC_TIMEOUT));
factory.setCreator(new WebSocketCreator() {
@Override
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {
- long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY);
- return new AsyncSocket(userId);
+ if (req.getSession() != null) {
+ long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY);
+ return new AsyncSocket(userId);
+ } else {
+ return null;
+ }
}
});
}
diff --git a/src/org/traccar/api/CorsResponseFilter.java b/src/org/traccar/api/CorsResponseFilter.java
index 001f6ab4c..178d08812 100644
--- a/src/org/traccar/api/CorsResponseFilter.java
+++ b/src/org/traccar/api/CorsResponseFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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,6 @@ import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.traccar.Context;
import java.io.IOException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
@@ -37,7 +35,7 @@ public class CorsResponseFilter implements ContainerResponseFilter {
public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true";
public static final String ACCESS_CONTROL_ALLOW_METHODS_KEY = "Access-Control-Allow-Methods";
- public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE";
+ public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE, OPTIONS";
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
@@ -54,11 +52,11 @@ public class CorsResponseFilter implements ContainerResponseFilter {
if (!response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN_KEY)) {
String origin = request.getHeaderString(HttpHeaders.Names.ORIGIN);
String allowed = Context.getConfig().getString("web.origin");
- if (allowed == null) {
+
+ if (allowed == null || origin == null) {
response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, ACCESS_CONTROL_ALLOW_ORIGIN_VALUE);
- } else if (allowed.contains(origin)) {
- String originSafe = URLEncoder.encode(origin, StandardCharsets.UTF_8.name());
- response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, originSafe);
+ } else if (allowed.equals(ACCESS_CONTROL_ALLOW_ORIGIN_VALUE) || allowed.contains(origin)) {
+ response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, origin);
}
}
}
diff --git a/src/org/traccar/api/SecurityRequestFilter.java b/src/org/traccar/api/SecurityRequestFilter.java
index 20186b0cb..4c6137ede 100644
--- a/src/org/traccar/api/SecurityRequestFilter.java
+++ b/src/org/traccar/api/SecurityRequestFilter.java
@@ -55,6 +55,11 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
+
+ if (requestContext.getMethod().equals("OPTIONS")) {
+ throw new WebApplicationException(Response.status(Response.Status.OK).build());
+ }
+
SecurityContext securityContext = null;
String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER);
diff --git a/src/org/traccar/api/resource/PermissionResource.java b/src/org/traccar/api/resource/DevicePermissionResource.java
index 50deb77c2..f77375cba 100644
--- a/src/org/traccar/api/resource/PermissionResource.java
+++ b/src/org/traccar/api/resource/DevicePermissionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseResource;
-import org.traccar.model.Permission;
+import org.traccar.model.DevicePermission;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -28,13 +28,13 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.sql.SQLException;
-@Path("permissions")
+@Path("permissions/devices")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class PermissionResource extends BaseResource {
+public class DevicePermissionResource extends BaseResource {
@POST
- public Response add(Permission entity) throws SQLException {
+ public Response add(DevicePermission entity) throws SQLException {
Context.getPermissionsManager().checkAdmin(getUserId());
Context.getDataManager().linkDevice(entity.getUserId(), entity.getDeviceId());
Context.getPermissionsManager().refresh();
@@ -42,7 +42,7 @@ public class PermissionResource extends BaseResource {
}
@DELETE
- public Response remove(Permission entity) throws SQLException {
+ public Response remove(DevicePermission entity) throws SQLException {
Context.getPermissionsManager().checkAdmin(getUserId());
Context.getDataManager().unlinkDevice(entity.getUserId(), entity.getDeviceId());
Context.getPermissionsManager().refresh();
diff --git a/src/org/traccar/api/resource/GroupPermissionResource.java b/src/org/traccar/api/resource/GroupPermissionResource.java
new file mode 100644
index 000000000..b8ec4ae3c
--- /dev/null
+++ b/src/org/traccar/api/resource/GroupPermissionResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.api.resource;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.GroupPermission;
+
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.sql.SQLException;
+
+@Path("permissions/groups")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class GroupPermissionResource extends BaseResource {
+
+ @POST
+ public Response add(GroupPermission entity) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getDataManager().linkGroup(entity.getUserId(), entity.getGroupId());
+ Context.getPermissionsManager().refresh();
+ return Response.ok(entity).build();
+ }
+
+ @DELETE
+ public Response remove(GroupPermission entity) throws SQLException {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ Context.getDataManager().unlinkGroup(entity.getUserId(), entity.getGroupId());
+ Context.getPermissionsManager().refresh();
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/GroupResource.java b/src/org/traccar/api/resource/GroupResource.java
new file mode 100644
index 000000000..49f839499
--- /dev/null
+++ b/src/org/traccar/api/resource/GroupResource.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.api.resource;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+
+import javax.ws.rs.Consumes;
+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.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.sql.SQLException;
+import java.util.Collection;
+
+@Path("groups")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class GroupResource extends BaseResource {
+
+ @GET
+ public Collection<Group> get(
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
+ if (all) {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ return Context.getDataManager().getAllGroups();
+ } else {
+ if (userId == 0) {
+ userId = getUserId();
+ }
+ Context.getPermissionsManager().checkUser(getUserId(), userId);
+ return Context.getDataManager().getGroups(userId);
+ }
+ }
+
+ @POST
+ public Response add(Group entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getDataManager().addGroup(entity);
+ Context.getDataManager().linkGroup(getUserId(), entity.getId());
+ Context.getPermissionsManager().refresh();
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @PUT
+ public Response update(@PathParam("id") long id, Group entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkGroup(getUserId(), id);
+ Context.getDataManager().updateGroup(entity);
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkGroup(getUserId(), id);
+ Context.getDataManager().removeGroup(id);
+ Context.getPermissionsManager().refresh();
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/PositionResource.java b/src/org/traccar/api/resource/PositionResource.java
index ec6925b3a..9c174625c 100644
--- a/src/org/traccar/api/resource/PositionResource.java
+++ b/src/org/traccar/api/resource/PositionResource.java
@@ -38,9 +38,14 @@ public class PositionResource extends BaseResource {
public Collection<Position> get(
@QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
throws SQLException {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- return Context.getDataManager().getPositions(
- deviceId, JsonConverter.parseDate(from), JsonConverter.parseDate(to));
+ if (deviceId == 0) {
+ return Context.getConnectionManager().getInitialState(
+ Context.getPermissionsManager().getDevicePermissions(getUserId()));
+ } else {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ return Context.getDataManager().getPositions(
+ deviceId, JsonConverter.parseDate(from), JsonConverter.parseDate(to));
+ }
}
}
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index 73984952e..d2da4e701 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@@ -36,10 +37,13 @@ import liquibase.exception.LiquibaseException;
import liquibase.resource.FileSystemResourceAccessor;
import liquibase.resource.ResourceAccessor;
import org.traccar.Config;
+import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.GroupPermission;
import org.traccar.model.MiscFormatter;
-import org.traccar.model.Permission;
+import org.traccar.model.DevicePermission;
import org.traccar.model.Position;
import org.traccar.model.Server;
import org.traccar.model.User;
@@ -53,10 +57,14 @@ public class DataManager implements IdentityManager {
private DataSource dataSource;
+ private final long dataRefreshDelay;
+
private final Map<Long, Device> devicesById = new HashMap<>();
private final Map<String, Device> devicesByUniqueId = new HashMap<>();
private long devicesLastUpdate;
- private final long devicesRefreshDelay;
+
+ private final Map<Long, Group> groupsById = new HashMap<>();
+ private long groupsLastUpdate;
public DataManager(Config config) throws Exception {
this.config = config;
@@ -64,7 +72,7 @@ public class DataManager implements IdentityManager {
initDatabase();
initDatabaseSchema();
- devicesRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000;
+ dataRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000;
}
public DataSource getDataSource() {
@@ -112,7 +120,7 @@ public class DataManager implements IdentityManager {
}
private void updateDeviceCache(boolean force) throws SQLException {
- if (System.currentTimeMillis() - devicesLastUpdate > devicesRefreshDelay || force) {
+ if (System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay || force) {
devicesById.clear();
devicesByUniqueId.clear();
for (Device device : getAllDevices()) {
@@ -140,6 +148,25 @@ public class DataManager implements IdentityManager {
return devicesByUniqueId.get(uniqueId);
}
+ private void updateGroupCache(boolean force) throws SQLException {
+ if (System.currentTimeMillis() - groupsLastUpdate > dataRefreshDelay || force) {
+ groupsById.clear();
+ for (Group group : getAllGroups()) {
+ groupsById.put(group.getId(), group);
+ }
+ groupsLastUpdate = System.currentTimeMillis();
+ }
+ }
+
+ public Group getGroupById(long id) {
+ try {
+ updateGroupCache(!groupsById.containsKey(id));
+ } catch (SQLException e) {
+ Log.warning(e);
+ }
+ return groupsById.get(id);
+ }
+
private String getQuery(String key) {
String query = config.getString(key);
if (query == null) {
@@ -221,9 +248,14 @@ public class DataManager implements IdentityManager {
.executeUpdate();
}
- public Collection<Permission> getPermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.getPermissionsAll"))
- .executeQuery(Permission.class);
+ public Collection<DevicePermission> getDevicePermissions() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectDevicePermissions"))
+ .executeQuery(DevicePermission.class);
+ }
+
+ public Collection<GroupPermission> getGroupPermissions() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectGroupPermissions"))
+ .executeQuery(GroupPermission.class);
}
public Collection<Device> getAllDevices() throws SQLException {
@@ -232,9 +264,11 @@ public class DataManager implements IdentityManager {
}
public Collection<Device> getDevices(long userId) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectDevices"))
- .setLong("userId", userId)
- .executeQuery(Device.class);
+ Collection<Device> devices = new ArrayList<>();
+ for (long id : Context.getPermissionsManager().getDevicePermissions(userId)) {
+ devices.add(getDeviceById(id));
+ }
+ return devices;
}
public void addDevice(Device device) throws SQLException {
@@ -286,6 +320,51 @@ public class DataManager implements IdentityManager {
AsyncServlet.sessionRefreshUser(userId);
}
+ public Collection<Group> getAllGroups() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectGroupsAll"))
+ .executeQuery(Group.class);
+ }
+
+ public Collection<Group> getGroups(long userId) throws SQLException {
+ Collection<Group> groups = new ArrayList<>();
+ for (long id : Context.getPermissionsManager().getGroupPermissions(userId)) {
+ groups.add(getGroupById(id));
+ }
+ return groups;
+ }
+
+ public void addGroup(Group group) throws SQLException {
+ group.setId(QueryBuilder.create(dataSource, getQuery("database.insertGroup"), true)
+ .setObject(group)
+ .executeUpdate());
+ }
+
+ public void updateGroup(Group group) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.updateGroup"))
+ .setObject(group)
+ .executeUpdate();
+ }
+
+ public void removeGroup(long groupId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.deleteGroup"))
+ .setLong("id", groupId)
+ .executeUpdate();
+ }
+
+ public void linkGroup(long userId, long groupId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.linkGroup"))
+ .setLong("userId", userId)
+ .setLong("groupId", groupId)
+ .executeUpdate();
+ }
+
+ public void unlinkGroup(long userId, long groupId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.unlinkGroup"))
+ .setLong("userId", userId)
+ .setLong("groupId", groupId)
+ .executeUpdate();
+ }
+
public Collection<Position> getPositions(long deviceId, Date from, Date to) throws SQLException {
return QueryBuilder.create(dataSource, getQuery("database.selectPositions"))
.setLong("deviceId", deviceId)
diff --git a/src/org/traccar/database/GroupTree.java b/src/org/traccar/database/GroupTree.java
new file mode 100644
index 000000000..b383b1501
--- /dev/null
+++ b/src/org/traccar/database/GroupTree.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.database;
+
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class GroupTree {
+
+ private static class TreeNode {
+
+ private Group group;
+ private Device device;
+ private Collection<TreeNode> children = new HashSet<>();
+
+ public TreeNode(Group group) {
+ this.group = group;
+ }
+
+ public TreeNode(Device device) {
+ this.device = device;
+ }
+
+ @Override
+ public int hashCode() {
+ if (group != null) {
+ return (int) group.getId();
+ } else {
+ return (int) device.getId();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TreeNode)) {
+ return false;
+ }
+ TreeNode other = (TreeNode) obj;
+ if (other == this) {
+ return true;
+ }
+ if (group != null) {
+ if (other.group != null) {
+ return group.getId() == other.group.getId();
+ }
+ } else if (device != null) {
+ if (other.device != null) {
+ return device.getId() == other.device.getId();
+ }
+ }
+ return false;
+ }
+
+ public Group getGroup() {
+ return group;
+ }
+
+ public Device getDevice() {
+ return device;
+ }
+
+ public void setParent(TreeNode parent) {
+ if (parent != null) {
+ parent.children.add(this);
+ }
+ }
+
+ public Collection<TreeNode> getChildren() {
+ return children;
+ }
+
+ }
+
+ private final Map<Long, TreeNode> groupMap = new HashMap<>();
+
+ public GroupTree(Collection<Group> groups, Collection<Device> devices) {
+
+ for (Group group : groups) {
+ groupMap.put(group.getId(), new TreeNode(group));
+ }
+
+ for (TreeNode node : groupMap.values()) {
+ if (node.getGroup().getGroupId() != 0) {
+ node.setParent(groupMap.get(node.getGroup().getGroupId()));
+ }
+ }
+
+ Map<Long, TreeNode> deviceMap = new HashMap<>();
+
+ for (Device device : devices) {
+ deviceMap.put(device.getId(), new TreeNode(device));
+ }
+
+ for (TreeNode node : deviceMap.values()) {
+ if (node.getDevice().getGroupId() != 0) {
+ node.setParent(groupMap.get(node.getDevice().getGroupId()));
+ }
+ }
+
+ }
+
+ public Collection<Group> getGroups(long groupId) {
+ Set<TreeNode> results = new HashSet<>();
+ getNodes(results, groupMap.get(groupId));
+ Collection<Group> groups = new ArrayList<>();
+ for (TreeNode node : results) {
+ if (node.getGroup() != null) {
+ groups.add(node.getGroup());
+ }
+ }
+ return groups;
+ }
+
+ public Collection<Device> getDevices(long groupId) {
+ Set<TreeNode> results = new HashSet<>();
+ getNodes(results, groupMap.get(groupId));
+ Collection<Device> devices = new ArrayList<>();
+ for (TreeNode node : results) {
+ if (node.getDevice() != null) {
+ devices.add(node.getDevice());
+ }
+ }
+ return devices;
+ }
+
+ private void getNodes(Set<TreeNode> results, TreeNode node) {
+ for (TreeNode child : node.getChildren()) {
+ results.add(child);
+ getNodes(results, child);
+ }
+ }
+
+}
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index c676dea23..dc37bbc84 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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,15 @@
package org.traccar.database;
import java.sql.SQLException;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.traccar.helper.Log;
-import org.traccar.model.Permission;
+import org.traccar.model.Device;
+import org.traccar.model.DevicePermission;
+import org.traccar.model.Group;
+import org.traccar.model.GroupPermission;
import org.traccar.model.Server;
import org.traccar.model.User;
@@ -34,13 +36,21 @@ public class PermissionsManager {
private final Map<Long, User> users = new HashMap<>();
- private final Map<Long, Set<Long>> permissions = new HashMap<>();
+ private final Map<Long, Set<Long>> groupPermissions = new HashMap<>();
+ private final Map<Long, Set<Long>> devicePermissions = new HashMap<>();
- private Set<Long> getPermissions(long userId) {
- if (!permissions.containsKey(userId)) {
- permissions.put(userId, new HashSet<Long>());
+ public Set<Long> getGroupPermissions(long userId) {
+ if (!groupPermissions.containsKey(userId)) {
+ groupPermissions.put(userId, new HashSet<Long>());
}
- return permissions.get(userId);
+ return groupPermissions.get(userId);
+ }
+
+ public Set<Long> getDevicePermissions(long userId) {
+ if (!devicePermissions.containsKey(userId)) {
+ devicePermissions.put(userId, new HashSet<Long>());
+ }
+ return devicePermissions.get(userId);
}
public PermissionsManager(DataManager dataManager) {
@@ -50,15 +60,31 @@ public class PermissionsManager {
public final void refresh() {
users.clear();
- permissions.clear();
+ groupPermissions.clear();
+ devicePermissions.clear();
try {
server = dataManager.getServer();
for (User user : dataManager.getUsers()) {
users.put(user.getId(), user);
}
- for (Permission permission : dataManager.getPermissions()) {
- getPermissions(permission.getUserId()).add(permission.getDeviceId());
+
+ GroupTree groupTree = new GroupTree(dataManager.getAllGroups(), dataManager.getAllDevices());
+ for (GroupPermission permission : dataManager.getGroupPermissions()) {
+ Set<Long> userGroupPermissions = getGroupPermissions(permission.getUserId());
+ Set<Long> userDevicePermissions = getDevicePermissions(permission.getUserId());
+ userGroupPermissions.add(permission.getGroupId());
+ for (Group group : groupTree.getGroups(permission.getGroupId())) {
+ userGroupPermissions.add(group.getId());
+ }
+ for (Device device : groupTree.getDevices(permission.getGroupId())) {
+ userDevicePermissions.add(device.getId());
+ }
+ }
+
+ for (DevicePermission permission : dataManager.getDevicePermissions()) {
+ getDevicePermissions(permission.getUserId()).add(permission.getDeviceId());
}
+
} catch (SQLException error) {
Log.warning(error);
}
@@ -80,12 +106,14 @@ public class PermissionsManager {
}
}
- public Collection<Long> allowedDevices(long userId) {
- return getPermissions(userId);
+ public void checkGroup(long userId, long groupId) throws SecurityException {
+ if (!getGroupPermissions(userId).contains(groupId)) {
+ throw new SecurityException("Group access denied");
+ }
}
public void checkDevice(long userId, long deviceId) throws SecurityException {
- if (!getPermissions(userId).contains(deviceId)) {
+ if (!getDevicePermissions(userId).contains(deviceId)) {
throw new SecurityException("Device access denied");
}
}
diff --git a/src/org/traccar/helper/Checksum.java b/src/org/traccar/helper/Checksum.java
index e6b6cd2c8..c156e53f7 100644
--- a/src/org/traccar/helper/Checksum.java
+++ b/src/org/traccar/helper/Checksum.java
@@ -158,13 +158,20 @@ public final class Checksum {
public static String nmea(String msg) {
int checksum = 0;
- byte[] bytes = msg.getBytes(Charset.defaultCharset());
- for (int i = 1; i < msg.length(); i++) {
- checksum ^= bytes[i];
+ for (byte b : msg.getBytes(Charset.defaultCharset())) {
+ checksum ^= b;
}
return String.format("*%02x", checksum).toUpperCase();
}
+ public static String sum(String msg) {
+ byte checksum = 0;
+ for (byte b : msg.getBytes(Charset.defaultCharset())) {
+ checksum += b;
+ }
+ return String.format("%02X", checksum).toUpperCase();
+ }
+
public static long luhn(long imei) {
long checksum = 0;
long remain = imei;
diff --git a/src/org/traccar/helper/ObdDecoder.java b/src/org/traccar/helper/ObdDecoder.java
index 35fa4dc07..c2d9b1841 100644
--- a/src/org/traccar/helper/ObdDecoder.java
+++ b/src/org/traccar/helper/ObdDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,8 +25,9 @@ public final class ObdDecoder {
private ObdDecoder() {
}
- private static final int MODE_CURRENT = 0x01;
- private static final int MODE_FREEZE_FRAME = 0x02;
+ public static final int MODE_CURRENT = 0x01;
+ public static final int MODE_FREEZE_FRAME = 0x02;
+ public static final int MODE_CODES = 0x03;
private static final int PID_ENGINE_LOAD = 0x04;
private static final int PID_COOLANT_TEMPERATURE = 0x05;
@@ -37,11 +38,13 @@ public final class ObdDecoder {
private static final int PID_FUEL_LEVEL = 0x2F;
private static final int PID_DISTANCE_CLEARED = 0x31;
- public static Map.Entry<String, Object> decode(int mode, int pid, String value) {
+ public static Map.Entry<String, Object> decode(int mode, String value) {
switch (mode) {
case MODE_CURRENT:
case MODE_FREEZE_FRAME:
- return decodeData(pid, value);
+ return decodeData(Integer.parseInt(value.substring(0, 2), 16), value.substring(2));
+ case MODE_CODES:
+ return decodeCodes(value);
default:
return null;
}
@@ -51,6 +54,30 @@ public final class ObdDecoder {
return new AbstractMap.SimpleEntry<>(key, value);
}
+ private static Map.Entry<String, Object> decodeCodes(String value) {
+ 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));
+ }
+ return createEntry("dtcs", codes.toString().replaceFirst(",", ""));
+ }
+
private static Map.Entry<String, Object> decodeData(int pid, String value) {
int intValue = Integer.parseInt(value, 16);
switch (pid) {
@@ -69,7 +96,7 @@ public final class ObdDecoder {
case PID_FUEL_LEVEL:
return createEntry(Event.KEY_FUEL, intValue * 100 / 255);
case PID_DISTANCE_CLEARED:
- return createEntry(Event.KEY_FUEL, intValue);
+ return createEntry("cleared-distance", intValue);
default:
return null;
}
diff --git a/src/org/traccar/helper/Parser.java b/src/org/traccar/helper/Parser.java
index a3f118df0..dcea1c8f7 100644
--- a/src/org/traccar/helper/Parser.java
+++ b/src/org/traccar/helper/Parser.java
@@ -37,12 +37,17 @@ public class Parser {
return matcher.find();
}
+ public void skip(int number) {
+ position += number;
+ }
+
public boolean hasNext() {
return hasNext(1);
}
public boolean hasNext(int number) {
- if (matcher.group(position) != null) {
+ String value = matcher.group(position);
+ if (value != null && !value.isEmpty()) {
return true;
} else {
position += number;
diff --git a/src/org/traccar/model/Device.java b/src/org/traccar/model/Device.java
index 7ca9fc843..934e753b1 100644
--- a/src/org/traccar/model/Device.java
+++ b/src/org/traccar/model/Device.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,4 +91,14 @@ public class Device {
this.positionId = positionId;
}
+ private long groupId;
+
+ public long getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(long groupId) {
+ this.groupId = groupId;
+ }
+
}
diff --git a/src/org/traccar/model/Permission.java b/src/org/traccar/model/DevicePermission.java
index 393de2359..b3bc0cae0 100644
--- a/src/org/traccar/model/Permission.java
+++ b/src/org/traccar/model/DevicePermission.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
package org.traccar.model;
-public class Permission {
+public class DevicePermission {
private long userId;
diff --git a/src/org/traccar/model/Event.java b/src/org/traccar/model/Event.java
index a9f6f9204..221536530 100644
--- a/src/org/traccar/model/Event.java
+++ b/src/org/traccar/model/Event.java
@@ -29,6 +29,7 @@ public abstract class Event extends Extensible {
public static final String KEY_ALARM = "alarm";
public static final String KEY_STATUS = "status";
public static final String KEY_ODOMETER = "odometer";
+ public static final String KEY_HOURS = "hours";
public static final String KEY_INPUT = "input";
public static final String KEY_OUTPUT = "output";
public static final String KEY_POWER = "power";
diff --git a/src/org/traccar/model/Group.java b/src/org/traccar/model/Group.java
new file mode 100644
index 000000000..00f2b2cfc
--- /dev/null
+++ b/src/org/traccar/model/Group.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.model;
+
+public class Group {
+
+ private long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ private long groupId;
+
+ public long getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(long groupId) {
+ this.groupId = groupId;
+ }
+
+}
diff --git a/src/org/traccar/model/GroupPermission.java b/src/org/traccar/model/GroupPermission.java
new file mode 100644
index 000000000..9b0011575
--- /dev/null
+++ b/src/org/traccar/model/GroupPermission.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.model;
+
+public class GroupPermission {
+
+ private long userId;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ private long groupId;
+
+ public long getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(long groupId) {
+ this.groupId = groupId;
+ }
+
+}
diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java
index 6ae6aa09f..a3d34cf3c 100644
--- a/src/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/org/traccar/protocol/AplicomProtocolDecoder.java
@@ -288,7 +288,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0200) != 0) {
- buf.skipBytes(6); // button
+ position.set(Event.KEY_RFID, (((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt());
}
if ((selector & 0x0400) != 0) {
diff --git a/src/org/traccar/protocol/AstraProtocol.java b/src/org/traccar/protocol/AstraProtocol.java
new file mode 100644
index 000000000..d0b1d47a5
--- /dev/null
+++ b/src/org/traccar/protocol/AstraProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class AstraProtocol extends BaseProtocol {
+
+ public AstraProtocol() {
+ super("astra");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 1, 2, -3, 0));
+ pipeline.addLast("objectDecoder", new AstraProtocolDecoder(AstraProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/AstraProtocolDecoder.java b/src/org/traccar/protocol/AstraProtocolDecoder.java
new file mode 100644
index 000000000..8cfec95fe
--- /dev/null
+++ b/src/org/traccar/protocol/AstraProtocolDecoder.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.Charset;
+import java.util.LinkedList;
+import java.util.List;
+
+public class AstraProtocolDecoder extends BaseProtocolDecoder {
+
+ public AstraProtocolDecoder(AstraProtocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_HEARTBEAT = 0x1A;
+ public static final int MSG_DATA = 0x10;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ if (channel != null) {
+ channel.write(ChannelBuffers.wrappedBuffer(new byte[] {0x06}), remoteAddress);
+ }
+
+ buf.readUnsignedByte(); // protocol
+ buf.readUnsignedShort(); // length
+
+ String imei = String.format("%08d", buf.readUnsignedInt()) + String.format("%07d", buf.readUnsignedMedium());
+ if (!identify(imei, channel, remoteAddress)) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.readableBytes() > 2) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(getDeviceId());
+
+ buf.readUnsignedByte(); // index
+
+ position.setLatitude(buf.readInt() * 0.000001);
+ position.setLongitude(buf.readInt() * 0.000001);
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(1980, 1, 6).addMillis(buf.readUnsignedInt() * 1000L);
+ position.setTime(dateBuilder.getDate());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte() * 2));
+ position.setCourse(buf.readUnsignedByte() * 2);
+
+ int reason = buf.readUnsignedMedium();
+ buf.readUnsignedShort(); // status
+
+ position.set(Event.PREFIX_IO + 1, buf.readUnsignedByte());
+ position.set(Event.PREFIX_ADC + 1, buf.readUnsignedByte());
+ position.set(Event.KEY_BATTERY, buf.readUnsignedByte());
+ position.set(Event.KEY_POWER, buf.readUnsignedByte());
+
+ buf.readUnsignedByte(); // max journey speed
+ buf.skipBytes(6); // accelerometer
+ buf.readUnsignedShort(); // journey distance
+ buf.readUnsignedShort(); // journey idle time
+
+ position.setAltitude(buf.readUnsignedByte() * 20);
+
+ int quality = buf.readUnsignedByte();
+ position.set(Event.KEY_SATELLITES, quality & 0xf);
+ position.set(Event.KEY_GSM, quality >> 4);
+
+ buf.readUnsignedByte(); // geofence events
+
+ if (BitUtil.check(reason, 6) || BitUtil.check(reason, 7)) {
+
+ position.set(Event.KEY_RFID, buf.readBytes(7).toString(Charset.defaultCharset()));
+ position.set(Event.KEY_ODOMETER, buf.readUnsignedMedium());
+
+ buf.readUnsignedShort(); // engine time
+
+ }
+
+ // extra data
+
+ positions.add(position);
+
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/org/traccar/protocol/BoxProtocol.java b/src/org/traccar/protocol/BoxProtocol.java
index ae8207002..33cfca282 100644
--- a/src/org/traccar/protocol/BoxProtocol.java
+++ b/src/org/traccar/protocol/BoxProtocol.java
@@ -37,8 +37,8 @@ public class BoxProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new BoxProtocolDecoder(BoxProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/CarscopProtocol.java b/src/org/traccar/protocol/CarscopProtocol.java
index 1ac3e5e9f..4432fb87a 100644
--- a/src/org/traccar/protocol/CarscopProtocol.java
+++ b/src/org/traccar/protocol/CarscopProtocol.java
@@ -37,8 +37,8 @@ public class CarscopProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '^'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new CarscopProtocolDecoder(CarscopProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java
index 3179cbd78..9f332dd04 100644
--- a/src/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/org/traccar/protocol/CastelProtocolDecoder.java
@@ -109,6 +109,28 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, ChannelBuffer id, short type) {
+
+ if (channel != null) {
+ int length = 2 + 2 + id.readableBytes() + 2 + 4 + 8 + 2 + 2;
+
+ ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length);
+ response.writeByte('@'); response.writeByte('@');
+ response.writeShort(length);
+ response.writeBytes(id);
+ response.writeShort(ChannelBuffers.swapShort(type));
+ response.writeInt(0);
+ for (int i = 0; i < 8; i++) {
+ response.writeByte(0xff);
+ }
+ response.writeShort(
+ Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex())));
+ response.writeByte(0x0D); response.writeByte(0x0A);
+ channel.write(response, remoteAddress);
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -134,23 +156,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
if (type == 0x2001) {
- if (channel != null) {
- int length = 2 + 2 + id.readableBytes() + 2 + 4 + 8 + 2 + 2;
-
- ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length);
- response.writeByte('@'); response.writeByte('@');
- response.writeShort(length);
- response.writeBytes(id);
- response.writeShort(ChannelBuffers.swapShort((short) 0x1001));
- response.writeInt(0);
- for (int i = 0; i < 8; i++) {
- response.writeByte(0xff);
- }
- response.writeShort(
- Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex())));
- response.writeByte(0x0D); response.writeByte(0x0A);
- channel.write(response, remoteAddress);
- }
+ sendResponse(channel, remoteAddress, id, (short) 0x1001);
buf.readUnsignedInt(); // index
buf.readUnsignedInt(); // unix time
diff --git a/src/org/traccar/protocol/CityeasyProtocol.java b/src/org/traccar/protocol/CityeasyProtocol.java
index 1049e48eb..82302d2d7 100644
--- a/src/org/traccar/protocol/CityeasyProtocol.java
+++ b/src/org/traccar/protocol/CityeasyProtocol.java
@@ -41,8 +41,8 @@ public class CityeasyProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0));
- pipeline.addLast("objectDecoder", new CityeasyProtocolDecoder(CityeasyProtocol.this));
pipeline.addLast("objectEncoder", new CityeasyProtocolEncoder());
+ pipeline.addLast("objectDecoder", new CityeasyProtocolDecoder(CityeasyProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/FlextrackProtocol.java b/src/org/traccar/protocol/FlextrackProtocol.java
index 0d1b13d8a..3b1aaa3d8 100644
--- a/src/org/traccar/protocol/FlextrackProtocol.java
+++ b/src/org/traccar/protocol/FlextrackProtocol.java
@@ -36,8 +36,8 @@ public class FlextrackProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r"));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new FlextrackProtocolDecoder(FlextrackProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/Gl100Protocol.java b/src/org/traccar/protocol/Gl100Protocol.java
index cc2c826ab..f51834ea2 100644
--- a/src/org/traccar/protocol/Gl100Protocol.java
+++ b/src/org/traccar/protocol/Gl100Protocol.java
@@ -38,16 +38,16 @@ public class Gl100Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\0'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Gl100ProtocolDecoder(Gl100Protocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Gl100ProtocolDecoder(Gl100Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/Gl200Protocol.java b/src/org/traccar/protocol/Gl200Protocol.java
index 3aa69567d..701652e12 100644
--- a/src/org/traccar/protocol/Gl200Protocol.java
+++ b/src/org/traccar/protocol/Gl200Protocol.java
@@ -44,19 +44,19 @@ public class Gl200Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "$", "\0"));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
index e4bf43979..574f9a8c3 100644
--- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -16,10 +16,13 @@
package org.traccar.protocol;
import java.net.SocketAddress;
+import java.util.LinkedList;
import java.util.regex.Pattern;
+
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -33,7 +36,7 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
- private static final Pattern PATTERN_HEARTBEAT = new PatternBuilder()
+ private static final Pattern PATTERN_HBD = new PatternBuilder()
.text("+ACK:GTHBD,")
.number("([0-9A-Z]{2}xxxx),")
.any().text(",")
@@ -68,115 +71,204 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
.text("$").optional()
.compile();
- private static final Pattern PATTERN = new PatternBuilder()
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(?:d{1,2})?,") // gps accuracy
+ .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
+ .number("(dd)(dd)(dd)").optional(2) // time
+ .text(",")
.groupBegin()
- .text("+").expression("(?:RESP|BUFF)").text(":")
+ .number("(0ddd)?,") // mcc
+ .number("(0ddd)?,") // mnc
+ .number("(xxxx)?,") // lac
+ .number("(xxxx)?,") // cell
.or()
- .binary("00?04,")
- .number("xxxx,")
- .expression("[01],")
+ .number("(d+)?,") // mcc
+ .number("(d+)?,") // mnc
+ .number("(d+)?,") // lac
+ .number("(d+)?,") // cell
.groupEnd()
- .expression("GT...,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .expression("([^,]+),") // imei
+ .number("(?:d+|(d+.d))?,") // odometer
+ .compile();
- .groupBegin()
- .expression("[0-9A-Z]{17},") // vin
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("+RESP:GTOBD,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}),") // 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
+ .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{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("(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})?,") // throttle
+ .number("(?:d{1,3})?,") // engine load
.number("(d{1,3})?,") // fuel level
- .number("(d+)") // odometer
- .or().any()
- .groupEnd().text(",")
-
- .number("(d{1,2})?,") // gps accuracy
- .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("(d+),") // odometer
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{1,7}.d)?,") // odometer
.number("(dddd)(dd)(dd)") // date
.number("(dd)(dd)(dd)").optional(2) // time
.text(",")
- .number("(0ddd)?,") // mcc
- .number("(0ddd)?,") // mnc
- .number("(?:xxxx)?")
- .number("(xxxx)").optional(2) // lac
- .text(",")
- .number("(xxxx)?,") // cell
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_FRI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTFRI,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(d+)?,") // power
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
.groupBegin()
- .number("(d+.d)?,") // odometer
+ .number("(d{1,7}.d)?,").optional() // odometer
.number("(d{1,3})?,") // battery
- .groupEnd("?")
+ .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
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date
+ .number("(dd)(dd)(dd)").optional(2) // time
+ .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}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d*,")
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression(PATTERN_LOCATION.pattern())
.groupBegin()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .or()
+ .number("(d{1,7}.d)?,") // odometer
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date
+ .number("(dd)(dd)(dd)").optional(2) // time
+ .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)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
.any()
+ .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
.number("(dd)(dd)(dd)") // time
- .or()
+ .text(",")
.any()
- .groupEnd()
- .number(",(xxxx)")
+ .number("(xxxx)") // count number
.text("$").optional()
.compile();
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ private Object decodeHbd(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_HBD, sentence);
+ if (parser.matches() && channel != null) {
+ channel.write("+SACK:GTHBD," + parser.next() + "," + parser.next() + "$", remoteAddress);
+ }
+ return null;
+ }
- String sentence = (String) msg;
+ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_INF, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
- Parser parser = new Parser(PATTERN_HEARTBEAT, sentence);
- if (parser.matches()) {
- if (channel != null) {
- channel.write("+SACK:GTHBD," + parser.next() + "," + parser.next() + "$", remoteAddress);
- }
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
return null;
}
+ position.setDeviceId(getDeviceId());
- parser = new Parser(PATTERN_INF, sentence);
- if (parser.matches()) {
+ position.set(Event.KEY_STATUS, parser.next());
+ position.set(Event.KEY_POWER, parser.next());
+ position.set(Event.KEY_BATTERY, parser.next());
+ position.set(Event.KEY_CHARGE, parser.next());
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
- if (!identify(parser.next(), channel, remoteAddress)) {
- return null;
- }
- position.setDeviceId(getDeviceId());
+ getLastLocation(position, dateBuilder.getDate());
+
+ position.set(Event.KEY_INDEX, parser.next());
- position.set(Event.KEY_STATUS, parser.next());
- position.set(Event.KEY_POWER, parser.next());
- position.set(Event.KEY_BATTERY, parser.next());
- position.set(Event.KEY_CHARGE, parser.next());
+ return position;
+ }
+
+ private void decodeLocation(Position position, Parser parser) {
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+
+ if (parser.hasNext(8)) {
+ position.setValid(true);
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
DateBuilder dateBuilder = new DateBuilder()
.setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+ } else {
+ getLastLocation(position, null);
+ }
- getLastLocation(position, dateBuilder.getDate());
+ if (parser.hasNext(4)) {
+ position.set(Event.KEY_MCC, parser.nextInt());
+ position.set(Event.KEY_MNC, parser.nextInt());
+ position.set(Event.KEY_LAC, parser.nextInt(16));
+ position.set(Event.KEY_CID, parser.nextInt(16));
+ }
- position.set(Event.KEY_INDEX, parser.next());
+ parser.skip(4); // alternative networks
- return position;
- }
+ position.set(Event.KEY_ODOMETER, parser.next());
+ }
- parser = new Parser(PATTERN, sentence);
+ private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_OBD, sentence);
if (!parser.matches()) {
return null;
}
@@ -189,12 +281,6 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(getDeviceId());
- // RFID
- if (sentence.startsWith("+RESP:GTIDA")) {
- position.set(Event.KEY_RFID, sentence.split(",")[5]);
- }
-
- // OBD
position.set(Event.KEY_RPM, parser.next());
position.set(Event.KEY_OBD_SPEED, parser.next());
position.set(Event.PREFIX_TEMP + 1, parser.next());
@@ -207,32 +293,111 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_FUEL, parser.next());
position.set(Event.KEY_OBD_ODOMETER, parser.next());
- if (parser.hasNext(12)) {
- position.setValid(parser.nextInt() < 20);
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
- position.setCourse(parser.nextDouble());
- position.setAltitude(parser.nextDouble());
- position.setLongitude(parser.nextDouble());
- position.setLatitude(parser.nextDouble());
+ decodeLocation(position, parser);
+ position.set(Event.KEY_ODOMETER, parser.next());
+
+ if (parser.hasNext(6)) {
DateBuilder dateBuilder = new DateBuilder()
.setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setTime(dateBuilder.getDate());
- } else {
- getLastLocation(position, null);
+ if (!position.getOutdated() && position.getFixTime().after(dateBuilder.getDate())) {
+ position.setTime(dateBuilder.getDate());
+ }
}
- if (parser.hasNext(4)) {
- position.set(Event.KEY_MCC, parser.nextInt());
- position.set(Event.KEY_MNC, parser.nextInt());
- position.set(Event.KEY_LAC, parser.nextInt(16));
- position.set(Event.KEY_CID, parser.nextInt(16));
+ return position;
+ }
+
+ private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_FRI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+
+ int power = parser.nextInt();
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(getDeviceId());
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ decodeLocation(position, parser);
+
+ // power value only on some devices
+ if (power > 10) {
+ position.set(Event.KEY_POWER, power);
}
position.set(Event.KEY_ODOMETER, parser.next());
position.set(Event.KEY_BATTERY, parser.next());
+ position.set(Event.KEY_ODOMETER, parser.next());
+ position.set(Event.KEY_HOURS, parser.next());
+ position.set(Event.PREFIX_ADC + 1, parser.next());
+ position.set(Event.PREFIX_ADC + 2, parser.next());
+ position.set(Event.KEY_BATTERY, parser.next());
+
+ if (parser.hasNext(3)) {
+ int ignition = parser.nextInt(16);
+ if (BitUtil.check(ignition, 4)) {
+ position.set(Event.KEY_IGNITION, false);
+ } else if (BitUtil.check(ignition, 5)) {
+ position.set(Event.KEY_IGNITION, true);
+ }
+ position.set(Event.KEY_INPUT, parser.nextInt(16));
+ position.set(Event.KEY_OUTPUT, parser.nextInt(16));
+ }
+
+ // workaround for wrong location time
+ if (parser.hasNext(6)) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ if (!position.getOutdated() && position.getFixTime().after(dateBuilder.getDate())) {
+ position.setTime(dateBuilder.getDate());
+ }
+ }
+
+ return positions;
+ }
+
+ private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ decodeLocation(position, parser);
+
+ position.set(Event.KEY_ODOMETER, parser.next());
+ position.set(Event.KEY_BATTERY, parser.next());
+
+ position.set(Event.KEY_ODOMETER, parser.next());
+
+ // workaround for wrong location time
if (parser.hasNext(6)) {
DateBuilder dateBuilder = new DateBuilder()
.setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
@@ -249,4 +414,71 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_BASIC, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+
+ position.setValid(true);
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ int typeIndex = sentence.indexOf(":GT");
+ if (typeIndex < 0) {
+ return null;
+ }
+
+ Object result;
+ switch (sentence.substring(typeIndex + 3, typeIndex + 6)) {
+ case "HBD":
+ result = decodeHbd(channel, remoteAddress, sentence);
+ break;
+ case "INF":
+ result = decodeInf(channel, remoteAddress, sentence);
+ break;
+ case "OBD":
+ result = decodeObd(channel, remoteAddress, sentence);
+ break;
+ case "FRI":
+ result = decodeFri(channel, remoteAddress, sentence);
+ break;
+ default:
+ result = decodeOther(channel, remoteAddress, sentence);
+ break;
+ }
+
+ if (result == null) {
+ result = decodeBasic(channel, remoteAddress, sentence);
+ }
+
+ return result;
+ }
+
}
diff --git a/src/org/traccar/protocol/GlobalSatProtocol.java b/src/org/traccar/protocol/GlobalSatProtocol.java
index 10db8e313..592ba152e 100644
--- a/src/org/traccar/protocol/GlobalSatProtocol.java
+++ b/src/org/traccar/protocol/GlobalSatProtocol.java
@@ -37,8 +37,8 @@ public class GlobalSatProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '!'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new GlobalSatProtocolDecoder(GlobalSatProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/GoSafeProtocol.java b/src/org/traccar/protocol/GoSafeProtocol.java
index af2c2858d..bc1ed14de 100644
--- a/src/org/traccar/protocol/GoSafeProtocol.java
+++ b/src/org/traccar/protocol/GoSafeProtocol.java
@@ -36,8 +36,8 @@ public class GoSafeProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new GoSafeProtocolDecoder(GoSafeProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/Gps103Protocol.java b/src/org/traccar/protocol/Gps103Protocol.java
index 6ddc2dea1..d8ad0a8b7 100644
--- a/src/org/traccar/protocol/Gps103Protocol.java
+++ b/src/org/traccar/protocol/Gps103Protocol.java
@@ -47,19 +47,19 @@ public class Gps103Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "\r\n", "\n", ";"));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this));
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gps103ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this));
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gps103ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new Gps103ProtocolDecoder(Gps103Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
index a45e20ba2..e2aed6309 100644
--- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -118,11 +118,16 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
}
// Send response #2
- if (sentence.length() == 15 && Character.isDigit(sentence.charAt(0))) {
+ if (Character.isDigit(sentence.charAt(0))) {
if (channel != null) {
channel.write("ON", remoteAddress);
}
- return null;
+ int start = sentence.indexOf("imei:");
+ if (start >= 0) {
+ sentence = sentence.substring(start);
+ } else {
+ return null;
+ }
}
Position position = new Position();
diff --git a/src/org/traccar/protocol/GpsGateProtocol.java b/src/org/traccar/protocol/GpsGateProtocol.java
index 9f07550e4..c654810b1 100644
--- a/src/org/traccar/protocol/GpsGateProtocol.java
+++ b/src/org/traccar/protocol/GpsGateProtocol.java
@@ -37,8 +37,8 @@ public class GpsGateProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new GpsGateProtocolDecoder(GpsGateProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/org/traccar/protocol/GpsGateProtocolDecoder.java
index 421dc5304..b48fa6266 100644
--- a/src/org/traccar/protocol/GpsGateProtocolDecoder.java
+++ b/src/org/traccar/protocol/GpsGateProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2013 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.traccar.protocol;
import java.net.SocketAddress;
import java.util.regex.Pattern;
+
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.helper.Checksum;
@@ -31,7 +32,7 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
+ private static final Pattern PATTERN_GPRMC = new PatternBuilder()
.text("$GPRMC,")
.number("(dd)(dd)(dd).?(d+)?,") // time
.expression("([AV]),") // validity
@@ -45,6 +46,24 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_FRCMD = new PatternBuilder()
+ .text("$FRCMD,")
+ .number("(d+),") // imei
+ .expression("[^,]*,") // command
+ .expression("[^,]*,")
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.?d*),") // altitude
+ .number("(d+.?d*),") // speed
+ .number("(d+.?d*),") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd).?(d+)?,") // time
+ .expression("([01]),") // validity
+ .any()
+ .compile();
+
private void send(Channel channel, String message) {
if (channel != null) {
channel.write(message + Checksum.nmea(message) + "\r\n");
@@ -85,10 +104,10 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
// Version check
send(channel, "$FRVER,1,0,GpsGate Server 1.0");
- } else if (sentence.startsWith("$GPRMC,") && hasDeviceId()) {
+ } else if (sentence.startsWith("$GPRMC,")) {
- Parser parser = new Parser(PATTERN, sentence);
- if (!parser.matches()) {
+ Parser parser = new Parser(PATTERN_GPRMC, sentence);
+ if (!parser.matches() || !hasDeviceId()) {
return null;
}
@@ -109,6 +128,37 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
position.setTime(dateBuilder.getDate());
return position;
+
+ } else if (sentence.startsWith("$FRCMD,")) {
+
+ Parser parser = new Parser(PATTERN_FRCMD, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setAltitude(parser.nextDouble());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.setValid(parser.next().equals("1"));
+
+ return position;
+
}
return null;
diff --git a/src/org/traccar/protocol/GpsmtaProtocol.java b/src/org/traccar/protocol/GpsmtaProtocol.java
index fe586644a..d1eaa2fd3 100644
--- a/src/org/traccar/protocol/GpsmtaProtocol.java
+++ b/src/org/traccar/protocol/GpsmtaProtocol.java
@@ -35,8 +35,8 @@ public class GpsmtaProtocol extends BaseProtocol {
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new GpsmtaProtocolDecoder(GpsmtaProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/Gt06FrameDecoder.java b/src/org/traccar/protocol/Gt06FrameDecoder.java
index ed0d8d548..67f42efb4 100644
--- a/src/org/traccar/protocol/Gt06FrameDecoder.java
+++ b/src/org/traccar/protocol/Gt06FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2014 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,9 +24,7 @@ public class Gt06FrameDecoder extends FrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
// Check minimum length
if (buf.readableBytes() < 5) {
@@ -37,6 +35,12 @@ public class Gt06FrameDecoder extends FrameDecoder {
if (buf.getByte(buf.readerIndex()) == 0x78) {
length += 1 + buf.getUnsignedByte(buf.readerIndex() + 2);
+
+ int type = buf.getUnsignedByte(buf.readerIndex() + 3);
+ if (type == Gt06ProtocolDecoder.MSG_STATUS && length == 13) {
+ length += 2; // workaround for #1727
+ }
+
} else {
length += 2 + buf.getUnsignedShort(buf.readerIndex() + 2);
}
diff --git a/src/org/traccar/protocol/Gt06Protocol.java b/src/org/traccar/protocol/Gt06Protocol.java
index eac9ad71a..4630342a5 100644
--- a/src/org/traccar/protocol/Gt06Protocol.java
+++ b/src/org/traccar/protocol/Gt06Protocol.java
@@ -37,8 +37,8 @@ public class Gt06Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new Gt06FrameDecoder());
- pipeline.addLast("objectDecoder", new Gt06ProtocolDecoder(Gt06Protocol.this));
pipeline.addLast("objectEncoder", new Gt06ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new Gt06ProtocolDecoder(Gt06Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/HomtecsProtocol.java b/src/org/traccar/protocol/HomtecsProtocol.java
new file mode 100644
index 000000000..1a2e98565
--- /dev/null
+++ b/src/org/traccar/protocol/HomtecsProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class HomtecsProtocol extends BaseProtocol {
+
+ public HomtecsProtocol() {
+ super("homtecs");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new HomtecsProtocolDecoder(HomtecsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/HomtecsProtocolDecoder.java b/src/org/traccar/protocol/HomtecsProtocolDecoder.java
new file mode 100644
index 000000000..0b1cd99e9
--- /dev/null
+++ b/src/org/traccar/protocol/HomtecsProtocolDecoder.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class HomtecsProtocolDecoder extends BaseProtocolDecoder {
+
+ public HomtecsProtocolDecoder(HomtecsProtocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("([^,]+),") // id
+ .number("(dd)(dd)(dd),") // date
+ .number("(dd)(dd)(dd).(d+),") // time
+ .number("(d+),") // satellites
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.?d*)?,") // speed
+ .number("(d+.?d*)?,") // course
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setTime(dateBuilder.getDate());
+
+ position.setValid(true);
+ position.set(Event.KEY_SATELLITES, parser.nextInt());
+
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/IntellitracProtocol.java b/src/org/traccar/protocol/IntellitracProtocol.java
index c06585547..a6caf770f 100644
--- a/src/org/traccar/protocol/IntellitracProtocol.java
+++ b/src/org/traccar/protocol/IntellitracProtocol.java
@@ -36,8 +36,8 @@ public class IntellitracProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new IntellitracFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new IntellitracProtocolDecoder(IntellitracProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/KenjiProtocolDecoder.java b/src/org/traccar/protocol/KenjiProtocolDecoder.java
index e1f8130fa..e190af948 100755
--- a/src/org/traccar/protocol/KenjiProtocolDecoder.java
+++ b/src/org/traccar/protocol/KenjiProtocolDecoder.java
@@ -47,7 +47,7 @@ public class KenjiProtocolDecoder extends BaseProtocolDecoder {
.number("G(d+)") // satellites
.any()
.compile();
-
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
diff --git a/src/org/traccar/protocol/KhdProtocol.java b/src/org/traccar/protocol/KhdProtocol.java
index a2edbfd5e..93edb0415 100644
--- a/src/org/traccar/protocol/KhdProtocol.java
+++ b/src/org/traccar/protocol/KhdProtocol.java
@@ -39,8 +39,8 @@ public class KhdProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(512, 3, 2));
- pipeline.addLast("objectDecoder", new KhdProtocolDecoder(KhdProtocol.this));
pipeline.addLast("objectEncoder", new KhdProtocolEncoder());
+ pipeline.addLast("objectDecoder", new KhdProtocolDecoder(KhdProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/LaipacProtocol.java b/src/org/traccar/protocol/LaipacProtocol.java
index fff7b1c32..c2e5c585e 100644
--- a/src/org/traccar/protocol/LaipacProtocol.java
+++ b/src/org/traccar/protocol/LaipacProtocol.java
@@ -37,8 +37,8 @@ public class LaipacProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new LaipacProtocolDecoder(LaipacProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/ManPowerProtocol.java b/src/org/traccar/protocol/ManPowerProtocol.java
index 4fc194963..647a1bea7 100644
--- a/src/org/traccar/protocol/ManPowerProtocol.java
+++ b/src/org/traccar/protocol/ManPowerProtocol.java
@@ -37,8 +37,8 @@ public class ManPowerProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new ManPowerProtocolDecoder(ManPowerProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/MegastekProtocol.java b/src/org/traccar/protocol/MegastekProtocol.java
index cdf891afe..f61a4443c 100644
--- a/src/org/traccar/protocol/MegastekProtocol.java
+++ b/src/org/traccar/protocol/MegastekProtocol.java
@@ -36,8 +36,8 @@ public class MegastekProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new MegastekFrameDecoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new MegastekProtocolDecoder(MegastekProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/MeiligaoProtocol.java b/src/org/traccar/protocol/MeiligaoProtocol.java
index 05dd8834b..2e4226e19 100644
--- a/src/org/traccar/protocol/MeiligaoProtocol.java
+++ b/src/org/traccar/protocol/MeiligaoProtocol.java
@@ -43,15 +43,15 @@ public class MeiligaoProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new MeiligaoFrameDecoder());
- pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder());
+ pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder());
+ pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
index cb2139a00..5c17aab61 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -54,23 +54,19 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("|(xxxx)?") // state
.groupBegin()
.number("|(xxxx),(xxxx)") // adc
+ .number("(?:,(xxxx),(xxxx),(xxxx),(xxxx),(xxxx),(xxxx))?")
.groupBegin()
- .number(",(xxxx),(xxxx),(xxxx),(xxxx),(xxxx),(xxxx)")
- .groupEnd("?")
- .groupBegin()
- .text("|")
- .groupBegin()
- .number("(x{16})") // cell
+ .number("|x{16}") // cell
.number("|(xx)") // gsm
- .number("|(x{8})|") // odometer
- .number("(x{9})") // odometer
+ .number("|(x{8})") // odometer
+ .or()
+ .number("|(x{9})") // odometer
.groupBegin()
.number("|(x{5,})") // rfid
.groupEnd("?")
.groupEnd("?")
.groupEnd("?")
.groupEnd("?")
- .groupEnd("?")
.any()
.compile();
@@ -283,15 +279,14 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
}
- position.set(Event.KEY_CID, parser.next());
-
if (parser.hasNext()) {
position.set(Event.KEY_GSM, parser.nextInt(16));
}
if (parser.hasNext()) {
position.set(Event.KEY_ODOMETER, parser.nextInt(16));
- } else if (parser.hasNext()) {
+ }
+ if (parser.hasNext()) {
position.set(Event.KEY_ODOMETER, parser.nextInt(16));
}
diff --git a/src/org/traccar/protocol/MeitrackProtocol.java b/src/org/traccar/protocol/MeitrackProtocol.java
index c957d4ea1..e5b5ddf2b 100644
--- a/src/org/traccar/protocol/MeitrackProtocol.java
+++ b/src/org/traccar/protocol/MeitrackProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,10 +31,12 @@ public class MeitrackProtocol extends BaseProtocol {
public MeitrackProtocol() {
super("meitrack");
setSupportedCommands(
+ Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME,
Command.TYPE_ALARM_ARM,
- Command.TYPE_ALARM_DISARM);
+ Command.TYPE_ALARM_DISARM,
+ Command.TYPE_REQUEST_PHOTO);
}
@Override
@@ -44,6 +46,7 @@ public class MeitrackProtocol extends BaseProtocol {
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new MeitrackFrameDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectEncoder", new MeitrackProtocolEncoder());
pipeline.addLast("objectDecoder", new MeitrackProtocolDecoder(MeitrackProtocol.this));
}
};
@@ -53,6 +56,7 @@ public class MeitrackProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectEncoder", new MeitrackProtocolEncoder());
pipeline.addLast("objectDecoder", new MeitrackProtocolDecoder(MeitrackProtocol.this));
}
};
diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
index e0ca01412..a920c6e76 100644
--- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -24,6 +24,7 @@ import java.util.regex.Pattern;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -237,10 +238,17 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
index = buf.indexOf(index + 1, buf.writerIndex(), (byte) ',');
String type = buf.toString(index + 1, 3, Charset.defaultCharset());
- if (type.equals("CCC")) {
- return decodeBinaryMessage(channel, remoteAddress, buf);
- } else {
- return decodeRegularMessage(channel, remoteAddress, buf);
+ switch (type) {
+ case "D03":
+ if (channel != null) {
+ String imei = Context.getIdentityManager().getDeviceById(getDeviceId()).getUniqueId();
+ channel.write("@@O46," + imei + ",D00,camera_picture.jpg,0*00\r\n");
+ }
+ return null;
+ case "CCC":
+ return decodeBinaryMessage(channel, remoteAddress, buf);
+ default:
+ return decodeRegularMessage(channel, remoteAddress, buf);
}
}
diff --git a/src/org/traccar/protocol/MeitrackProtocolEncoder.java b/src/org/traccar/protocol/MeitrackProtocolEncoder.java
index bfda2b7d2..381935c58 100644
--- a/src/org/traccar/protocol/MeitrackProtocolEncoder.java
+++ b/src/org/traccar/protocol/MeitrackProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,23 +16,36 @@
package org.traccar.protocol;
import org.traccar.StringProtocolEncoder;
+import org.traccar.helper.Checksum;
import org.traccar.helper.Log;
import org.traccar.model.Command;
public class MeitrackProtocolEncoder extends StringProtocolEncoder {
+ private Object formatCommand(Command command, char dataId, String content) {
+ String uniqueId = getUniqueId(command.getDeviceId());
+ int length = 1 + uniqueId.length() + 1 + content.length() + 5;
+ String result = String.format("@@%c%02d,%s,%s*", dataId, length, uniqueId, content);
+ result += Checksum.sum(result) + "\r\n";
+ return result;
+ }
+
@Override
protected Object encodeCommand(Command command) {
switch (command.getType()) {
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, 'Q', "A10");
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "@@M33,{%s},C01,0,12222*18\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, 'M', "C01,0,12222");
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "@@M33,{%s},C01,0,02222*18\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, 'M', "C01,0,02222");
case Command.TYPE_ALARM_ARM:
- return formatCommand(command, "@@M33,{%s},C01,0,22122*18\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, 'M', "C01,0,22122");
case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, "@@M33,{%s},C01,0,22022*18\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, 'M', "C01,0,22022");
+ case Command.TYPE_REQUEST_PHOTO:
+ return formatCommand(command, 'D', "D03,1,camera_picture.jpg");
default:
Log.warning(new UnsupportedOperationException(command.getType()));
break;
diff --git a/src/org/traccar/protocol/Mta6Protocol.java b/src/org/traccar/protocol/Mta6Protocol.java
index 8232b850c..607bf8118 100644
--- a/src/org/traccar/protocol/Mta6Protocol.java
+++ b/src/org/traccar/protocol/Mta6Protocol.java
@@ -35,8 +35,8 @@ public class Mta6Protocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("httpEncoder", new HttpResponseEncoder());
+ pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("objectDecoder", new Mta6ProtocolDecoder(
Mta6Protocol.this, !Context.getConfig().getBoolean(getName() + ".can")));
}
diff --git a/src/org/traccar/protocol/MtxProtocol.java b/src/org/traccar/protocol/MtxProtocol.java
index 4fba981a5..e0bef8350 100644
--- a/src/org/traccar/protocol/MtxProtocol.java
+++ b/src/org/traccar/protocol/MtxProtocol.java
@@ -37,8 +37,8 @@ public class MtxProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new MtxProtocolDecoder(MtxProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/org/traccar/protocol/MxtProtocolDecoder.java
index fedb7c715..53d5b8b09 100644
--- a/src/org/traccar/protocol/MxtProtocolDecoder.java
+++ b/src/org/traccar/protocol/MxtProtocolDecoder.java
@@ -114,7 +114,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(infoGroups, 4)) {
- position.set("hours", buf.readUnsignedInt());
+ position.set(Event.KEY_HOURS, buf.readUnsignedInt());
}
if (BitUtil.check(infoGroups, 5)) {
diff --git a/src/org/traccar/protocol/OsmAndProtocol.java b/src/org/traccar/protocol/OsmAndProtocol.java
index 1e60ae807..3b473c435 100644
--- a/src/org/traccar/protocol/OsmAndProtocol.java
+++ b/src/org/traccar/protocol/OsmAndProtocol.java
@@ -35,8 +35,8 @@ public class OsmAndProtocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("httpEncoder", new HttpResponseEncoder());
+ pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("objectDecoder", new OsmAndProtocolDecoder(OsmAndProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/PathAwayProtocol.java b/src/org/traccar/protocol/PathAwayProtocol.java
index dfa34abd5..d7efdfaf6 100644
--- a/src/org/traccar/protocol/PathAwayProtocol.java
+++ b/src/org/traccar/protocol/PathAwayProtocol.java
@@ -35,8 +35,8 @@ public class PathAwayProtocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("httpEncoder", new HttpResponseEncoder());
+ pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("objectDecoder", new PathAwayProtocolDecoder(PathAwayProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/PiligrimProtocol.java b/src/org/traccar/protocol/PiligrimProtocol.java
index 0478835d9..11aafa412 100644
--- a/src/org/traccar/protocol/PiligrimProtocol.java
+++ b/src/org/traccar/protocol/PiligrimProtocol.java
@@ -36,9 +36,9 @@ public class PiligrimProtocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("httpEncoder", new HttpResponseEncoder());
pipeline.addLast("httpDecoder", new HttpRequestDecoder());
pipeline.addLast("httpAggregator", new HttpChunkAggregator(16384));
- pipeline.addLast("httpEncoder", new HttpResponseEncoder());
pipeline.addLast("objectDecoder", new PiligrimProtocolDecoder(PiligrimProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/Pt3000Protocol.java b/src/org/traccar/protocol/Pt3000Protocol.java
index bde8e7709..3a22be214 100644
--- a/src/org/traccar/protocol/Pt3000Protocol.java
+++ b/src/org/traccar/protocol/Pt3000Protocol.java
@@ -37,8 +37,8 @@ public class Pt3000Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, 'd')); // probably wrong
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Pt3000ProtocolDecoder(Pt3000Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/SuntechProtocol.java b/src/org/traccar/protocol/SuntechProtocol.java
index d3f5d6009..e06ed2aac 100644
--- a/src/org/traccar/protocol/SuntechProtocol.java
+++ b/src/org/traccar/protocol/SuntechProtocol.java
@@ -41,10 +41,10 @@ public class SuntechProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new SuntechProtocolDecoder(SuntechProtocol.this));
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new SuntechProtocolEncoder());
+ pipeline.addLast("objectDecoder", new SuntechProtocolDecoder(SuntechProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/T55Protocol.java b/src/org/traccar/protocol/T55Protocol.java
index 21f1d3e6c..041413a65 100644
--- a/src/org/traccar/protocol/T55Protocol.java
+++ b/src/org/traccar/protocol/T55Protocol.java
@@ -37,8 +37,8 @@ public class T55Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new T55ProtocolDecoder(T55Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/org/traccar/protocol/T55ProtocolDecoder.java
index 9c47d1ab0..71f493318 100644
--- a/src/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/org/traccar/protocol/T55ProtocolDecoder.java
@@ -42,7 +42,13 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW]),")
.number("(d+.?d*)?,") // speed
.number("(d+.?d*)?,") // course
- .number("(dd)(dd)(dd)") // date
+ .number("(dd)(dd)(dd),") // date
+ .expression("[^*]+")
+ .text("*")
+ .expression("[^,]+")
+ .number(",(d+)") // satellites
+ .number(",(d+)") // imei
+ .number(",(d+)").optional(3)
.any()
.compile();
@@ -84,7 +90,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
private Position position = null;
- private Position decodeGprmc(String sentence, Channel channel) {
+ private Position decodeGprmc(String sentence, SocketAddress remoteAddress, Channel channel) {
if (channel != null) {
channel.write("OK1\r\n");
@@ -114,6 +120,14 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
+ if (parser.hasNext(3)) {
+ position.set(Event.KEY_SATELLITES, parser.next());
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+ }
+
if (hasDeviceId()) {
return position;
} else {
@@ -217,7 +231,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
} else if (sentence.startsWith("IMEI")) {
identify(sentence.substring(5, sentence.length()), channel, remoteAddress);
} else if (sentence.startsWith("$GPFID")) {
- if (identify(sentence.substring(6, sentence.length()), channel, remoteAddress) && position != null) {
+ if (identify(sentence.substring(7, sentence.length()), channel, remoteAddress) && position != null) {
Position position = this.position;
position.setDeviceId(getDeviceId());
this.position = null;
@@ -226,7 +240,7 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
} else if (Character.isDigit(sentence.charAt(0)) && sentence.length() == 15) {
identify(sentence, channel, remoteAddress);
} else if (sentence.startsWith("$GPRMC")) {
- return decodeGprmc(sentence, channel);
+ return decodeGprmc(sentence, remoteAddress, channel);
} else if (sentence.startsWith("$GPGGA") && hasDeviceId()) {
return decodeGpgga(sentence);
} else if (sentence.startsWith("$GPRMA") && hasDeviceId()) {
diff --git a/src/org/traccar/protocol/TaipProtocol.java b/src/org/traccar/protocol/TaipProtocol.java
index 439fe7696..a3b744e70 100644
--- a/src/org/traccar/protocol/TaipProtocol.java
+++ b/src/org/traccar/protocol/TaipProtocol.java
@@ -38,14 +38,15 @@ public class TaipProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '<'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new TaipProtocolDecoder(TaipProtocol.this, true));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("stringEncoder", new StringEncoder());
pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new TaipProtocolDecoder(TaipProtocol.this, false));
}
diff --git a/src/org/traccar/protocol/TelicFrameDecoder.java b/src/org/traccar/protocol/TelicFrameDecoder.java
new file mode 100644
index 000000000..2a6e121cf
--- /dev/null
+++ b/src/org/traccar/protocol/TelicFrameDecoder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+public class TelicFrameDecoder extends FrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+
+ if (buf.readableBytes() < 4) {
+ return null;
+ }
+
+ long length = buf.getUnsignedInt(buf.readerIndex());
+
+ if (length < 1024) {
+ if (buf.readableBytes() >= length + 4) {
+ buf.readUnsignedInt();
+ return buf.readBytes((int) length);
+ }
+ } else {
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
+ if (endIndex >= 0) {
+ ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex());
+ buf.readByte();
+ if (frame.readableBytes() > 0) {
+ return frame;
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/TelicProtocol.java b/src/org/traccar/protocol/TelicProtocol.java
new file mode 100644
index 000000000..464892431
--- /dev/null
+++ b/src/org/traccar/protocol/TelicProtocol.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+import java.util.List;
+
+public class TelicProtocol extends BaseProtocol {
+
+ public TelicProtocol() {
+ super("telic");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ TrackerServer server = new TrackerServer(new ServerBootstrap(), this.getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new TelicFrameDecoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new TelicProtocolDecoder(TelicProtocol.this));
+ }
+ };
+ server.setEndianness(ByteOrder.LITTLE_ENDIAN);
+ serverList.add(server);
+ }
+
+}
diff --git a/src/org/traccar/protocol/TelikProtocolDecoder.java b/src/org/traccar/protocol/TelicProtocolDecoder.java
index 4171750d6..ba6d9c47e 100644
--- a/src/org/traccar/protocol/TelikProtocolDecoder.java
+++ b/src/org/traccar/protocol/TelicProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2014 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,9 +25,9 @@ import org.traccar.helper.PatternBuilder;
import org.traccar.model.Event;
import org.traccar.model.Position;
-public class TelikProtocolDecoder extends BaseProtocolDecoder {
+public class TelicProtocolDecoder extends BaseProtocolDecoder {
- public TelikProtocolDecoder(TelikProtocol protocol) {
+ public TelicProtocolDecoder(TelicProtocol protocol) {
super(protocol);
}
@@ -39,12 +39,19 @@ public class TelikProtocolDecoder extends BaseProtocolDecoder {
.number("d+,")
.number("(dd)(dd)(dd)") // date
.number("(dd)(dd)(dd),") // time
+ .groupBegin()
+ .number("(ddd)(dd)(dddd),") // longitude
+ .number("(dd)(dd)(dddd),") // latitude
+ .or()
.number("(-?d+),") // longitude
.number("(-?d+),") // latitude
+ .groupEnd()
.number("(d),") // validity
.number("(d+),") // speed
.number("(d+),") // course
.number("(d+),") // satellites
+ .expression("(?:[^,]*,){7}")
+ .number("(d+),")
.any()
.compile();
@@ -72,13 +79,22 @@ public class TelikProtocolDecoder extends BaseProtocolDecoder {
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
- position.setLongitude(parser.nextDouble() / 10000);
- position.setLatitude(parser.nextDouble() / 10000);
+ if (parser.hasNext(6)) {
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN));
+ }
+
+ if (parser.hasNext(2)) {
+ position.setLongitude(parser.nextDouble() / 10000);
+ position.setLatitude(parser.nextDouble() / 10000);
+ }
+
position.setValid(parser.nextInt() != 1);
position.setSpeed(parser.nextDouble());
position.setCourse(parser.nextDouble());
position.set(Event.KEY_SATELLITES, parser.next());
+ position.set(Event.KEY_BATTERY, parser.nextInt());
return position;
}
diff --git a/src/org/traccar/protocol/TeltonikaProtocol.java b/src/org/traccar/protocol/TeltonikaProtocol.java
index 859b2a08c..f944c3003 100644
--- a/src/org/traccar/protocol/TeltonikaProtocol.java
+++ b/src/org/traccar/protocol/TeltonikaProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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.protocol;
+import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
@@ -37,6 +38,12 @@ public class TeltonikaProtocol extends BaseProtocol {
pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this));
}
});
+ serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this));
+ }
+ });
}
}
diff --git a/src/org/traccar/protocol/Tk102Protocol.java b/src/org/traccar/protocol/Tk102Protocol.java
index 4e1d067f9..1f4eda730 100644
--- a/src/org/traccar/protocol/Tk102Protocol.java
+++ b/src/org/traccar/protocol/Tk102Protocol.java
@@ -37,8 +37,8 @@ public class Tk102Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ']'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tk102ProtocolDecoder(Tk102Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/Tk103Protocol.java b/src/org/traccar/protocol/Tk103Protocol.java
index b14264091..324c01a31 100644
--- a/src/org/traccar/protocol/Tk103Protocol.java
+++ b/src/org/traccar/protocol/Tk103Protocol.java
@@ -38,16 +38,16 @@ public class Tk103Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ')'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tk103ProtocolDecoder(Tk103Protocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tk103ProtocolDecoder(Tk103Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/Tlt2hProtocol.java b/src/org/traccar/protocol/Tlt2hProtocol.java
index 3a1bf99ff..14787537d 100644
--- a/src/org/traccar/protocol/Tlt2hProtocol.java
+++ b/src/org/traccar/protocol/Tlt2hProtocol.java
@@ -37,8 +37,8 @@ public class Tlt2hProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(32 * 1024, "##\r\n"));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tlt2hProtocolDecoder(Tlt2hProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/TotemProtocol.java b/src/org/traccar/protocol/TotemProtocol.java
index 097f6a593..4d7f0a135 100644
--- a/src/org/traccar/protocol/TotemProtocol.java
+++ b/src/org/traccar/protocol/TotemProtocol.java
@@ -41,10 +41,10 @@ public class TotemProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new TotemFrameDecoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("objectDecoder", new TotemProtocolDecoder(TotemProtocol.this));
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new TotemProtocolEncoder());
+ pipeline.addLast("objectDecoder", new TotemProtocolDecoder(TotemProtocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Tr20Protocol.java b/src/org/traccar/protocol/Tr20Protocol.java
index 8c5a27ae2..4e55d5f98 100644
--- a/src/org/traccar/protocol/Tr20Protocol.java
+++ b/src/org/traccar/protocol/Tr20Protocol.java
@@ -37,8 +37,8 @@ public class Tr20Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tr20ProtocolDecoder(Tr20Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/Tr900Protocol.java b/src/org/traccar/protocol/Tr900Protocol.java
index 4db0a2239..d54e4cb3c 100644
--- a/src/org/traccar/protocol/Tr900Protocol.java
+++ b/src/org/traccar/protocol/Tr900Protocol.java
@@ -38,16 +38,16 @@ public class Tr900Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tr900ProtocolDecoder(Tr900Protocol.this));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new Tr900ProtocolDecoder(Tr900Protocol.this));
}
});
diff --git a/src/org/traccar/protocol/TrackboxProtocol.java b/src/org/traccar/protocol/TrackboxProtocol.java
index 68554394e..5477de852 100644
--- a/src/org/traccar/protocol/TrackboxProtocol.java
+++ b/src/org/traccar/protocol/TrackboxProtocol.java
@@ -37,8 +37,8 @@ public class TrackboxProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new TrackboxProtocolDecoder(TrackboxProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/TrvProtocol.java b/src/org/traccar/protocol/TrvProtocol.java
index 916b7d612..af598ea02 100644
--- a/src/org/traccar/protocol/TrvProtocol.java
+++ b/src/org/traccar/protocol/TrvProtocol.java
@@ -37,8 +37,8 @@ public class TrvProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new TrvProtocolDecoder(TrvProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/org/traccar/protocol/TrvProtocolDecoder.java
index 94796fa5e..8075515c2 100644
--- a/src/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/org/traccar/protocol/TrvProtocolDecoder.java
@@ -105,7 +105,7 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_GSM, parser.nextInt());
position.set(Event.KEY_SATELLITES, parser.nextInt());
- position.set(Event.KEY_BATTERY , parser.nextInt());
+ position.set(Event.KEY_BATTERY, parser.nextInt());
position.set(Event.KEY_IGNITION, parser.nextInt() != 0);
position.set("arm", parser.nextInt());
diff --git a/src/org/traccar/protocol/TelikProtocol.java b/src/org/traccar/protocol/Tt8850Protocol.java
index b6b5ba14d..a409205c7 100644
--- a/src/org/traccar/protocol/TelikProtocol.java
+++ b/src/org/traccar/protocol/Tt8850Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,10 +24,10 @@ import org.traccar.TrackerServer;
import java.util.List;
-public class TelikProtocol extends BaseProtocol {
+public class Tt8850Protocol extends BaseProtocol {
- public TelikProtocol() {
- super("telik");
+ public Tt8850Protocol() {
+ super("tt8850");
}
@Override
@@ -35,9 +35,9 @@ public class TelikProtocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\0'));
+ pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "$"));
pipeline.addLast("stringDecoder", new StringDecoder());
- pipeline.addLast("objectDecoder", new TelikProtocolDecoder(TelikProtocol.this));
+ pipeline.addLast("objectDecoder", new Tt8850ProtocolDecoder(Tt8850Protocol.this));
}
});
}
diff --git a/src/org/traccar/protocol/Tt8850ProtocolDecoder.java b/src/org/traccar/protocol/Tt8850ProtocolDecoder.java
new file mode 100644
index 000000000..9979fd61d
--- /dev/null
+++ b/src/org/traccar/protocol/Tt8850ProtocolDecoder.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class Tt8850ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Tt8850ProtocolDecoder(Tt8850Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .binary("0004,")
+ .number("xxxx,")
+ .expression("[01],")
+ .expression("GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .expression("([^,]+),") // imei
+ .any()
+ .number("(d{1,2})?,") // gps accuracy
+ .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
+ .number("(dd)(dd)(dd),") // time
+ .number("(0ddd)?,") // mcc
+ .number("(0ddd)?,") // mnc
+ .number("(xxxx)?,") // lac
+ .number("(xxxx)?,") // cell
+ .any()
+ .number("(dddd)(dd)(dd)") // date
+ .number("(dd)(dd)(dd),") // time
+ .number("(xxxx)")
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ position.setValid(parser.nextInt() < 20);
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setLatitude(parser.nextDouble());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ if (parser.hasNext(4)) {
+ position.set(Event.KEY_MCC, parser.nextInt());
+ position.set(Event.KEY_MNC, parser.nextInt());
+ position.set(Event.KEY_LAC, parser.nextInt(16));
+ position.set(Event.KEY_CID, parser.nextInt(16));
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
index bf0a9cc76..a5c608dd3 100644
--- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -60,8 +60,8 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
while (buf.readerIndex() < end) {
int parameterLength = buf.getUnsignedByte(buf.readerIndex()) >> 4;
- position.add(ObdDecoder.decode(buf.readUnsignedByte() & 0x0F, buf.readUnsignedByte(),
- ChannelBuffers.hexDump(buf.readBytes(parameterLength - 2))));
+ int mode = buf.readUnsignedByte() & 0x0F;
+ position.add(ObdDecoder.decode(mode, ChannelBuffers.hexDump(buf.readBytes(parameterLength - 1))));
}
}
diff --git a/src/org/traccar/protocol/WatchProtocol.java b/src/org/traccar/protocol/WatchProtocol.java
index 3a10f9c59..1198f689e 100644
--- a/src/org/traccar/protocol/WatchProtocol.java
+++ b/src/org/traccar/protocol/WatchProtocol.java
@@ -37,8 +37,8 @@ public class WatchProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ']'));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new WatchProtocolDecoder(WatchProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/WialonProtocol.java b/src/org/traccar/protocol/WialonProtocol.java
index 6fe924c2d..d0d3aa446 100644
--- a/src/org/traccar/protocol/WialonProtocol.java
+++ b/src/org/traccar/protocol/WialonProtocol.java
@@ -37,8 +37,8 @@ public class WialonProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(4 * 1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new WialonProtocolDecoder(WialonProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/XexunProtocolDecoder.java b/src/org/traccar/protocol/XexunProtocolDecoder.java
index 0929ee99b..4d4831a20 100644
--- a/src/org/traccar/protocol/XexunProtocolDecoder.java
+++ b/src/org/traccar/protocol/XexunProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2012 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,8 +38,8 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
.expression("G[PN]RMC,")
.number("(dd)(dd)(dd).(d+),") // time
.expression("([AV]),") // validity
- .number("(d+)(dd.d+),([NS]),") // latitude
- .number("(d+)(dd.d+),([EW])?,") // longitude
+ .number("(d*?)(d?d.d+),([NS]),") // latitude
+ .number("(d*?)(d?d.d+),([EW])?,") // longitude
.number("(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
.number("(dd)(dd)(dd),") // date
diff --git a/src/org/traccar/protocol/XirgoProtocol.java b/src/org/traccar/protocol/XirgoProtocol.java
index 83c373175..f186c731f 100644
--- a/src/org/traccar/protocol/XirgoProtocol.java
+++ b/src/org/traccar/protocol/XirgoProtocol.java
@@ -37,8 +37,8 @@ public class XirgoProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, "##"));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new XirgoProtocolDecoder(XirgoProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/YwtProtocol.java b/src/org/traccar/protocol/YwtProtocol.java
index e9cdb1615..3fa154ec0 100644
--- a/src/org/traccar/protocol/YwtProtocol.java
+++ b/src/org/traccar/protocol/YwtProtocol.java
@@ -37,8 +37,8 @@ public class YwtProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectDecoder", new YwtProtocolDecoder(YwtProtocol.this));
}
});
diff --git a/src/org/traccar/web/AsyncServlet.java b/src/org/traccar/web/AsyncServlet.java
index b10df5408..e4cb64c57 100644
--- a/src/org/traccar/web/AsyncServlet.java
+++ b/src/org/traccar/web/AsyncServlet.java
@@ -226,7 +226,7 @@ public class AsyncServlet extends BaseServlet {
synchronized (ASYNC_SESSIONS) {
if (Boolean.parseBoolean(req.getParameter("first")) || !ASYNC_SESSIONS.containsKey(userId)) {
- Collection<Long> devices = Context.getPermissionsManager().allowedDevices(userId);
+ Collection<Long> devices = Context.getPermissionsManager().getDevicePermissions(userId);
ASYNC_SESSIONS.put(userId, new AsyncSession(userId, devices));
}
diff --git a/src/org/traccar/web/BaseServlet.java b/src/org/traccar/web/BaseServlet.java
index 8b022d556..17f4f19cd 100644
--- a/src/org/traccar/web/BaseServlet.java
+++ b/src/org/traccar/web/BaseServlet.java
@@ -53,7 +53,7 @@ public abstract class BaseServlet extends HttpServlet {
String origin = req.getHeader(HttpHeaders.Names.ORIGIN);
String allowed = Context.getConfig().getString("web.origin");
- if (allowed == null) {
+ if (allowed == null || origin == null) {
resp.setHeader(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW_ORIGIN_VALUE);
} else if (allowed.contains(origin)) {
String originSafe = URLEncoder.encode(origin, StandardCharsets.UTF_8.name());
diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java
index d6cd19d87..4e764d5d6 100644
--- a/src/org/traccar/web/WebServer.java
+++ b/src/org/traccar/web/WebServer.java
@@ -42,7 +42,9 @@ import org.traccar.api.ResourceErrorHandler;
import org.traccar.api.SecurityRequestFilter;
import org.traccar.api.resource.CommandResource;
import org.traccar.api.resource.DeviceResource;
-import org.traccar.api.resource.PermissionResource;
+import org.traccar.api.resource.DevicePermissionResource;
+import org.traccar.api.resource.GroupPermissionResource;
+import org.traccar.api.resource.GroupResource;
import org.traccar.api.resource.PositionResource;
import org.traccar.api.resource.ServerResource;
import org.traccar.api.resource.SessionResource;
@@ -140,7 +142,8 @@ public class WebServer {
resourceConfig.register(SecurityRequestFilter.class);
resourceConfig.register(CorsResponseFilter.class);
resourceConfig.registerClasses(ServerResource.class, SessionResource.class, CommandResource.class,
- PermissionResource.class, DeviceResource.class, UserResource.class, PositionResource.class);
+ GroupPermissionResource.class, DevicePermissionResource.class, UserResource.class,
+ GroupResource.class, DeviceResource.class, PositionResource.class);
servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
handlers.addHandler(servletHandler);
diff --git a/test/org/traccar/ProtocolTest.java b/test/org/traccar/ProtocolTest.java
index 75c12b171..d7104edba 100644
--- a/test/org/traccar/ProtocolTest.java
+++ b/test/org/traccar/ProtocolTest.java
@@ -194,7 +194,7 @@ public class ProtocolTest {
if (attributes.containsKey(Event.KEY_LAC) || attributes.containsKey(Event.KEY_CID)) {
checkInteger(attributes.get(Event.KEY_LAC), 1, 65535);
- checkInteger(attributes.get(Event.KEY_CID), 1, 268435455);
+ checkInteger(attributes.get(Event.KEY_CID), 0, 268435455);
}
if (attributes.containsKey(Event.KEY_MCC) || attributes.containsKey(Event.KEY_MNC)) {
diff --git a/test/org/traccar/database/GroupTreeTest.java b/test/org/traccar/database/GroupTreeTest.java
new file mode 100644
index 000000000..f73cb75b6
--- /dev/null
+++ b/test/org/traccar/database/GroupTreeTest.java
@@ -0,0 +1,55 @@
+package org.traccar.database;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class GroupTreeTest {
+
+ private static Group createGroup(long id, String name, long parent) {
+ Group group = new Group();
+ group.setId(id);
+ group.setName(name);
+ group.setGroupId(parent);
+ return group;
+ }
+
+ private static Device createDevice(long id, String name, long parent) {
+ Device device = new Device();
+ device.setId(id);
+ device.setName(name);
+ device.setGroupId(parent);
+ return device;
+ }
+
+ @Test
+ public void testGetDescendants() {
+ Collection<Group> groups = new ArrayList<>();
+ groups.add(createGroup(1, "First", 0));
+ groups.add(createGroup(2, "Second", 1));
+ groups.add(createGroup(3, "Third", 2));
+ groups.add(createGroup(4, "Fourth", 2));
+ groups.add(createGroup(5, "Fifth", 4));
+
+ Collection<Device> devices = new ArrayList<>();
+ devices.add(createDevice(1, "One", 3));
+ devices.add(createDevice(2, "Two", 5));
+ devices.add(createDevice(3, "One", 5));
+
+ GroupTree groupTree = new GroupTree(groups, devices);
+
+ Assert.assertEquals(4, groupTree.getGroups(1).size());
+ Assert.assertEquals(3, groupTree.getGroups(2).size());
+ Assert.assertEquals(0, groupTree.getGroups(3).size());
+ Assert.assertEquals(1, groupTree.getGroups(4).size());
+
+ Assert.assertEquals(3, groupTree.getDevices(1).size());
+ Assert.assertEquals(1, groupTree.getDevices(3).size());
+ Assert.assertEquals(2, groupTree.getDevices(4).size());
+ }
+
+}
diff --git a/test/org/traccar/helper/ObdDecoderTest.java b/test/org/traccar/helper/ObdDecoderTest.java
index 238f3e0a6..62881b66c 100644
--- a/test/org/traccar/helper/ObdDecoderTest.java
+++ b/test/org/traccar/helper/ObdDecoderTest.java
@@ -8,11 +8,11 @@ public class ObdDecoderTest {
@Test
public void testDecode() {
- Assert.assertEquals(83, ObdDecoder.decode(0x01, 0x05, "7b").getValue());
- Assert.assertEquals(1225, ObdDecoder.decode(0x01, 0x0C, "1324").getValue());
- Assert.assertEquals(20, ObdDecoder.decode(0x01, 0x0D, "14").getValue());
- Assert.assertEquals(64050, ObdDecoder.decode(0x01, 0x31, "fa32").getValue());
- Assert.assertEquals(25, ObdDecoder.decode(0x01, 0x2F, "41").getValue());
+ Assert.assertEquals(83, ObdDecoder.decode(0x01, "057b").getValue());
+ Assert.assertEquals(1225, ObdDecoder.decode(0x01, "0C1324").getValue());
+ Assert.assertEquals(20, ObdDecoder.decode(0x01, "0D14").getValue());
+ Assert.assertEquals(64050, ObdDecoder.decode(0x01, "31fa32").getValue());
+ Assert.assertEquals(25, ObdDecoder.decode(0x01, "2F41").getValue());
}
diff --git a/test/org/traccar/protocol/AplicomProtocolDecoderTest.java b/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
index 103718941..26844994e 100644
--- a/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
@@ -10,6 +10,15 @@ public class AplicomProtocolDecoderTest extends ProtocolTest {
AplicomProtocolDecoder decoder = new AplicomProtocolDecoder(new AplicomProtocol());
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66002300a21f0b01f056d3e62856d3e626031f845f00c6ee440800000000000000000017bd1cb30000"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66002300a21f0b00f056d3e64756d3e646031f845f00c6ee440800000000000000000017bd1cb30001"));
+
+ verifyPosition(decoder, binary(
+ "44c3014645e8e91b66001f00221f0b01f456ba1e0d56ba1e0b031f842200c6ef550c000000000017bd1cb30004"));
+
verifyNothing(decoder, binary(
"44c3014645e8e9bada003e03fff7070055a4f24200000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00000001000000000000000044c3014645e8e9bada003e03fff77bff55a4f24300000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00300002000000000000000044c3014645e8e9bada003e03fff7690655a4f24500000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff003000030000000000000000"));
diff --git a/test/org/traccar/protocol/AstraProtocolDecoderTest.java b/test/org/traccar/protocol/AstraProtocolDecoderTest.java
new file mode 100644
index 000000000..93a939879
--- /dev/null
+++ b/test/org/traccar/protocol/AstraProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class AstraProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ AstraProtocolDecoder decoder = new AstraProtocolDecoder(new AstraProtocol());
+
+ verifyPositions(decoder, binary(
+ "4b013c02213aec35c501ad031368b8ffcd1ad043e5c4500c79000100003101005c490c001c0009020200020015069600ae03136801ffcd1af143e5c452125e000100003101005c491200090011010000020015068500af0313629effcd1f4b43e5c45d1e46000100003101005c491e00080409040500040015068700b0031359d5ffcd35ad43e5c47b2a3b000001003101005c492a1b1a0d0b0f0b00080015068700b103134984ffcd4b1e43e5c4913354000100003101005c49340b0103090606000f0015067700b203132e1affcd5a5a43e5c4af3348000001003101005c4935070a08000a070017001505f700b30313192cffcd7af143e5c4cd3733000001003101005c4937091206050a0800200015058600b403130debffcda88743e5c4eb2c3e000001003101005c493707030601080600290015058600b377"));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/AutoFonProtocolDecoderTest.java b/test/org/traccar/protocol/AutoFonProtocolDecoderTest.java
index 4a0a42a7b..f84873c6e 100644
--- a/test/org/traccar/protocol/AutoFonProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/AutoFonProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class AutoFonProtocolDecoderTest extends ProtocolTest {
AutoFonProtocolDecoder decoder = new AutoFonProtocolDecoder(new AutoFonProtocol());
+ verifyPosition(decoder, binary(
+ "02080000251848470afa010262daa690013aa4046da83745f8812560df010001126a"));
+
verifyNothing(decoder, binary(
"10556103592310314825728F"));
diff --git a/test/org/traccar/protocol/Gl100ProtocolDecoderTest.java b/test/org/traccar/protocol/Gl100ProtocolDecoderTest.java
index 1e4490f09..803d076b9 100644
--- a/test/org/traccar/protocol/Gl100ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gl100ProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class Gl100ProtocolDecoderTest extends ProtocolTest {
Gl100ProtocolDecoder decoder = new Gl100ProtocolDecoder(new Gl100Protocol());
verifyPosition(decoder, text(
+ "+RESP:GTLGL,359464030492644,1,2,1,0,0.4,0,299.7,1,5.455551,51.449776,20160311083229,0204,0016,03EC,BD94,00,0036,0102090501"));
+
+ verifyPosition(decoder, text(
"+RESP:GTTRI,359464030439249,1,0,61,1,0.0,346,-2.7,2,-80.392825,26.122424,20151214000354,0310,0260,72BC,35F5,00,04B6,0102070407"));
verifyPosition(decoder, text(
diff --git a/test/org/traccar/protocol/Gl200ProtocolDecoderTest.java b/test/org/traccar/protocol/Gl200ProtocolDecoderTest.java
index b06715e29..bf7e3f175 100644
--- a/test/org/traccar/protocol/Gl200ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gl200ProtocolDecoderTest.java
@@ -10,13 +10,25 @@ public class Gl200ProtocolDecoderTest extends ProtocolTest {
Gl200ProtocolDecoder decoder = new Gl200ProtocolDecoder(new Gl200Protocol());
+ verifyPositions(decoder, text(
+ "+RESP:GTFRI,060228,862894020180553,,,10,1,1,20.0,147,329.7,-62.899703,-17.720434,20160309113548,0736,0003,6AAE,3381,00,913.3,,,,0,220101,,,,20160309073554,132B$"));
+
+ verifyPositions(decoder, text(
+ "+BUFF:GTFRI,060402,862894021808798,,,10,1,1,0.0,349,394.3,-63.287717,-17.662410,20160116234031,0736,0003,6ABA,8305,00,3326.8,,,,94,220100,,,,20160116194035,4D83"));
+
verifyPosition(decoder, text(
+ "+RESP:GTFRI,2C0204,867162020003125,GL300W,0,0,2,1,1.7,205,2867.0,-78.481127,-0.206828,20160215210433,0740,0000,7596,5891C,0.0,1,1.7,205,2867.0,-78.481127,-0.206828,20160215210503,0740,0000,7596,5891C,0.0,88,20160215210506,1E78$"));
+
+ verifyPositions(decoder, text(
+ "+RESP:GTFRI,060228,862894020178276,,15153,10,1,1,0.0,0,431.7,-63.169571,-17.776235,20160210153458,0736,0003,6AD4,80EF,00,34.9,00117:31:26,13442,15163,0,210101,,,,20160210113503,38EE$"));
+
+ verifyPositions(decoder, text(
"+RESP:GTFRI,110100,A5868800000015,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20110214013254,0460,0000,18d8,6141,00,80,20110214013254,000C"));
- verifyNothing(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,210102,A10000458356CE,,0,1,1,15,1.4,0,190.6,-85.765763,42.894896,20160208164505,4126,210,0,18673,00,92,20160208164507,00A6"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+BUFF:GTFRI,060402,862894021808798,,,10,1,1,0.0,349,394.3,-63.287717,-17.662410,20160116234031,0736,0003,6ABA,8305,00,3326.8,,,,94,220100,,,,20160116194035,4D83"));
verifyPosition(decoder, text(
@@ -25,10 +37,10 @@ public class Gl200ProtocolDecoderTest extends ProtocolTest {
verifyAttributes(decoder, text(
"+RESP:GTINF,1F0101,135790246811220,1G1JC5444R7252367,,16,898600810906F8048812,16,0,1,12000,,4.2,0,0,,,20090214013254,,,,,,+0800,0,20090214093254,11F0$"));
- verifyAttributes(decoder, text(
+ verifyPositions(decoder, false, text(
"+RESP:GTFRI,120113,555564055560555,,1,1,1,,,,,,,,0282,0380,f080,cabf,6900,79,20140824165629,0001$"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,0F0106,862193020451183,,,10,1,1,0.0,163,,-57.513617,-25.368191,20150918182145,,,,,,21235.0,,,,0,210100,,,,20150918182149,00B8$"));
verifyPosition(decoder, text(
@@ -40,31 +52,31 @@ public class Gl200ProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, text(
"+RESP:GTSTT,1A0401,860599000508846,,41,0,0.0,84,107.5,-76.657998,39.497203,20150623160622,0310,0260,B435,3B81,,20150623160622,0F54$"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,1A0401,860599000508846,,0,0,1,1,134.8,154,278.7,-76.671089,39.778885,20150623154301,0310,0260,043F,7761,,99,20150623154314,0F24$"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,1A0200,860599000165464,CRI001,0,0,1,2,,41,,-71.153137,42.301634,20150328020301,,,,,280.3,55,20150327220351,320C"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,02010D,867844001675407,,0,0,1,2,0.0,0,28.9,8.591011,56.476397,20140915213209,0238,0001,03CB,2871,,97,20140915213459,009A"));
verifyNothing(decoder, text(
"+RESP:GTINF,359464030073766,8938003990320469804f,18,99,100,1,0,+2.00,0,20131018084015,00EE,0103090402"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,04040C,359231038939904,,,10,1,2,0.0,117,346.0,8.924243,50.798077,20130618122040,0262,0002,0299,109C,00,0.0,,,,,,,,,20130618122045,00F6"));
verifyPosition(decoder, text(
"+RESP:GTSTT,04040C,359231038939904,,42,0,0.0,117,346.0,8.924243,50.798077,20130618125152,0262,0002,0299,109C,00,20130618125154,017A"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,020102,000035988863964,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,020102,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,,20090214093254,11F0"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,020102,135790246811220,,0,0,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,,20090214093254,11F0"));
verifyPosition(decoder, text(
@@ -78,72 +90,27 @@ public class Gl200ProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, text(
"+RESP:GTFRI,07000D,868487001005941,,0,0,1,1,0.0,0,46.3,-77.039627,38.907573,20120731175232,0310,0260,B44B,EBC9,0015e96913a7,-58,,100,20120731175244,0114"));
-
- verifyPosition(decoder, text(
- "+RESP:GTTOW,0F0100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTDIS,0F0100,135790246811220,,,20,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTIOB,0F0100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTGEO,0F0100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTSPD,0F0100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTSOS,0F0100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTRTL,0F0100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTDOG,0F0100,135790246811220,,,01,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "+RESP:GTIGL,0F0100,135790246811220,,,00,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
verifyPosition(decoder, text(
"+RESP:GTHBM,0F0100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
verifyPosition(decoder, text(
"+RESP:GTHBM,0F0100,135790246811220,,,11,1,1,24.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
+
+ verifyPositions(decoder, text(
"+RESP:GTFRI,02010C,867844001274144,,0,0,1,1,18.0,233,118.1,7.615551,51.515600,20140106130516,0262,0007,79E6,B956,,72,20140106140524,09CE$"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,02010C,867844001274649,,0,0,1,1,0.0,0,122.5,7.684216,51.524512,20140106233722,0262,0007,79EE,1D22,,93,20140107003805,03C4$"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+BUFF:GTFRI,210101,863286020016706,,,10,1,1,,,,49.903915,40.391669,20140818105815,,,,,,,,,,,210100,,,,,000C$"));
- verifyPosition(decoder, text(
+ verifyPositions(decoder, text(
"+RESP:GTFRI,240100,135790246811220,,,10,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,2000.0,12345:12:34,,80,,,,,,20090214093254,11F0$"));
- verifyPosition(decoder, text(
- "+RESP:GTFRI,240100,135790246811220,,,10,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,,,,,,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "\u0000\u0004,005F,0,GTFRI,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "\u0000\u0004,005F,0,GTGEO,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "\u0000\u0004,005F,0,GTNMR,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0$"));
-
- verifyPosition(decoder, text(
- "\u0000\u0004,0017,0,GTNMR,,867844000400914,,0,41,1,2,0.0,0,1504.2,-75.569202,6.242850,20150404162835,,,,,97,20150404162836,05EF$"));
-
- verifyNothing(decoder, text(
- "\u0000\u0004,0017,0,GTPNA,,867844000400914,,0,0,1,0,,,,0,0,,,,,,99,20150404190153,0601$"));
-
- verifyPosition(decoder, text(
- "\u0000\u0004,0017,0,GTEPN,,867844000400914,,0,0,1,0,0.0,0,1717.4,-75.598445,6.278578,20150405003116,,,,,95,20150405003358,0607$"));
+ verifyPositions(decoder, text(
+ "+RESP:GTFRI,240100,135790246811220,,,10,2,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,00,0,4.3,92,70.0,121.354335,31.222073,20090101000000,0460,0000,18d8,6141,00,2000.0,12345:12:34,,,80,,,,,20090214093254,11F0$"));
verifyPosition(decoder, text(
"+RESP:GTSTT,280100,A1000043D20139,,42,0,0.1,321,228.6,-76.660884,39.832552,20150615120628,0310,0484,00600019,0A52,,20150615085741,0320$"));
diff --git a/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java
index ae3d2fa55..ab59ba331 100644
--- a/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class Gps103ProtocolDecoderTest extends ProtocolTest {
Gps103ProtocolDecoder decoder = new Gps103ProtocolDecoder(new Gps103Protocol());
+ verifyPosition(decoder, text(
+ "359769031878322imei:359769031878322,tracker,1602160718,2,F,221811.000,A,1655.2193,S,14546.6722,E,0.00,,"));
+
verifyNothing(decoder, text(
"imei:865328021049167,OBD,141118115036,,,0.0,,000,0.0%,+,0.0%,00000,,,,,"));
diff --git a/test/org/traccar/protocol/GpsGateProtocolDecoderTest.java b/test/org/traccar/protocol/GpsGateProtocolDecoderTest.java
index c4ed38656..788830e52 100644
--- a/test/org/traccar/protocol/GpsGateProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/GpsGateProtocolDecoderTest.java
@@ -11,6 +11,12 @@ public class GpsGateProtocolDecoderTest extends ProtocolTest {
GpsGateProtocolDecoder decoder = new GpsGateProtocolDecoder(new GpsGateProtocol());
verifyNothing(decoder, text(
+ "$FRCMD,862950025974620,_Ping,voltage=4*4F"));
+
+ verifyPosition(decoder, text(
+ "$FRCMD,862950025974620,_SendMessage, ,2721.5781,S,15259.145,E,61,0.00,61,080316,092612,1,SosButton=0,voltage=4*60"));
+
+ verifyNothing(decoder, text(
"$FRLIN,,user1,8IVHF*7A"));
verifyNothing(decoder, text(
diff --git a/test/org/traccar/protocol/Gt06FrameDecoderTest.java b/test/org/traccar/protocol/Gt06FrameDecoderTest.java
index 9a4c709e8..12ee6bb60 100644
--- a/test/org/traccar/protocol/Gt06FrameDecoderTest.java
+++ b/test/org/traccar/protocol/Gt06FrameDecoderTest.java
@@ -12,6 +12,18 @@ public class Gt06FrameDecoderTest extends ProtocolTest {
Gt06FrameDecoder decoder = new Gt06FrameDecoder();
Assert.assertEquals(
+ binary("78781f1210020e140613cc04770690003e3f2e3414b20000000000000000044c446a0d0a"),
+ decoder.decode(null, null, binary("78781f1210020e140613cc04770690003e3f2e3414b20000000000000000044c446a0d0a")));
+
+ Assert.assertEquals(
+ binary("787808134606020002044dc5050d0a"),
+ decoder.decode(null, null, binary("787808134606020002044dc5050d0a")));
+
+ Assert.assertEquals(
+ binary("78781f1210020e14061dcc0476fcd0003e3faf3e14b20000000000000000044ef6740d0a"),
+ decoder.decode(null, null, binary("78781f1210020e14061dcc0476fcd0003e3faf3e14b20000000000000000044ef6740d0a")));
+
+ Assert.assertEquals(
binary("78780d010352887071911998000479d00d0a"),
decoder.decode(null, null, binary("78780d010352887071911998000479d00d0a")));
diff --git a/test/org/traccar/protocol/HomtecsProtocolDecoderTest.java b/test/org/traccar/protocol/HomtecsProtocolDecoderTest.java
new file mode 100644
index 000000000..5b70453e0
--- /dev/null
+++ b/test/org/traccar/protocol/HomtecsProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HomtecsProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ HomtecsProtocolDecoder decoder = new HomtecsProtocolDecoder(new HomtecsProtocol());
+
+ verifyPosition(decoder, text(
+ "strommabus939_R01272028,160217,191003.00,06,5540.12292,N,01237.49814,E,0.391,,1,1.27,1.2"));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/test/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
index 2dbb24d9e..dd497858b 100644
--- a/test/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class MeiligaoProtocolDecoderTest extends ProtocolTest {
MeiligaoProtocolDecoder decoder = new MeiligaoProtocolDecoder(new MeiligaoProtocol());
verifyPosition(decoder, binary(
+ "24240072190820157fffff99553039343335342e3030302c412c313930372e303631392c4e2c30373235312e333235312c452c3031302e312c3138382e352c3234303231362c2c2c412a36427c302e387c36352e327c303830307c303030302c303030307c303336343838373532c73f0d0a"));
+
+ verifyPosition(decoder, binary(
"242400680790209818ffff99553038333235382e3030302c412c303131352e393338302c532c30333634382e313430392c452c302e30302c3331352e35352c3132303131367c302e37347c313930322e337c303430307c303030302c303030307c302e30f41b0d0a"));
verifyNothing(decoder, binary(
diff --git a/test/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/test/org/traccar/protocol/MeitrackProtocolDecoderTest.java
index dcefb1637..f74914461 100644
--- a/test/org/traccar/protocol/MeitrackProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest {
MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(new MeitrackProtocol());
+ verifyNothing(decoder, buffer(
+ "$$D28,353358017784062,D03,OK*F3"));
+
verifyPosition(decoder, buffer(
"$$A158,79007001520234,AAA,35,40.996370,-8.575065,150730184834,A,8,24,0,1,1.3,173,32573389,31405012,268|3|2BC0|250B,2000,|||0A2D|0000,00000001,,50,,,,,,,,,,,,,*4A"),
position("2015-07-30 18:48:34.000", true, 40.99637, -8.57507));
diff --git a/test/org/traccar/protocol/MeitrackProtocolEncoderTest.java b/test/org/traccar/protocol/MeitrackProtocolEncoderTest.java
new file mode 100644
index 000000000..64812c1da
--- /dev/null
+++ b/test/org/traccar/protocol/MeitrackProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class MeitrackProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ MeitrackProtocolEncoder encoder = new MeitrackProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_POSITION_SINGLE);
+
+ Assert.assertEquals("@@Q25,123456789012345,A10*68\r\n", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/T55ProtocolDecoderTest.java b/test/org/traccar/protocol/T55ProtocolDecoderTest.java
index 20d30ce73..4160758fc 100644
--- a/test/org/traccar/protocol/T55ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/T55ProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class T55ProtocolDecoderTest extends ProtocolTest {
T55ProtocolDecoder decoder = new T55ProtocolDecoder(new T55Protocol());
+ verifyPosition(decoder, text(
+ "$GPRMC,073501.000,A,1255.5125,N,07738.2948,E,0.00,0.53,080316,,,D*73,12,865733027593268,10011"));
+
verifyNothing(decoder, text(
"$GPFID,ID123456ABC"));
diff --git a/test/org/traccar/protocol/TelicFrameDecoderTest.java b/test/org/traccar/protocol/TelicFrameDecoderTest.java
new file mode 100644
index 000000000..64f8b55e6
--- /dev/null
+++ b/test/org/traccar/protocol/TelicFrameDecoderTest.java
@@ -0,0 +1,30 @@
+package org.traccar.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import java.nio.ByteOrder;
+
+public class TelicFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelicFrameDecoder decoder = new TelicFrameDecoder();
+
+ Assert.assertNull(
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "00303032363937393238317c3233327c30337c30303230303430313000")));
+
+ Assert.assertEquals(
+ binary(ByteOrder.LITTLE_ENDIAN, "303032363937393238317c3233327c30337c303032303034303130"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "303032363937393238317c3233327c30337c30303230303430313000")));
+
+ Assert.assertEquals(
+ binary(ByteOrder.LITTLE_ENDIAN, "3030323039373932383139392c3231303231363038313930302c302c3231303231363038313835392c3031333839333338352c34363635383639352c332c302c302c382c2c2c3534312c36313239382c2c303030302c30302c302c3139362c302c30343037"),
+ decoder.decode(null, null, binary(ByteOrder.LITTLE_ENDIAN, "650000003030323039373932383139392c3231303231363038313930302c302c3231303231363038313835392c3031333839333338352c34363635383639352c332c302c302c382c2c2c3534312c36313239382c2c303030302c30302c302c3139362c302c3034303700")));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/TelicProtocolDecoderTest.java b/test/org/traccar/protocol/TelicProtocolDecoderTest.java
new file mode 100644
index 000000000..a922c390d
--- /dev/null
+++ b/test/org/traccar/protocol/TelicProtocolDecoderTest.java
@@ -0,0 +1,37 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TelicProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ TelicProtocolDecoder decoder = new TelicProtocolDecoder(new TelicProtocol());
+
+ verifyPosition(decoder, text(
+ "002017297899,220216111100,0,220216111059,014306446,46626713,3,7,137,7,,,448,266643,,0000,00,0,206,0,0407"));
+
+ verifyPosition(decoder, text(
+ "003097061325,210216112630,0,210216001405,246985,594078,3,0,283,12,,,23,4418669,,0010,00,117,0,0,0108"));
+
+ verifyNothing(decoder, text(
+ "0026970613|248|01|004006011"));
+
+ verifyPosition(decoder, text(
+ "032097061399,210216112800,0,210216112759,246912,594076,3,47,291,10,,,46,4419290,,0010,00,100,0,0,0108"));
+
+ verifyPosition(decoder, text(
+ "002017297899,190216202500,0,190216202459,014221890,46492170,3,0,0,6,,,1034,43841,,0000,00,0,209,0,0407"));
+
+ verifyPosition(decoder, text(
+ "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"),
+ position("2013-06-27 04:16:52.000", true, 47.53410, 16.66530));
+
+ verifyPosition(decoder, text(
+ "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/TelikProtocolDecoderTest.java b/test/org/traccar/protocol/TelikProtocolDecoderTest.java
deleted file mode 100644
index 180ab1b3d..000000000
--- a/test/org/traccar/protocol/TelikProtocolDecoderTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.traccar.protocol;
-
-import org.junit.Test;
-import org.traccar.ProtocolTest;
-
-public class TelikProtocolDecoderTest extends ProtocolTest {
-
- @Test
- public void testDecode() throws Exception {
-
- TelikProtocolDecoder decoder = new TelikProtocolDecoder(new TelikProtocol());
-
- verifyNothing(decoder, text(
- "0026436729|232|01|003002030"));
-
- verifyPosition(decoder, text(
- "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"),
- position("2013-06-27 04:16:52.000", true, 47.53410, 16.66530));
-
- verifyPosition(decoder, text(
- "182043672999,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7"));
-
- }
-
-}
diff --git a/test/org/traccar/protocol/TotemProtocolDecoderTest.java b/test/org/traccar/protocol/TotemProtocolDecoderTest.java
index de554b7bc..237e77612 100644
--- a/test/org/traccar/protocol/TotemProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/TotemProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class TotemProtocolDecoderTest extends ProtocolTest {
TotemProtocolDecoder decoder = new TotemProtocolDecoder(new TotemProtocol());
+ verifyPosition(decoder, text(
+ "$$0108AA863835024426319|18004000160216160756411100007DCD0000111000000000.800000000316.3519N10228.5086E126522"));
+
// $$0128AA867521029231005|1880100015101802314842140000000000000000000000001AB48366093127600000.900000000806.1947N09818.4795E080355
verifyPosition(decoder, text(
diff --git a/test/org/traccar/protocol/Tt8850ProtocolDecoderTest.java b/test/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
new file mode 100644
index 000000000..b2b7c4fe5
--- /dev/null
+++ b/test/org/traccar/protocol/Tt8850ProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Tt8850ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Tt8850ProtocolDecoder decoder = new Tt8850ProtocolDecoder(new Tt8850Protocol());
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTFRI,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTGEO,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,005F,0,GTNMR,020100,135790246811220,,0,0,1,1,4.3,92,70.0,121.354335,31.222073,20090214013254,0460,0000,18d8,6141,90,20090214093254,11F0"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,0017,0,GTNMR,,867844000400914,,0,41,1,2,0.0,0,1504.2,-75.569202,6.242850,20150404162835,,,,,97,20150404162836,05EF"));
+
+ verifyNothing(decoder, text(
+ "\u0000\u0004,0017,0,GTPNA,,867844000400914,,0,0,1,0,,,,0,0,,,,,,99,20150404190153,0601"));
+
+ verifyPosition(decoder, text(
+ "\u0000\u0004,0017,0,GTEPN,,867844000400914,,0,0,1,0,0.0,0,1717.4,-75.598445,6.278578,20150405003116,,,,,95,20150405003358,0607"));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java
index e87e95efe..edee9930d 100644
--- a/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class UlbotechProtocolDecoderTest extends ProtocolTest {
UlbotechProtocolDecoder decoder = new UlbotechProtocolDecoder(new UlbotechProtocol());
+ verifyPosition(decoder, binary(
+ "f8010103515790566431569e5fbb9d010e015ee2b906bde4a000000000009f03040a4000000404000115fe05060340173f22030711310583410c0000310d00312f834131000008040000b78c09077320290082c021100101120af8"));
+
verifyNothing(decoder, binary(
"2a545330312c33353430343330353133383934363023"));
diff --git a/test/org/traccar/protocol/XexunProtocolDecoderTest.java b/test/org/traccar/protocol/XexunProtocolDecoderTest.java
index 76c516caf..ac90daec5 100644
--- a/test/org/traccar/protocol/XexunProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/XexunProtocolDecoderTest.java
@@ -12,6 +12,12 @@ public class XexunProtocolDecoderTest extends ProtocolTest {
XexunProtocolDecoder decoder = new XexunProtocolDecoder(new XexunProtocol(), false);
verifyPosition(decoder, text(
+ "GPRMC,113518.000,A,5303.4150,N,10.2368,E,60.73,207.42,260216,00,0000.0,A*74,F,,imei:351525018007873,"));
+
+ verifyPosition(decoder, text(
+ "GPRMC,215853.000,A,5304.9600,N,6.7907,E,1.43,80.67,250216,00,0000.0,A*47,F,,imei:351525018007873,"));
+
+ verifyPosition(decoder, text(
"GPRMC,121535.000,A,5417.2666,N,04822.1264,E,1.452,30.42,031014,0.0,A*4D\r\n,L,imei:355227042011730,"));
verifyPosition(decoder, text(
diff --git a/tools/test-integration.py b/tools/test-integration.py
index 928464895..3f1f519be 100755
--- a/tools/test-integration.py
+++ b/tools/test-integration.py
@@ -27,12 +27,11 @@ messages = {
'meitrack' : '$$d138,123456789012345,AAA,35,60.000000,130.000000,120101122000,A,7,18,0,0,0,49,3800,24965,510|10|0081|4F4F,0000,000D|0010|0012|0963|0000,,*BF\r\n',
'megastek' : 'STX,102110830074542,$GPRMC,122400.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,A*64,F,LowBattery,imei:123456789012345,03,113.1,Battery=24%,,1,460,01,2531,647E;57\r\n',
'gpsgate' : '$FRLIN,IMEI,123456789012345,*7B\r\n$GPRMC,122600.000,A,6000.00000,N,13000.00000,E,0.000,0.0,010112,,*0A\r\n',
- 'tlt2h' : '#123456789012345#V500#0000#AUTO#1\r\n#$GPRMC,123000.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,D*70\r\n##',
+ 'tlt2h' : '#123456789012345#V500#0000#AUTO#1\r\n#$GPRMC,123000.000,A,6000.0000,N,13000.0000,E,0.00,0.00,010112,,,D*70\r\n##\r\n',
'taip' : '>REV481669045060+6000000-1300000000000012;ID=123456789012345<',
'wondex' : '123456789012345,20120101123200,130.000000,60.000000,0,000,0,0,2\r\n',
'ywt' : '%RP,123456789012345:0,120101123500,E130.000000,N60.000000,,0,0,4,0,00\r\n',
'tk102' : '[!0000000081r(123456789012345,TK102-W998_01_V1.1.001_130219,255,001,255,001,0,100,100,0,internet,0000,0000,0,0,255,0,4,1,11,00)][=00000000836(ITV123600A6000.0000N13000.0000E000.00001011210010000)]',
- 'intellitrac' : '123456789012345,20120101123700,130.000000,60.000000,0,0,0,7,0,11,15\r\n',
'wialon' : '#L#123456789012345;test\r\n#SD#010112;123900;6000.0000;N;13000.0000;E;0;0;0;4\r\n',
'carscop' : '*040331141830UB05123456789012345010112A6000.0000N13000.0000E000.0124000000.0000000000L000000^',
'manpower' : 'simei:123456789012345,,,tracker,51,24,1.73,120101124200,A,6000.0000,N,13000.0000,E,0.00,28B9,1DED,425,01,1x0x0*0x1*60x+2,en-us,;',
@@ -50,7 +49,7 @@ messages = {
'haicom' : '$GPRS123456789012345,T100001,150618,230031,5402267400332464,0004,2014,000001,,,1,00#V040*',
'box' : 'H,BT,123456789012345,081028142432,F5813D19,6D6E6DC2\rL,081028142429,G,52.51084,-1.70849,0,170,0,1,0\r',
'freedom' : 'IMEI,123456789012345,2014/05/22, 20:49:32, N, Lat:4725.9624, E, Lon:01912.5483, Spd:5.05\r\n',
- 'telik' : '182012345699,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7\0',
+ 'telic' : '182012345699,010100001301,0,270613041652,166653,475341,3,0,355,6,2,1,231,8112432,23201,01,00,217,0,0,0,0,7\0',
'trackbox' : 'a=connect&v=11&i=123456789012345\r\n183457.999,5126.0247N,00002.8686E,5.2,70.4,3,57.63,32.11,17.32,150507,05\r\n',
'visiontek' : '$1,AP09BU9397,123456789012345,20,06,14,15,03,28,17267339N,078279407E,060.0,073,0550,11,0,1,0,0,1,1,26,A,0000000000#',
'tr900' : '>123456,4,1,150626,131252,W05830.2978,S3137.2783,,00,348,18,00,003-000,0,3,11111011*3b!\r\n',
diff --git a/web/app.css b/web/app.css
index eb0fdf136..0dbf94ca6 100644
--- a/web/app.css
+++ b/web/app.css
@@ -8,6 +8,10 @@
background-color: rgba(255, 84, 104, 0.3);
}
+.x-tree-icon {
+ display: none !important;
+}
+
.state-indicator {
position: absolute;
top: -999em;
diff --git a/web/app/Application.js b/web/app/Application.js
index fc4344a08..69ce8f891 100644
--- a/web/app/Application.js
+++ b/web/app/Application.js
@@ -26,6 +26,7 @@ Ext.define('Traccar.Application', {
models: [
'Server',
'User',
+ 'Group',
'Device',
'Position',
'Attribute',
@@ -33,7 +34,9 @@ Ext.define('Traccar.Application', {
],
stores: [
+ 'Groups',
'Devices',
+ 'AllGroups',
'AllDevices',
'Positions',
'LatestPositions',
diff --git a/web/app/controller/Root.js b/web/app/controller/Root.js
index 23ca94972..5bd567619 100644
--- a/web/app/controller/Root.js
+++ b/web/app/controller/Root.js
@@ -73,6 +73,7 @@ Ext.define('Traccar.controller.Root', {
},
loadApp: function () {
+ Ext.getStore('Groups').load();
Ext.getStore('Devices').load();
Ext.get('attribution').remove();
if (this.isPhone) {
@@ -86,7 +87,7 @@ Ext.define('Traccar.controller.Root', {
asyncUpdate: function (first) {
var protocol, socket, self = this;
protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
- socket = new WebSocket(protocol + window.location.host + '/api/socket');
+ socket = new WebSocket(protocol + '//' + window.location.host + '/api/socket');
socket.onclose = function (event) {
self.asyncUpdate(false);
diff --git a/web/app/model/Device.js b/web/app/model/Device.js
index b8434d6ad..588d53c1f 100644
--- a/web/app/model/Device.js
+++ b/web/app/model/Device.js
@@ -33,6 +33,9 @@ Ext.define('Traccar.model.Device', {
}, {
name: 'lastUpdate',
type: 'date',
- dateWriteFormat: 'c'
+ dateFormat: 'c'
+ }, {
+ name: 'groupId',
+ type: 'int'
}]
});
diff --git a/web/app/model/Group.js b/web/app/model/Group.js
new file mode 100644
index 000000000..a28897feb
--- /dev/null
+++ b/web/app/model/Group.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.model.Group', {
+ extend: 'Ext.data.Model',
+ identifier: 'negative',
+
+ fields: [{
+ name: 'id',
+ type: 'int'
+ }, {
+ name: 'name',
+ type: 'string'
+ }, {
+ name: 'groupId',
+ type: 'int'
+ }]
+});
diff --git a/web/app/model/Position.js b/web/app/model/Position.js
index 365b06115..e559a7eab 100644
--- a/web/app/model/Position.js
+++ b/web/app/model/Position.js
@@ -29,13 +29,16 @@ Ext.define('Traccar.model.Position', {
type: 'int'
}, {
name: 'serverTime',
- type: 'date'
+ type: 'date',
+ dateFormat: 'c'
}, {
name: 'deviceTime',
- type: 'date'
+ type: 'date',
+ dateFormat: 'c'
}, {
name: 'fixTime',
- type: 'date'
+ type: 'date',
+ dateFormat: 'c'
}, {
name: 'valid',
type: 'boolean'
diff --git a/web/app/store/AllGroups.js b/web/app/store/AllGroups.js
new file mode 100644
index 000000000..880ccc8f5
--- /dev/null
+++ b/web/app/store/AllGroups.js
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.store.AllGroups', {
+ extend: 'Ext.data.Store',
+ model: 'Traccar.model.Group',
+
+ proxy: {
+ type: 'rest',
+ url: '/api/groups',
+ extraParams: {
+ all: true
+ }
+ }
+});
diff --git a/web/app/store/Groups.js b/web/app/store/Groups.js
new file mode 100644
index 000000000..938abed64
--- /dev/null
+++ b/web/app/store/Groups.js
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.store.Groups', {
+ extend: 'Ext.data.Store',
+ model: 'Traccar.model.Group',
+
+ proxy: {
+ type: 'rest',
+ url: '/api/groups',
+ writer: {
+ writeAllFields: true
+ }
+ }
+});
diff --git a/web/app/view/DeviceDialog.js b/web/app/view/DeviceDialog.js
index c42af95d0..4a22ca008 100644
--- a/web/app/view/DeviceDialog.js
+++ b/web/app/view/DeviceDialog.js
@@ -29,13 +29,20 @@ Ext.define('Traccar.view.DeviceDialog', {
items: [{
xtype: 'textfield',
name: 'name',
- fieldLabel: Strings.deviceName,
+ fieldLabel: Strings.sharedName,
allowBlank: false
}, {
xtype: 'textfield',
name: 'uniqueId',
fieldLabel: Strings.deviceIdentifier,
allowBlank: false
+ }, {
+ xtype: 'combobox',
+ name: 'groupId',
+ fieldLabel: Strings.groupParent,
+ store: 'Groups',
+ displayField: 'name',
+ valueField: 'id'
}]
}
});
diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js
index 66c4e813b..ebe3ca195 100644
--- a/web/app/view/Devices.js
+++ b/web/app/view/Devices.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
Ext.define('Traccar.view.Devices', {
- extend: 'Ext.grid.Panel',
+ extend: 'Ext.tree.Panel',
xtype: 'devicesView',
requires: [
@@ -25,7 +25,17 @@ Ext.define('Traccar.view.Devices', {
],
controller: 'devices',
- store: 'Devices',
+ rootVisible: false,
+ store: {
+ type: 'tree',
+ parentIdProperty: 'groupId',
+ proxy: {
+ type: 'memory',
+ reader: {
+ type: 'json'
+ }
+ }
+ },
title: Strings.deviceTitle,
selType: 'rowmodel',
@@ -53,11 +63,13 @@ Ext.define('Traccar.view.Devices', {
},
listeners: {
- selectionchange: 'onSelectionChange'
+ selectionchange: 'onSelectionChange',
+ beforeselect: 'onBeforeSelect'
},
columns: [{
- text: Strings.deviceName,
+ xtype: 'treecolumn',
+ text: Strings.sharedName,
dataIndex: 'name',
flex: 1
}, {
@@ -65,19 +77,20 @@ Ext.define('Traccar.view.Devices', {
dataIndex: 'lastUpdate',
flex: 1,
renderer: function (value, metaData, record) {
- var status = record.get('status');
- switch (status) {
- case 'online':
- metaData.tdCls = 'status-color-online';
- break;
- case 'offline':
- metaData.tdCls = 'status-color-offline';
- break;
- default:
- metaData.tdCls = 'status-color-unknown';
- break;
+ if (record.get('leaf')) {
+ switch (record.get('status')) {
+ case 'online':
+ metaData.tdCls = 'status-color-online';
+ break;
+ case 'offline':
+ metaData.tdCls = 'status-color-offline';
+ break;
+ default:
+ metaData.tdCls = 'status-color-unknown';
+ break;
+ }
+ return Ext.Date.format(value, Traccar.Style.dateTimeFormat);
}
- return Ext.Date.format(value, Traccar.Style.dateTimeFormat);
}
}]
diff --git a/web/app/view/DevicesController.js b/web/app/view/DevicesController.js
index 8c6c72725..4d8231d94 100644
--- a/web/app/view/DevicesController.js
+++ b/web/app/view/DevicesController.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,9 +30,54 @@ Ext.define('Traccar.view.DevicesController', {
selectDevice: 'selectDevice',
selectReport: 'selectReport'
}
+ },
+ store: {
+ '#Groups': {
+ datachanged: 'storeUpdate',
+ update: 'storeUpdate'
+ },
+ '#Devices': {
+ datachanged: 'storeUpdate',
+ update: 'storeUpdate'
+ }
}
}
},
+
+ storeUpdate: function () {
+ var nodes = [];
+ Ext.getStore('Groups').each(function (record) {
+ var groupId, node = {
+ id: 'g' + record.get('id'),
+ original: record,
+ name: record.get('name')
+ };
+ groupId = record.get('groupId');
+ if (groupId !== 0) {
+ node.groupId = 'g' + groupId;
+ }
+ nodes.push(node);
+ }, this);
+ Ext.getStore('Devices').each(function (record) {
+ var groupId, node = {
+ id: 'd' + record.get('id'),
+ original: record,
+ name: record.get('name'),
+ status: record.get('status'),
+ lastUpdate: record.get('lastUpdate'),
+ leaf: true
+ };
+ groupId = record.get('groupId');
+ if (groupId !== 0) {
+ node.groupId = 'g' + groupId;
+ }
+ nodes.push(node);
+ }, this);
+ this.getView().getStore().getProxy().setData(nodes);
+ this.getView().getStore().load();
+ this.getView().expandAll();
+ },
+
init: function () {
var readonly = Traccar.app.getServer().get('readonly') && !Traccar.app.getUser().get('admin');
this.lookupReference('toolbarAddButton').setVisible(!readonly);
@@ -43,7 +88,7 @@ Ext.define('Traccar.view.DevicesController', {
onAddClick: function () {
var device, dialog;
device = Ext.create('Traccar.model.Device');
- device.store = this.getView().getStore();
+ device.store = Ext.getStore('Devices');
dialog = Ext.create('Traccar.view.DeviceDialog');
dialog.down('form').loadRecord(device);
dialog.show();
@@ -51,14 +96,14 @@ Ext.define('Traccar.view.DevicesController', {
onEditClick: function () {
var device, dialog;
- device = this.getView().getSelectionModel().getSelection()[0];
+ device = this.getView().getSelectionModel().getSelection()[0].get('original');
dialog = Ext.create('Traccar.view.DeviceDialog');
dialog.down('form').loadRecord(device);
dialog.show();
},
onRemoveClick: function () {
- var device = this.getView().getSelectionModel().getSelection()[0];
+ var device = this.getView().getSelectionModel().getSelection()[0].get('original');
Ext.Msg.show({
title: Strings.deviceDialog,
message: Strings.sharedRemoveConfirm,
@@ -80,7 +125,7 @@ Ext.define('Traccar.view.DevicesController', {
onCommandClick: function () {
var device, command, dialog;
- device = this.getView().getSelectionModel().getSelection()[0];
+ device = this.getView().getSelectionModel().getSelection()[0].get('original');
command = Ext.create('Traccar.model.Command');
command.set('deviceId', device.get('id'));
dialog = Ext.create('Traccar.view.CommandDialog');
@@ -89,17 +134,22 @@ Ext.define('Traccar.view.DevicesController', {
},
onSelectionChange: function (selected) {
- var empty = selected.getCount() === 0;
+ var empty = selected.getCount() === 0 || !this.getView().getSelectionModel().getSelection()[0].get('leaf');
this.lookupReference('toolbarEditButton').setDisabled(empty);
this.lookupReference('toolbarRemoveButton').setDisabled(empty);
this.lookupReference('deviceCommandButton').setDisabled(empty);
if (!empty) {
- this.fireEvent('selectDevice', selected.getLastSelected(), true);
+ this.fireEvent('selectDevice', selected.getLastSelected().get('original'), true);
}
},
+ onBeforeSelect: function (row, record) {
+ return record.get('leaf');
+ },
+
selectDevice: function (device, center) {
- this.getView().getSelectionModel().select([device], false, true);
+ var node = this.getView().getStore().getNodeById('d' + device.get('id'));
+ this.getView().getSelectionModel().select([node], false, true);
},
selectReport: function (position) {
diff --git a/web/app/view/GroupDialog.js b/web/app/view/GroupDialog.js
new file mode 100644
index 000000000..2cca61ef5
--- /dev/null
+++ b/web/app/view/GroupDialog.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.view.GroupDialog', {
+ extend: 'Traccar.view.BaseEditDialog',
+
+ requires: [
+ 'Traccar.view.BaseEditDialogController'
+ ],
+
+ controller: 'baseEditDialog',
+ title: Strings.groupDialog,
+
+ items: {
+ xtype: 'form',
+ items: [{
+ xtype: 'textfield',
+ name: 'name',
+ fieldLabel: Strings.sharedName,
+ allowBlank: false
+ }, {
+ xtype: 'combobox',
+ name: 'groupId',
+ fieldLabel: Strings.groupParent,
+ store: 'Groups',
+ displayField: 'name',
+ valueField: 'id'
+ }]
+ }
+});
diff --git a/web/app/view/Groups.js b/web/app/view/Groups.js
new file mode 100644
index 000000000..8404c59a9
--- /dev/null
+++ b/web/app/view/Groups.js
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.view.Groups', {
+ extend: 'Ext.grid.Panel',
+ xtype: 'groupsView',
+
+ requires: [
+ 'Traccar.view.GroupsController',
+ 'Traccar.view.EditToolbar'
+ ],
+
+ controller: 'groups',
+ store: 'Groups',
+
+ selType: 'rowmodel',
+
+ tbar: {
+ xtype: 'editToolbar'
+ },
+
+ listeners: {
+ selectionchange: 'onSelectionChange'
+ },
+
+ columns: [{
+ text: Strings.sharedName,
+ dataIndex: 'name',
+ flex: 1
+ }]
+});
diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js
new file mode 100644
index 000000000..6cc568ea2
--- /dev/null
+++ b/web/app/view/GroupsController.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.view.GroupsController', {
+ extend: 'Ext.app.ViewController',
+ alias: 'controller.groups',
+
+ onAddClick: function () {
+ var group, dialog;
+ group = Ext.create('Traccar.model.Group');
+ group.store = this.getView().getStore();
+ dialog = Ext.create('Traccar.view.GroupDialog');
+ dialog.down('form').loadRecord(group);
+ dialog.show();
+ },
+
+ onEditClick: function () {
+ var group, dialog;
+ group = this.getView().getSelectionModel().getSelection()[0];
+ dialog = Ext.create('Traccar.view.GroupDialog');
+ dialog.down('form').loadRecord(group);
+ dialog.show();
+ },
+
+ onRemoveClick: function () {
+ var group = this.getView().getSelectionModel().getSelection()[0];
+ Ext.Msg.show({
+ title: Strings.groupDialog,
+ message: Strings.sharedRemoveConfirm,
+ buttons: Ext.Msg.YESNO,
+ buttonText: {
+ yes: Strings.sharedRemove,
+ no: Strings.sharedCancel
+ },
+ fn: function (btn) {
+ var store = Ext.getStore('Groups');
+ if (btn === 'yes') {
+ store.remove(group);
+ store.sync();
+ }
+ }
+ });
+ },
+
+ onSelectionChange: function (selected) {
+ var disabled = selected.length > 0;
+ this.lookupReference('toolbarEditButton').setDisabled(disabled);
+ this.lookupReference('toolbarRemoveButton').setDisabled(disabled);
+ }
+});
diff --git a/web/app/view/MapController.js b/web/app/view/MapController.js
index ce420c2ec..918f81390 100644
--- a/web/app/view/MapController.js
+++ b/web/app/view/MapController.js
@@ -113,29 +113,31 @@ Ext.define('Traccar.view.MapController', {
deviceId = position.get('deviceId');
device = Ext.getStore('Devices').findRecord('id', deviceId, 0, false, false, true);
- geometry = new ol.geom.Point(ol.proj.fromLonLat([
- position.get('longitude'),
- position.get('latitude')
- ]));
-
- if (deviceId in this.latestMarkers) {
- marker = this.latestMarkers[deviceId];
- marker.setGeometry(geometry);
- } else {
- marker = new ol.Feature(geometry);
- marker.set('record', device);
- this.latestMarkers[deviceId] = marker;
- this.getView().getLatestSource().addFeature(marker);
-
- style = this.getLatestMarker(this.getDeviceColor(device));
- style.getText().setText(device.get('name'));
- marker.setStyle(style);
- }
+ if (device) {
+ geometry = new ol.geom.Point(ol.proj.fromLonLat([
+ position.get('longitude'),
+ position.get('latitude')
+ ]));
+
+ if (deviceId in this.latestMarkers) {
+ marker = this.latestMarkers[deviceId];
+ marker.setGeometry(geometry);
+ } else {
+ marker = new ol.Feature(geometry);
+ marker.set('record', device);
+ this.latestMarkers[deviceId] = marker;
+ this.getView().getLatestSource().addFeature(marker);
+
+ style = this.getLatestMarker(this.getDeviceColor(device));
+ style.getText().setText(device.get('name'));
+ marker.setStyle(style);
+ }
- marker.getStyle().getImage().setRotation(position.get('course') * Math.PI / 180);
+ marker.getStyle().getImage().setRotation(position.get('course') * Math.PI / 180);
- if (marker === this.selectedMarker && this.followSelected()) {
- this.getView().getMapView().setCenter(marker.getGeometry().getCoordinates());
+ if (marker === this.selectedMarker && this.followSelected()) {
+ this.getView().getMapView().setCenter(marker.getGeometry().getCoordinates());
+ }
}
}
},
diff --git a/web/app/view/Register.js b/web/app/view/Register.js
index 7c2881d62..198e10b8f 100644
--- a/web/app/view/Register.js
+++ b/web/app/view/Register.js
@@ -33,7 +33,7 @@ Ext.define('Traccar.view.Register', {
items: [{
xtype: 'textfield',
name: 'name',
- fieldLabel: Strings.userName,
+ fieldLabel: Strings.sharedName,
allowBlank: false
}, {
xtype: 'textfield',
diff --git a/web/app/view/SettingsMenu.js b/web/app/view/SettingsMenu.js
index c828b37af..1ada94359 100644
--- a/web/app/view/SettingsMenu.js
+++ b/web/app/view/SettingsMenu.js
@@ -32,6 +32,9 @@ Ext.define('Traccar.view.SettingsMenu', {
text: Strings.settingsUser,
handler: 'onUserClick'
}, {
+ text: Strings.settingsGroups,
+ handler: 'onGroupsClick'
+ }, {
text: Strings.settingsServer,
hidden: true,
handler: 'onServerClick',
diff --git a/web/app/view/SettingsMenuController.js b/web/app/view/SettingsMenuController.js
index c52f0a75e..6d767e3a9 100644
--- a/web/app/view/SettingsMenuController.js
+++ b/web/app/view/SettingsMenuController.js
@@ -23,6 +23,7 @@ Ext.define('Traccar.view.SettingsMenuController', {
'Traccar.view.UserDialog',
'Traccar.view.ServerDialog',
'Traccar.view.Users',
+ 'Traccar.view.Groups',
'Traccar.view.BaseWindow'
],
@@ -39,6 +40,16 @@ Ext.define('Traccar.view.SettingsMenuController', {
dialog.show();
},
+ onGroupsClick: function () {
+ Ext.create('Traccar.view.BaseWindow', {
+ title: Strings.settingsGroups,
+ modal: false,
+ items: {
+ xtype: 'groupsView'
+ }
+ }).show();
+ },
+
onServerClick: function () {
var dialog = Ext.create('Traccar.view.ServerDialog');
dialog.down('form').loadRecord(Traccar.app.getServer());
diff --git a/web/app/view/UserDevices.js b/web/app/view/UserDevices.js
index f9ab48266..fe16dd93a 100644
--- a/web/app/view/UserDevices.js
+++ b/web/app/view/UserDevices.js
@@ -37,10 +37,12 @@ Ext.define('Traccar.view.UserDevices', {
},
columns: [{
- text: Strings.deviceName,
- dataIndex: 'name', flex: 1
+ text: Strings.sharedName,
+ dataIndex: 'name',
+ flex: 1
}, {
text: Strings.deviceIdentifier,
- dataIndex: 'uniqueId', flex: 1
+ dataIndex: 'uniqueId',
+ flex: 1
}]
});
diff --git a/web/app/view/UserDevicesController.js b/web/app/view/UserDevicesController.js
index 4f013fd64..e5dbbdbb8 100644
--- a/web/app/view/UserDevicesController.js
+++ b/web/app/view/UserDevicesController.js
@@ -47,7 +47,7 @@ Ext.define('Traccar.view.UserDevicesController', {
onBeforeSelect: function (object, record, index) {
Ext.Ajax.request({
scope: this,
- url: '/api/permissions',
+ url: '/api/permissions/devices',
jsonData: {
userId: this.userId,
deviceId: record.getData().id
@@ -64,7 +64,7 @@ Ext.define('Traccar.view.UserDevicesController', {
Ext.Ajax.request({
scope: this,
method: 'DELETE',
- url: '/api/permissions',
+ url: '/api/permissions/devices',
jsonData: {
userId: this.userId,
deviceId: record.getData().id
diff --git a/web/app/view/UserDialog.js b/web/app/view/UserDialog.js
index 783ddd159..c1ed2fece 100644
--- a/web/app/view/UserDialog.js
+++ b/web/app/view/UserDialog.js
@@ -30,7 +30,7 @@ Ext.define('Traccar.view.UserDialog', {
items: [{
xtype: 'textfield',
name: 'name',
- fieldLabel: Strings.userName
+ fieldLabel: Strings.sharedName
}, {
xtype: 'textfield',
name: 'email',
diff --git a/web/app/view/UserGroups.js b/web/app/view/UserGroups.js
new file mode 100644
index 000000000..cb0f0bd5d
--- /dev/null
+++ b/web/app/view/UserGroups.js
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.view.UserGroups', {
+ extend: 'Ext.grid.Panel',
+ xtype: 'userGroupsView',
+
+ requires: [
+ 'Traccar.view.UserGroupsController'
+ ],
+
+ controller: 'userGroups',
+ store: 'AllGroups',
+
+ selModel: {
+ selType: 'checkboxmodel',
+ checkOnly: true,
+ showHeaderCheckbox: false
+ },
+
+ listeners: {
+ beforedeselect: 'onBeforeDeselect',
+ beforeselect: 'onBeforeSelect'
+ },
+
+ columns: [{
+ text: Strings.sharedName,
+ dataIndex: 'name',
+ flex: 1
+ }]
+});
diff --git a/web/app/view/UserGroupsController.js b/web/app/view/UserGroupsController.js
new file mode 100644
index 000000000..662508f0a
--- /dev/null
+++ b/web/app/view/UserGroupsController.js
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@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.
+ */
+
+Ext.define('Traccar.view.UserGroupsController', {
+ extend: 'Ext.app.ViewController',
+ alias: 'controller.userGroups',
+
+ init: function () {
+ this.userId = this.getView().user.getData().id;
+ this.getView().getStore().load({
+ scope: this,
+ callback: function (records, operation, success) {
+ var userStore = Ext.create('Traccar.store.Groups');
+
+ userStore.load({
+ params: {
+ userId: this.userId
+ },
+ scope: this,
+ callback: function (records, operation, success) {
+ var i, index;
+ if (success) {
+ for (i = 0; i < records.length; i++) {
+ index = this.getView().getStore().find('id', records[i].getData().id);
+ this.getView().getSelectionModel().select(index, true, true);
+ }
+ }
+ }
+ });
+ }
+ });
+ },
+
+ onBeforeSelect: function (object, record, index) {
+ Ext.Ajax.request({
+ scope: this,
+ url: '/api/permissions/groups',
+ jsonData: {
+ userId: this.userId,
+ groupId: record.getData().id
+ },
+ callback: function (options, success, response) {
+ if (!success) {
+ Traccar.app.showError(response);
+ }
+ }
+ });
+ },
+
+ onBeforeDeselect: function (object, record, index) {
+ Ext.Ajax.request({
+ scope: this,
+ method: 'DELETE',
+ url: '/api/permissions/groups',
+ jsonData: {
+ userId: this.userId,
+ groupId: record.getData().id
+ },
+ callback: function (options, success, response) {
+ if (!success) {
+ Traccar.app.showError(response);
+ }
+ }
+ });
+ }
+});
diff --git a/web/app/view/Users.js b/web/app/view/Users.js
index f4ef332b4..408a70885 100644
--- a/web/app/view/Users.js
+++ b/web/app/view/Users.js
@@ -35,6 +35,11 @@ Ext.define('Traccar.view.Users', {
disabled: true,
handler: 'onDevicesClick',
reference: 'userDevicesButton'
+ }, {
+ text: Strings.settingsGroups,
+ disabled: true,
+ handler: 'onGroupsClick',
+ reference: 'userGroupsButton'
}]
},
@@ -43,7 +48,7 @@ Ext.define('Traccar.view.Users', {
},
columns: [{
- text: Strings.userName,
+ text: Strings.sharedName,
dataIndex: 'name',
flex: 1
}, {
diff --git a/web/app/view/UsersController.js b/web/app/view/UsersController.js
index 3d0e813e8..c48f57cf4 100644
--- a/web/app/view/UsersController.js
+++ b/web/app/view/UsersController.js
@@ -21,6 +21,7 @@ Ext.define('Traccar.view.UsersController', {
requires: [
'Traccar.view.UserDialog',
'Traccar.view.UserDevices',
+ 'Traccar.view.UserGroups',
'Traccar.view.BaseWindow'
],
@@ -75,10 +76,22 @@ Ext.define('Traccar.view.UsersController', {
}).show();
},
+ onGroupsClick: function () {
+ var user = this.getView().getSelectionModel().getSelection()[0];
+ Ext.create('Traccar.view.BaseWindow', {
+ title: Strings.settingsGroups,
+ items: {
+ xtype: 'userGroupsView',
+ user: user
+ }
+ }).show();
+ },
+
onSelectionChange: function (selected) {
var disabled = selected.length > 0;
this.lookupReference('toolbarEditButton').setDisabled(disabled);
this.lookupReference('toolbarRemoveButton').setDisabled(disabled);
this.lookupReference('userDevicesButton').setDisabled(disabled);
+ this.lookupReference('userGroupsButton').setDisabled(disabled);
}
});
diff --git a/web/arrowstyle.js b/web/arrowstyle.js
index c1eb88909..ad0ba08ee 100644
--- a/web/arrowstyle.js
+++ b/web/arrowstyle.js
@@ -1,12 +1,10 @@
goog.provide('ol.style.Arrow');
goog.require('goog.asserts');
-goog.require('goog.dom');
goog.require('ol');
goog.require('ol.color');
goog.require('ol.has');
goog.require('ol.render.canvas');
-goog.require('ol.structs.IHasChecksum');
goog.require('ol.style.AtlasManager');
goog.require('ol.style.Fill');
goog.require('ol.style.Image');
@@ -22,7 +20,6 @@ goog.require('ol.style.Stroke');
* @constructor
* @param {olx.style.ArrowOptions} options Options.
* @extends {ol.style.Image}
- * @implements {ol.structs.IHasChecksum}
* @api
*/
ol.style.Arrow = function(options) {
@@ -119,9 +116,15 @@ ol.style.Arrow = function(options) {
var snapToPixel = options.snapToPixel !== undefined ?
options.snapToPixel : true;
+ /**
+ * @type {boolean}
+ */
+ var rotateWithView = options.rotateWithView !== undefined ?
+ options.rotateWithView : false;
+
goog.base(this, {
opacity: 1,
- rotateWithView: false,
+ rotateWithView: rotateWithView,
rotation: options.rotation !== undefined ? options.rotation : 0,
scale: 1,
snapToPixel: snapToPixel
@@ -283,7 +286,7 @@ ol.style.Arrow.RenderOptions;
/**
* @private
- * @param {ol.style.AtlasManager|undefined} atlasManager
+ * @param {ol.style.AtlasManager|undefined} atlasManager An atlas manager.
*/
ol.style.Arrow.prototype.render_ = function(atlasManager) {
var imageSize;
@@ -334,7 +337,7 @@ ol.style.Arrow.prototype.render_ = function(atlasManager) {
if (atlasManager === undefined) {
// no atlas manager is used, create a new canvas
this.canvas_ = /** @type {HTMLCanvasElement} */
- (goog.dom.createElement('CANVAS'));
+ (document.createElement('CANVAS'));
this.canvas_.height = size;
this.canvas_.width = size;
@@ -357,12 +360,12 @@ ol.style.Arrow.prototype.render_ = function(atlasManager) {
if (hasCustomHitDetectionImage) {
// render the hit-detection image into a separate atlas image
renderHitDetectionCallback =
- goog.bind(this.drawHitDetectionCanvas_, this, renderOptions);
+ this.drawHitDetectionCanvas_.bind(this, renderOptions);
}
var id = this.getChecksum();
var info = atlasManager.add(
- id, size, size, goog.bind(this.draw_, this, renderOptions),
+ id, size, size, this.draw_.bind(this, renderOptions),
renderHitDetectionCallback);
goog.asserts.assert(info, 'arrow size is too large');
@@ -388,8 +391,8 @@ ol.style.Arrow.prototype.render_ = function(atlasManager) {
/**
* @private
- * @param {ol.style.Arrow.RenderOptions} renderOptions
- * @param {CanvasRenderingContext2D} context
+ * @param {ol.style.Arrow.RenderOptions} renderOptions Render options.
+ * @param {CanvasRenderingContext2D} context The rendering context.
* @param {number} x The origin for the symbol (x).
* @param {number} y The origin for the symbol (y).
*/
@@ -419,7 +422,7 @@ ol.style.Arrow.prototype.draw_ = function(renderOptions, context, x, y) {
lineTo(this.radius_, 0);
if (this.fill_) {
- context.fillStyle = ol.color.asString(this.fill_.getColor());
+ context.fillStyle = ol.colorlike.asColorLike(this.fill_.getColor());
context.fill();
}
if (this.stroke_) {
@@ -439,10 +442,9 @@ ol.style.Arrow.prototype.draw_ = function(renderOptions, context, x, y) {
/**
* @private
- * @param {ol.style.Arrow.RenderOptions} renderOptions
+ * @param {ol.style.Arrow.RenderOptions} renderOptions Render options.
*/
-ol.style.Arrow.prototype.createHitDetectionCanvas_ =
- function(renderOptions) {
+ol.style.Arrow.prototype.createHitDetectionCanvas_ = function(renderOptions) {
this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size];
if (this.fill_) {
this.hitDetectionCanvas_ = this.canvas_;
@@ -452,7 +454,7 @@ ol.style.Arrow.prototype.createHitDetectionCanvas_ =
// if no fill style is set, create an extra hit-detection image with a
// default fill style
this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */
- (goog.dom.createElement('CANVAS'));
+ (document.createElement('CANVAS'));
var canvas = this.hitDetectionCanvas_;
canvas.height = renderOptions.size;
@@ -466,13 +468,12 @@ ol.style.Arrow.prototype.createHitDetectionCanvas_ =
/**
* @private
- * @param {ol.style.Arrow.RenderOptions} renderOptions
- * @param {CanvasRenderingContext2D} context
+ * @param {ol.style.Arrow.RenderOptions} renderOptions Render options.
+ * @param {CanvasRenderingContext2D} context The context.
* @param {number} x The origin for the symbol (x).
* @param {number} y The origin for the symbol (y).
*/
-ol.style.Arrow.prototype.drawHitDetectionCanvas_ =
- function(renderOptions, context, x, y) {
+ol.style.Arrow.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) {
var innerRadius = this.radius_ / Math.sin(Math.PI - this.backAngle_ / 2) *
Math.sin(this.backAngle_ / 2 - this.frontAngle_);
@@ -511,7 +512,7 @@ ol.style.Arrow.prototype.drawHitDetectionCanvas_ =
/**
- * @inheritDoc
+ * @return {string} The checksum.
*/
ol.style.Arrow.prototype.getChecksum = function() {
var strokeChecksum = this.stroke_ ?
diff --git a/web/debug.html b/web/debug.html
index 3608930c0..351bb9f9f 100644
--- a/web/debug.html
+++ b/web/debug.html
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Traccar</title>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css">
-<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol.css">
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="stylesheet" href="app.css">
</head>
@@ -14,8 +14,11 @@
<div id="attribution">Powered by <a href="https://www.traccar.org/">Traccar GPS Tracking System</a></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all-debug.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/theme-neptune.js"></script>
-<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol-debug.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol-debug.js"></script>
<script src="arrowstyle.js"></script>
+<script type="text/javascript">
+Ext.Loader.setConfig({ disableCaching: false });
+</script>
<script src="locale.js"></script>
<script src="app.js"></script>
</body>
diff --git a/web/l10n/en.json b/web/l10n/en.json
index ec8018863..cabd76362 100644
--- a/web/l10n/en.json
+++ b/web/l10n/en.json
@@ -13,10 +13,10 @@
"sharedHour": "Hour",
"sharedMinute": "Minute",
"sharedSecond": "Second",
+ "sharedName": "Name",
"errorTitle": "Error",
"errorUnknown": "Unknown error",
"errorConnection": "Connection error",
- "userName": "Name",
"userEmail": "Email",
"userPassword": "Password",
"userAdmin": "Admin",
@@ -30,13 +30,15 @@
"devicesAndState": "Devices and State",
"deviceDialog": "Device",
"deviceTitle": "Devices",
- "deviceName": "Name",
"deviceIdentifier": "Identifier",
"deviceLastUpdate": "Last Update",
"deviceCommand": "Command",
"deviceFollow": "Follow",
+ "groupDialog": "Group",
+ "groupParent": "Group",
"settingsTitle": "Settings",
"settingsUser": "Account",
+ "settingsGroups": "Groups",
"settingsServer": "Server",
"settingsUsers": "Users",
"settingsDistanceUnit": "Distance",
diff --git a/web/l10n/es.json b/web/l10n/es.json
index 2ace91caf..7cca1c8f2 100644
--- a/web/l10n/es.json
+++ b/web/l10n/es.json
@@ -1,6 +1,6 @@
{
"sharedLoading": "Cargando...",
- "sharedSave": "Grabar",
+ "sharedSave": "Guardar",
"sharedCancel": "Cancelar",
"sharedAdd": "Agregar",
"sharedEdit": "Editar",
@@ -12,14 +12,14 @@
"sharedMph": "MPH",
"sharedHour": "Hora",
"sharedMinute": "Minuto",
- "sharedSecond": "Segundos",
+ "sharedSecond": "Segundo",
"errorTitle": "Error",
"errorUnknown": "Error Desconocido",
"errorConnection": "Error de Conexión",
"userName": "Nombre",
"userEmail": "Email",
"userPassword": "Contraseña",
- "userAdmin": "Admin",
+ "userAdmin": "Administrador",
"loginTitle": "Ingresar",
"loginLanguage": "Idioma",
"loginRegister": "Registrar",
@@ -27,14 +27,14 @@
"loginFailed": "Dirección de Correo o Contraseña Incorrecta",
"loginCreated": "Nuevo Usuario ha sido registrado",
"loginLogout": "Salir",
- "devicesAndState": "Devices and State",
+ "devicesAndState": "Dispositivos y Estado",
"deviceDialog": "Dispositivo",
"deviceTitle": "Dispositivos",
"deviceName": "Nombre",
"deviceIdentifier": "Identificador",
- "deviceLastUpdate": "Last Update",
+ "deviceLastUpdate": "Última Actualización",
"deviceCommand": "Comando",
- "deviceFollow": "Follow",
+ "deviceFollow": "Seguir",
"settingsTitle": "Preferencias",
"settingsUser": "Cuenta",
"settingsServer": "Servidor",
@@ -59,7 +59,7 @@
"serverTitle": "Preferencias Servidor",
"serverZoom": "Zoom",
"serverRegistration": "Registrar",
- "serverReadonly": "Readonly",
+ "serverReadonly": "Sólo Lectura",
"mapTitle": "Mapa",
"mapLayer": "Capa de Mapa",
"mapCustom": "Mapa Personalizado",
diff --git a/web/l10n/fa.json b/web/l10n/fa.json
new file mode 100644
index 000000000..3497dff23
--- /dev/null
+++ b/web/l10n/fa.json
@@ -0,0 +1,83 @@
+{
+ "sharedLoading": "در حال بارگزارى ...",
+ "sharedSave": "ذخيره",
+ "sharedCancel": "Cancel",
+ "sharedAdd": "اضافه كردن",
+ "sharedEdit": "Edit",
+ "sharedRemove": "Remove",
+ "sharedRemoveConfirm": "Remove item?",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Hour",
+ "sharedMinute": "دقيقه",
+ "sharedSecond": "ثانيه",
+ "errorTitle": "Error",
+ "errorUnknown": "Unknown error",
+ "errorConnection": "Connection error",
+ "userName": "نام",
+ "userEmail": "نام كاربرى ( ايميل )",
+ "userPassword": "گذرواژه",
+ "userAdmin": "Admin",
+ "loginTitle": "ورود",
+ "loginLanguage": "انتخاب زبان",
+ "loginRegister": "ثبت نام",
+ "loginLogin": "ورود",
+ "loginFailed": "نام كاربرى ( ايميل ) يا گذرواژه اشتباه است",
+ "loginCreated": "ثبت نام با موفقيت انجام شد",
+ "loginLogout": "خروج",
+ "devicesAndState": "Devices and State",
+ "deviceDialog": "دستگاه",
+ "deviceTitle": "دستگاه ها",
+ "deviceName": "نام خودرو",
+ "deviceIdentifier": "سريال دستگاه",
+ "deviceLastUpdate": "آخرين بروزرسانى",
+ "deviceCommand": "Command",
+ "deviceFollow": "Follow",
+ "settingsTitle": "تنظيمات",
+ "settingsUser": "حساب كاربرى",
+ "settingsServer": "Server",
+ "settingsUsers": "Users",
+ "settingsDistanceUnit": "Distance",
+ "settingsSpeedUnit": "سرعت",
+ "reportTitle": "گزارشات ",
+ "reportDevice": "دستگاه",
+ "reportFrom": "از",
+ "reportTo": "To",
+ "reportShow": "Show",
+ "reportClear": "Clear",
+ "positionFixTime": "Time",
+ "positionValid": "Valid",
+ "positionLatitude": "عرض جغرافيايى",
+ "positionLongitude": "طول جغرافيايى",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "سرعت",
+ "positionCourse": "Course",
+ "positionAddress": "Address",
+ "positionProtocol": "Protocol",
+ "serverTitle": "Server Settings",
+ "serverZoom": "Zoom",
+ "serverRegistration": "Registration",
+ "serverReadonly": "Readonly",
+ "mapTitle": "Map",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Custom Map",
+ "mapOsm": "Open Street Map",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "stateTitle": "State",
+ "stateName": "Attribute",
+ "stateValue": "Value",
+ "commandTitle": "Command",
+ "commandSend": "ارسال",
+ "commandType": "Type",
+ "commandSent": "Command has been sent",
+ "commandPositionPeriodic": "Periodic Reporting",
+ "commandPositionStop": "Stop Reporting",
+ "commandEngineStop": "Engine Stop",
+ "commandEngineResume": "Engine Resume",
+ "commandFrequency": "Frequency",
+ "commandUnit": "Unit"
+} \ No newline at end of file
diff --git a/web/l10n/fi.json b/web/l10n/fi.json
new file mode 100644
index 000000000..dc44cb03a
--- /dev/null
+++ b/web/l10n/fi.json
@@ -0,0 +1,83 @@
+{
+ "sharedLoading": "Ladataan...",
+ "sharedSave": "Tallenna",
+ "sharedCancel": "Peruuta",
+ "sharedAdd": "Lisää",
+ "sharedEdit": "Muokkaa",
+ "sharedRemove": "Poista",
+ "sharedRemoveConfirm": "Poista kohde?",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Tunti",
+ "sharedMinute": "Minuutti",
+ "sharedSecond": "Sekunti",
+ "errorTitle": "Virhe",
+ "errorUnknown": "Tuntematon virhe",
+ "errorConnection": "Yhteysvirhe",
+ "userName": "Nimi",
+ "userEmail": "Email",
+ "userPassword": "Salasana",
+ "userAdmin": "Ylläpito",
+ "loginTitle": "Kirjaudu",
+ "loginLanguage": "Kieli",
+ "loginRegister": "Rekisteröidy",
+ "loginLogin": "Kirjaudu",
+ "loginFailed": "Virheellinen email tai salasana",
+ "loginCreated": "Uusi käyttäjä on rekisteröitynyt",
+ "loginLogout": "Kirjaudu ulos",
+ "devicesAndState": "Laitteet ja Tilat",
+ "deviceDialog": "Laite",
+ "deviceTitle": "Laitteet",
+ "deviceName": "Nimi",
+ "deviceIdentifier": "Tunniste",
+ "deviceLastUpdate": "Viimeisin päivitys",
+ "deviceCommand": "Komento",
+ "deviceFollow": "Seuraa",
+ "settingsTitle": "Asetukset",
+ "settingsUser": "Tili",
+ "settingsServer": "Palvelin",
+ "settingsUsers": "Käyttäjät",
+ "settingsDistanceUnit": "Etäisyys",
+ "settingsSpeedUnit": "Nopeus",
+ "reportTitle": "Raportit",
+ "reportDevice": "Laite",
+ "reportFrom": "Mistä",
+ "reportTo": "Mihin",
+ "reportShow": "Näytä",
+ "reportClear": "Tyhjennä",
+ "positionFixTime": "Aika",
+ "positionValid": "Kelvollinen",
+ "positionLatitude": "Latitude",
+ "positionLongitude": "Longitude",
+ "positionAltitude": "Korkeus",
+ "positionSpeed": "Nopeus",
+ "positionCourse": "Suunta",
+ "positionAddress": "Osoite",
+ "positionProtocol": "Protokolla",
+ "serverTitle": "Palvelinasetukset",
+ "serverZoom": "Lähennä",
+ "serverRegistration": "Rekisteröinti",
+ "serverReadonly": "Vain luku",
+ "mapTitle": "Kartta",
+ "mapLayer": "Karttataso",
+ "mapCustom": "Oma kartta",
+ "mapOsm": "Open Street Map",
+ "mapBingKey": "Bing Maps avain",
+ "mapBingRoad": "Bign Maps tiet",
+ "mapBingAerial": "Bing Maps ilmakuva",
+ "stateTitle": "Tila",
+ "stateName": "Ominaisuus",
+ "stateValue": "Arvo",
+ "commandTitle": "Komento",
+ "commandSend": "Lähetä",
+ "commandType": "Tyyppi",
+ "commandSent": "Komento on lähetetty",
+ "commandPositionPeriodic": "Määräaikaisraportointi",
+ "commandPositionStop": "Lopeta raportointi",
+ "commandEngineStop": "Sammuta moottori",
+ "commandEngineResume": "Palauta moottori",
+ "commandFrequency": "Taajuus",
+ "commandUnit": "Yksikkö"
+} \ No newline at end of file
diff --git a/web/l10n/lt.json b/web/l10n/lt.json
index 3b32a9c3d..77e7b95a5 100644
--- a/web/l10n/lt.json
+++ b/web/l10n/lt.json
@@ -27,14 +27,14 @@
"loginFailed": "Neteisingas el.paštas ir/ar slaptažodis",
"loginCreated": "Registracija sėmkinga",
"loginLogout": "Atsijungti",
- "devicesAndState": "Devices and State",
+ "devicesAndState": "Prietaisai ir Statusas",
"deviceDialog": "Prietaisas",
"deviceTitle": "Prietaisai",
"deviceName": "Pavadinimas",
"deviceIdentifier": "Identifikacinis kodas",
"deviceLastUpdate": "Naujausias atnaujinimas",
"deviceCommand": "Komanda",
- "deviceFollow": "Follow",
+ "deviceFollow": "Sekti",
"settingsTitle": "Nustatymai",
"settingsUser": "Paskyra",
"settingsServer": "Serveris",
@@ -59,7 +59,7 @@
"serverTitle": "Serverio nustatymai",
"serverZoom": "Priartinimas",
"serverRegistration": "Registracija",
- "serverReadonly": "Readonly",
+ "serverReadonly": "Tik skaityti",
"mapTitle": "Žemėlapis",
"mapLayer": "Žemėlapio sluoksnis",
"mapCustom": "Pasirinktinis Žemėlapis",
diff --git a/web/l10n/ml.json b/web/l10n/ml.json
new file mode 100644
index 000000000..02bd39b00
--- /dev/null
+++ b/web/l10n/ml.json
@@ -0,0 +1,83 @@
+{
+ "sharedLoading": "ലോഡുചെയ്യുന്നു ..",
+ "sharedSave": "Save",
+ "sharedCancel": "റദ്ദാക്കുക",
+ "sharedAdd": "ചേര്‍ക്കുക",
+ "sharedEdit": "തിരുത്തുക",
+ "sharedRemove": "നീക്കം ചെയ്യുക",
+ "sharedRemoveConfirm": "വിഷയം നീക്കം ചെയ്യുക",
+ "sharedKm": "കിലോമീറ്റർ",
+ "sharedMi": "mi",
+ "sharedKmh": "കിലോമീറ്റർ / മണിക്കൂർ",
+ "sharedMph": "mph",
+ "sharedHour": "മണിക്കൂര്",
+ "sharedMinute": "മിനിറ്റ്",
+ "sharedSecond": "സെക്കന്റ്",
+ "errorTitle": "പിശക്‌",
+ "errorUnknown": "അജ്ഞാത പിശക്",
+ "errorConnection": "കണക്ഷൻ പിശക്",
+ "userName": "പേര്",
+ "userEmail": "ഇമെയിൽ",
+ "userPassword": "രഹസ്യ കോഡ്‌",
+ "userAdmin": "Admin",
+ "loginTitle": "അകത്തു പ്രവേശിക്കുക",
+ "loginLanguage": "ഭാഷ",
+ "loginRegister": "Register",
+ "loginLogin": "അകത്തു പ്രവേശിക്കുക",
+ "loginFailed": "തെറ്റായ ഇമെയിൽ വിലാസവും പാസ്വേഡും",
+ "loginCreated": "പുതിയ ഉപയോക്താവ് രജിസ്റ്റർ ചെയ്തു",
+ "loginLogout": "പുറത്തുകടക്കുക",
+ "devicesAndState": "Devices and State",
+ "deviceDialog": "ഉപകരണം",
+ "deviceTitle": "Devices",
+ "deviceName": "പേര്",
+ "deviceIdentifier": "Identifier",
+ "deviceLastUpdate": "Last Update",
+ "deviceCommand": "Command",
+ "deviceFollow": "Follow",
+ "settingsTitle": "Settings",
+ "settingsUser": "Account",
+ "settingsServer": "Server",
+ "settingsUsers": "Users",
+ "settingsDistanceUnit": "Distance",
+ "settingsSpeedUnit": "വേഗം",
+ "reportTitle": "Reports",
+ "reportDevice": "ഉപകരണം",
+ "reportFrom": "From",
+ "reportTo": "To",
+ "reportShow": "Show",
+ "reportClear": "Clear",
+ "positionFixTime": "സമയം",
+ "positionValid": "Valid",
+ "positionLatitude": "അക്ഷാംശം",
+ "positionLongitude": "രേഖാംശം",
+ "positionAltitude": "Altitude",
+ "positionSpeed": "വേഗം",
+ "positionCourse": "Course",
+ "positionAddress": "Address",
+ "positionProtocol": "Protocol",
+ "serverTitle": "Server Settings",
+ "serverZoom": "വലുതാക്കിയോ ചെറുതാക്കിയോ കാണിക്കുക",
+ "serverRegistration": "രജിസ്ട്രേഷൻ",
+ "serverReadonly": "Readonly",
+ "mapTitle": "ഭൂപടം",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Custom Map",
+ "mapOsm": "Open Street Map",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "stateTitle": "State",
+ "stateName": "Attribute",
+ "stateValue": "Value",
+ "commandTitle": "Command",
+ "commandSend": "Send",
+ "commandType": "Type",
+ "commandSent": "Command has been sent",
+ "commandPositionPeriodic": "Periodic Reporting",
+ "commandPositionStop": "Stop Reporting",
+ "commandEngineStop": "Engine Stop",
+ "commandEngineResume": "Engine Resume",
+ "commandFrequency": "Frequency",
+ "commandUnit": "Unit"
+} \ No newline at end of file
diff --git a/web/l10n/ms.json b/web/l10n/ms.json
new file mode 100644
index 000000000..ff5f02f01
--- /dev/null
+++ b/web/l10n/ms.json
@@ -0,0 +1,83 @@
+{
+ "sharedLoading": "Memuatkan...",
+ "sharedSave": "Simpan",
+ "sharedCancel": "Batal",
+ "sharedAdd": "Tambah",
+ "sharedEdit": "Ubah",
+ "sharedRemove": "Hapus",
+ "sharedRemoveConfirm": "Hapuskan item?",
+ "sharedKm": "km",
+ "sharedMi": "mi",
+ "sharedKmh": "km/h",
+ "sharedMph": "mph",
+ "sharedHour": "Jam",
+ "sharedMinute": "Minit",
+ "sharedSecond": "Saat",
+ "errorTitle": "Ralat",
+ "errorUnknown": "Ralat tidak diketahui",
+ "errorConnection": "Ralat penyambungan",
+ "userName": "Nama",
+ "userEmail": "Emel",
+ "userPassword": "Katalaluan",
+ "userAdmin": "Admin",
+ "loginTitle": "Log masuk",
+ "loginLanguage": "Bahasa",
+ "loginRegister": "Daftar",
+ "loginLogin": "Log masuk",
+ "loginFailed": "Kesalahan emel atau katalaluan",
+ "loginCreated": "Pengguna baru telah didaftarkan",
+ "loginLogout": "Keluar",
+ "devicesAndState": "Peranti dan State",
+ "deviceDialog": "Peranti",
+ "deviceTitle": "Peranti",
+ "deviceName": "Nama",
+ "deviceIdentifier": "IMEI/ID",
+ "deviceLastUpdate": "Kemaskini Terakhir",
+ "deviceCommand": "Arahan",
+ "deviceFollow": "Ikut",
+ "settingsTitle": "Tetapan",
+ "settingsUser": "Akaun",
+ "settingsServer": "Server",
+ "settingsUsers": "Pengguna",
+ "settingsDistanceUnit": "Jarak",
+ "settingsSpeedUnit": "Kelajuan",
+ "reportTitle": "Laporan",
+ "reportDevice": "Peranti",
+ "reportFrom": "Daripada",
+ "reportTo": "Ke",
+ "reportShow": "Papar",
+ "reportClear": "Kosongkan",
+ "positionFixTime": "Masa",
+ "positionValid": "Sah",
+ "positionLatitude": "Latitud",
+ "positionLongitude": "Longitud",
+ "positionAltitude": "Altitud",
+ "positionSpeed": "Kelajuan",
+ "positionCourse": "Course",
+ "positionAddress": "Alamat",
+ "positionProtocol": "Protokol",
+ "serverTitle": "Tetapan Server",
+ "serverZoom": "Besarkan",
+ "serverRegistration": "Pendaftaran",
+ "serverReadonly": "Baca Sahaja",
+ "mapTitle": "Peta",
+ "mapLayer": "Map Layer",
+ "mapCustom": "Peta Lain",
+ "mapOsm": "Open Street Map",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "stateTitle": "Negeri",
+ "stateName": "Atribut",
+ "stateValue": "Nilai",
+ "commandTitle": "Arahan",
+ "commandSend": "Hantar",
+ "commandType": "Jenis",
+ "commandSent": "Arahan telah dihantar",
+ "commandPositionPeriodic": "Laporan Berkala",
+ "commandPositionStop": "Hentikan Laporan",
+ "commandEngineStop": "Matikan Enjin",
+ "commandEngineResume": "Hidupkan Enjin",
+ "commandFrequency": "Frekuensi",
+ "commandUnit": "Unit"
+} \ No newline at end of file
diff --git a/web/l10n/no.json b/web/l10n/no.json
index dcfef54ce..d3dbb003e 100644
--- a/web/l10n/no.json
+++ b/web/l10n/no.json
@@ -67,7 +67,7 @@
"mapBingKey": "Bing Maps Nøkkel",
"mapBingRoad": "Bing Maps Vei",
"mapBingAerial": "Bing Maps Fly",
- "stateTitle": "Stat",
+ "stateTitle": "Status",
"stateName": "Egenskap",
"stateValue": "Verdi",
"commandTitle": "Kommando",
diff --git a/web/l10n/tr.json b/web/l10n/tr.json
new file mode 100644
index 000000000..6eacb12d0
--- /dev/null
+++ b/web/l10n/tr.json
@@ -0,0 +1,83 @@
+{
+ "sharedLoading": "Yükleniyor...",
+ "sharedSave": "Kaydet",
+ "sharedCancel": "İptal",
+ "sharedAdd": "Ekle",
+ "sharedEdit": "Düzenle",
+ "sharedRemove": "Kaldır",
+ "sharedRemoveConfirm": "Öğeyi kaldır",
+ "sharedKm": "km",
+ "sharedMi": "mil",
+ "sharedKmh": "km/s",
+ "sharedMph": "mil/s",
+ "sharedHour": "Saat",
+ "sharedMinute": "Dakika",
+ "sharedSecond": "Saniye",
+ "errorTitle": "Hata",
+ "errorUnknown": "Bilinmeyen hata ",
+ "errorConnection": "Bağlantı Hatası",
+ "userName": "Ad",
+ "userEmail": "Eposta",
+ "userPassword": "Şifre",
+ "userAdmin": "Yönetici",
+ "loginTitle": "Oturum aç",
+ "loginLanguage": "Lisan",
+ "loginRegister": "Kayıt",
+ "loginLogin": "Oturumu aç",
+ "loginFailed": "Geçersiz eposta veya şifre",
+ "loginCreated": "Yeni kullanıcı kaydedildi",
+ "loginLogout": "Oturumu sonlandır",
+ "devicesAndState": "Cihazlar ve Bölge",
+ "deviceDialog": "Cihaz",
+ "deviceTitle": "Cihazlar",
+ "deviceName": "İsim",
+ "deviceIdentifier": "Kimlik",
+ "deviceLastUpdate": "Son Güncelleme",
+ "deviceCommand": "Komut",
+ "deviceFollow": "Takip",
+ "settingsTitle": "Ayarlar",
+ "settingsUser": "Hesap",
+ "settingsServer": "Sunucu",
+ "settingsUsers": "Kullanıcı",
+ "settingsDistanceUnit": "Mesafe",
+ "settingsSpeedUnit": "Hız",
+ "reportTitle": "Raporlar",
+ "reportDevice": "Aygıt",
+ "reportFrom": "Başlangıç",
+ "reportTo": "Varış",
+ "reportShow": "Göster",
+ "reportClear": "Temizle",
+ "positionFixTime": "Süre",
+ "positionValid": "Geçerli",
+ "positionLatitude": "Enlem",
+ "positionLongitude": "Boylam",
+ "positionAltitude": "Rakım",
+ "positionSpeed": "Sürat",
+ "positionCourse": "Yön",
+ "positionAddress": "Adres",
+ "positionProtocol": "Protokol",
+ "serverTitle": "Sunucu Ayarları",
+ "serverZoom": "Yakınlaştırma",
+ "serverRegistration": "Kayıt",
+ "serverReadonly": "Saltokunur",
+ "mapTitle": "Harita",
+ "mapLayer": "Harita Katmanı",
+ "mapCustom": "Özelleştirilmiş Harita",
+ "mapOsm": "Open Street Map",
+ "mapBingKey": "Bing Maps Key",
+ "mapBingRoad": "Bing Maps Road",
+ "mapBingAerial": "Bing Maps Aerial",
+ "stateTitle": "Bölge",
+ "stateName": "Özellik",
+ "stateValue": "Değer",
+ "commandTitle": "Komut",
+ "commandSend": "Gönder",
+ "commandType": "Tip",
+ "commandSent": "Komut gönderildi",
+ "commandPositionPeriodic": "Periyodik Rapor",
+ "commandPositionStop": "Raporlamayı Durdur",
+ "commandEngineStop": "Motoru Durdur",
+ "commandEngineResume": "Motoru Çalıştır",
+ "commandFrequency": "Frekans",
+ "commandUnit": "Ünite"
+} \ No newline at end of file
diff --git a/web/locale.js b/web/locale.js
index 7dc94ec45..c0b3af90b 100644
--- a/web/locale.js
+++ b/web/locale.js
@@ -16,9 +16,6 @@
var Locale = {};
-Ext.Ajax.disableCaching = false;
-Ext.Loader.disableCaching = false;
-
Locale.languages = {
'ar': { name: 'العربية', code: 'en' },
'bg': { name: 'Български', code: 'bg' },
@@ -28,12 +25,16 @@ Locale.languages = {
'el': { name: 'Ελληνικά', code: 'el' },
'en': { name: 'English', code: 'en' },
'es': { name: 'Español', code: 'es' },
+ 'fa': { name: 'فارسی', code: 'fa' },
+ 'fi': { name: 'Suomi', code: 'fi' },
'fr': { name: 'Français', code: 'fr' },
'hu': { name: 'Magyar', code: 'hu' },
'id': { name: 'Bahasa Indonesia', code: 'id' },
'it': { name: 'Italiano', code: 'it' },
'ka': { name: 'ქართული', code: 'en' },
'lt': { name: 'Lietuvių', code: 'lt' },
+ 'ml': { name: 'മലയാളം', code: 'en' },
+ 'ms': { name: 'بهاس ملايو', code: 'en' },
'nl': { name: 'Nederlands', code: 'nl' },
'no': { name: 'Norsk', code: 'no_NB' },
'pl': { name: 'Polski', code: 'pl' },
@@ -47,6 +48,7 @@ Locale.languages = {
'sr': { name: 'Srpski', code: 'sr' },
'ta': { name: 'தமிழ்', code: 'en' },
'th': { name: 'ไทย', code: 'th' },
+ 'tr': { name: 'Türkçe', code: 'tr' },
'uk': { name: 'Українська', code: 'ukr' },
'vi': { name: 'Tiếng Việt', code: 'en' },
'zh': { name: '中文', code: 'zh_CN' }
diff --git a/web/release.html b/web/release.html
index d5fc60e2b..155e13f1a 100644
--- a/web/release.html
+++ b/web/release.html
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Traccar</title>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css">
-<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol.min.css">
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
<link rel="stylesheet" href="app.css">
</head>
@@ -14,7 +14,7 @@
<div id="attribution">Powered by <a href="https://www.traccar.org/">Traccar GPS Tracking System</a></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/theme-neptune.js"></script>
-<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol-debug.min.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol-debug.min.js"></script>
<script src="arrowstyle.js"></script>
<script src="locale.js"></script>
<script type="text/javascript">