aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE.txt202
-rw-r--r--debug.xml31
-rw-r--r--schema/changelog-3.7.xml2
-rw-r--r--schema/changelog-3.8.xml45
-rw-r--r--src/org/traccar/BasePipelineFactory.java2
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java3
-rw-r--r--src/org/traccar/Context.java25
-rw-r--r--src/org/traccar/MainEventHandler.java1
-rw-r--r--src/org/traccar/api/SecurityRequestFilter.java2
-rw-r--r--src/org/traccar/api/resource/AttributeAliasResource.java90
-rw-r--r--src/org/traccar/api/resource/DeviceResource.java1
-rw-r--r--src/org/traccar/database/AliasesManager.java113
-rw-r--r--src/org/traccar/database/DataManager.java37
-rw-r--r--src/org/traccar/database/DeviceManager.java90
-rw-r--r--src/org/traccar/database/StatisticsManager.java88
-rw-r--r--src/org/traccar/events/OverspeedEventHandler.java7
-rw-r--r--src/org/traccar/model/AttributeAlias.java61
-rw-r--r--src/org/traccar/model/Position.java1
-rw-r--r--src/org/traccar/model/Statistics.java90
-rw-r--r--src/org/traccar/protocol/AplicomProtocolDecoder.java298
-rw-r--r--src/org/traccar/protocol/CellocatorProtocol.java10
-rw-r--r--src/org/traccar/protocol/CguardProtocol.java47
-rw-r--r--src/org/traccar/protocol/CguardProtocolDecoder.java91
-rw-r--r--src/org/traccar/protocol/Gt06ProtocolEncoder.java4
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolDecoder.java90
-rw-r--r--src/org/traccar/protocol/OigoProtocolDecoder.java4
-rw-r--r--src/org/traccar/reports/Summary.java6
-rw-r--r--src/org/traccar/reports/Trips.java7
-rw-r--r--src/org/traccar/web/CsvBuilder.java4
-rw-r--r--src/org/traccar/web/WebServer.java3
-rw-r--r--test/org/traccar/protocol/AplicomProtocolDecoderTest.java20
-rw-r--r--test/org/traccar/protocol/CguardProtocolDecoderTest.java54
-rw-r--r--test/org/traccar/protocol/Jt600ProtocolDecoderTest.java38
-rw-r--r--test/org/traccar/protocol/OigoProtocolDecoderTest.java3
-rwxr-xr-xtools/hex.sh20
35 files changed, 1504 insertions, 86 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/debug.xml b/debug.xml
index d27895706..39a52fe96 100644
--- a/debug.xml
+++ b/debug.xml
@@ -334,6 +334,36 @@
<entry key='database.deletePositions'>
DELETE FROM positions WHERE serverTime &lt; :serverTime AND id NOT IN (SELECT positionId FROM devices);
</entry>
+
+ <entry key='database.selectAttributeAliases'>
+ SELECT * FROM attribute_aliases;
+ </entry>
+
+ <entry key='database.insertAttributeAlias'>
+ INSERT INTO attribute_aliases (deviceId, attribute, alias)
+ VALUES (:deviceId, :attribute, :alias);
+ </entry>
+
+ <entry key='database.updateAttributeAlias'>
+ UPDATE attribute_aliases SET
+ deviceId = :deviceId,
+ attribute = :attribute,
+ alias = :alias
+ WHERE id = :id;
+ </entry>
+
+ <entry key='database.deleteAttributeAlias'>
+ DELETE FROM attribute_aliases WHERE id = :id;
+ </entry>
+
+ <entry key='database.selectStatistics'>
+ SELECT * FROM statistics;
+ </entry>
+
+ <entry key='database.insertStatistics'>
+ INSERT INTO statistics (captureTime, activeUsers, activeDevices, requests, messagesReceived, messagesStored, attributes)
+ VALUES (:captureTime, :activeUsers, :activeDevices, :requests, :messagesReceived, :messagesStored, :attributes);
+ </entry>
<!-- PROTOCOL CONFIG -->
@@ -460,5 +490,6 @@
<entry key='autograde.port'>5120</entry>
<entry key='oigo.port'>5121</entry>
<entry key='jpkorjar.port'>5122</entry>
+ <entry key='cguard.port'>5123</entry>
</properties>
diff --git a/schema/changelog-3.7.xml b/schema/changelog-3.7.xml
index d82dbe91b..1bae2aaff 100644
--- a/schema/changelog-3.7.xml
+++ b/schema/changelog-3.7.xml
@@ -17,7 +17,7 @@
<update tableName="groups">
<column name="groupid"/>
- <where>groupid NOT IN (SELECT id FROM (SELECT id FROM groups) AS groups_ids)</where>
+ <where>groupid NOT IN (SELECT id FROM (SELECT DISTINCT id FROM groups) AS groups_ids)</where>
</update>
<addColumn tableName="devices">
diff --git a/schema/changelog-3.8.xml b/schema/changelog-3.8.xml
index 97bc1c9a3..deab6603c 100644
--- a/schema/changelog-3.8.xml
+++ b/schema/changelog-3.8.xml
@@ -8,6 +8,24 @@
<changeSet author="author" id="changelog-3.8">
+ <createTable tableName="attribute_aliases">
+ <column name="id" type="INT" autoIncrement="true">
+ <constraints primaryKey="true" />
+ </column>
+ <column name="deviceid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="attribute" type="VARCHAR(128)">
+ <constraints nullable="false" />
+ </column>
+ <column name="alias" type="VARCHAR(128)">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="attribute_aliases" baseColumnNames="deviceid" constraintName="fk_attribute_aliases_deviceid" referencedTableName="devices" referencedColumnNames="id" onDelete="CASCADE" />
+ <addUniqueConstraint tableName="attribute_aliases" columnNames="deviceid, attribute" constraintName="uk_deviceid_attribute" />
+
<update tableName="users">
<column name="map" type="VARCHAR(128)"/>
<where>map = 'osm'</where>
@@ -21,5 +39,32 @@
<where>speedunit = 'kmh'</where>
</update>
+ <createTable tableName="statistics">
+ <column name="id" type="INT" autoIncrement="true">
+ <constraints primaryKey="true" />
+ </column>
+ <column name="capturetime" type="TIMESTAMP">
+ <constraints nullable="false" />
+ </column>
+ <column name="activeusers" type="INT" defaultValueNumeric="0">
+ <constraints nullable="false" />
+ </column>
+ <column name="activedevices" type="INT" defaultValueNumeric="0">
+ <constraints nullable="false" />
+ </column>
+ <column name="requests" type="INT" defaultValueNumeric="0">
+ <constraints nullable="false" />
+ </column>
+ <column name="messagesreceived" type="INT" defaultValueNumeric="0">
+ <constraints nullable="false" />
+ </column>
+ <column name="messagesstored" type="INT" defaultValueNumeric="0">
+ <constraints nullable="false" />
+ </column>
+ <column name="attributes" type="VARCHAR(4096)">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
</changeSet>
</databaseChangeLog>
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index 615251d5f..837712e84 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -229,7 +229,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
pipeline.addLast("AlertEventHandler", alertEventHandler);
}
- if (alertEventHandler != null) {
+ if (ignitionEventHandler != null) {
pipeline.addLast("IgnitionEventHandler", ignitionEventHandler);
}
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index 00f45727f..15f5471e9 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -149,6 +149,9 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
@Override
protected void onMessageEvent(Channel channel, SocketAddress remoteAddress, Object msg) {
+ if (Context.getStatisticsManager() != null) {
+ Context.getStatisticsManager().registerMessageReceived();
+ }
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession != null) {
Context.getConnectionManager().updateDevice(deviceSession.getDeviceId(), Device.STATUS_ONLINE, new Date());
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index c7359e76c..827761cc5 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -16,6 +16,8 @@
package org.traccar;
import com.ning.http.client.AsyncHttpClient;
+
+import org.traccar.database.AliasesManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
import org.traccar.database.DeviceManager;
@@ -23,6 +25,7 @@ import org.traccar.database.IdentityManager;
import org.traccar.database.NotificationManager;
import org.traccar.database.PermissionsManager;
import org.traccar.database.GeofenceManager;
+import org.traccar.database.StatisticsManager;
import org.traccar.geocode.BingMapsReverseGeocoder;
import org.traccar.geocode.FactualReverseGeocoder;
import org.traccar.geocode.GeocodeFarmReverseGeocoder;
@@ -134,13 +137,27 @@ public final class Context {
return eventForwarder;
}
+ private static AliasesManager aliasesManager;
+
+ public static AliasesManager getAliasesManager() {
+ return aliasesManager;
+ }
+
+ private static StatisticsManager statisticsManager;
+
+ public static StatisticsManager getStatisticsManager() {
+ return statisticsManager;
+ }
+
public static void init(String[] arguments) throws Exception {
config = new Config();
- if (arguments.length > 0) {
- config.load(arguments[0]);
+ if (arguments.length <= 0) {
+ throw new RuntimeException("Configuration file is not provided");
}
+ config.load(arguments[0]);
+
loggerEnabled = config.getBoolean("logger.enable");
if (loggerEnabled) {
Log.setupLogger(config);
@@ -233,6 +250,10 @@ public final class Context {
eventForwarder = new EventForwarder();
}
+ aliasesManager = new AliasesManager(dataManager);
+
+ statisticsManager = new StatisticsManager();
+
}
public static void init(IdentityManager testIdentityManager) {
diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java
index 3f7e68b2a..9f172c584 100644
--- a/src/org/traccar/MainEventHandler.java
+++ b/src/org/traccar/MainEventHandler.java
@@ -62,6 +62,7 @@ public class MainEventHandler extends IdleStateAwareChannelHandler {
}
Log.info(s.toString());
+ Context.getStatisticsManager().registerMessageStored(position.getDeviceId());
}
}
diff --git a/src/org/traccar/api/SecurityRequestFilter.java b/src/org/traccar/api/SecurityRequestFilter.java
index f0dd363db..9e0f03de7 100644
--- a/src/org/traccar/api/SecurityRequestFilter.java
+++ b/src/org/traccar/api/SecurityRequestFilter.java
@@ -69,6 +69,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
String[] auth = decodeBasicAuth(authHeader);
User user = Context.getDataManager().login(auth[0], auth[1]);
if (user != null) {
+ Context.getStatisticsManager().registerRequest(user.getId());
securityContext = new UserSecurityContext(new UserPrincipal(user.getId()));
}
} catch (SQLException e) {
@@ -79,6 +80,7 @@ public class SecurityRequestFilter implements ContainerRequestFilter {
Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY);
if (userId != null) {
+ Context.getStatisticsManager().registerRequest(userId);
securityContext = new UserSecurityContext(new UserPrincipal(userId));
}
diff --git a/src/org/traccar/api/resource/AttributeAliasResource.java b/src/org/traccar/api/resource/AttributeAliasResource.java
new file mode 100644
index 000000000..2417fb0ec
--- /dev/null
+++ b/src/org/traccar/api/resource/AttributeAliasResource.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.resource;
+
+import java.sql.SQLException;
+import java.util.Collection;
+
+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 org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.AttributeAlias;
+
+@Path("attributes/aliases")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class AttributeAliasResource extends BaseResource {
+
+ @GET
+ public Collection<AttributeAlias> get(@QueryParam("deviceId") long deviceId) throws SQLException {
+ if (deviceId != 0) {
+ if (!Context.getPermissionsManager().isAdmin(getUserId())) {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ }
+ return Context.getAliasesManager().getAttributeAliases(deviceId);
+ } else {
+ return Context.getAliasesManager().getAllAttributeAliases(getUserId());
+ }
+ }
+
+ @POST
+ public Response add(AttributeAlias entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ if (!Context.getPermissionsManager().isAdmin(getUserId())) {
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
+ }
+ Context.getAliasesManager().addAttributeAlias(entity);
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @PUT
+ public Response update(@PathParam("id") long id, AttributeAlias entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ if (!Context.getPermissionsManager().isAdmin(getUserId())) {
+ AttributeAlias oldEntity = Context.getAliasesManager().getAttributeAlias(entity.getId());
+ Context.getPermissionsManager().checkDevice(getUserId(), oldEntity.getDeviceId());
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
+ }
+ Context.getAliasesManager().updateAttributeAlias(entity);
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ if (!Context.getPermissionsManager().isAdmin(getUserId())) {
+ AttributeAlias entity = Context.getAliasesManager().getAttributeAlias(id);
+ Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
+ }
+ Context.getAliasesManager().removeArrtibuteAlias(id);
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/DeviceResource.java b/src/org/traccar/api/resource/DeviceResource.java
index b12ab8c36..56787b7bb 100644
--- a/src/org/traccar/api/resource/DeviceResource.java
+++ b/src/org/traccar/api/resource/DeviceResource.java
@@ -88,6 +88,7 @@ public class DeviceResource extends BaseResource {
if (Context.getGeofenceManager() != null) {
Context.getGeofenceManager().refresh();
}
+ Context.getAliasesManager().removeDevice(id);
return Response.noContent().build();
}
diff --git a/src/org/traccar/database/AliasesManager.java b/src/org/traccar/database/AliasesManager.java
new file mode 100644
index 000000000..6c09e8731
--- /dev/null
+++ b/src/org/traccar/database/AliasesManager.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.database;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.AttributeAlias;
+
+public class AliasesManager {
+
+ private final DataManager dataManager;
+
+ private final Map<Long, Set<AttributeAlias>> deviceAliases = new ConcurrentHashMap<>();
+ private final Map<Long, AttributeAlias> aliasesById = new ConcurrentHashMap<>();
+
+ public AliasesManager(DataManager dataManager) {
+ this.dataManager = dataManager;
+ if (dataManager != null) {
+ try {
+ for (AttributeAlias attributeAlias : dataManager.getAttributeAliases()) {
+ getAttributeAliases(attributeAlias.getDeviceId())
+ .add(attributeAlias);
+ aliasesById.put(attributeAlias.getId(), attributeAlias);
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ public Set<AttributeAlias> getAttributeAliases(long deviceId) {
+ if (!deviceAliases.containsKey(deviceId)) {
+ deviceAliases.put(deviceId, new HashSet<AttributeAlias>());
+ }
+ return deviceAliases.get(deviceId);
+ }
+
+ public void removeDevice(long deviceId) {
+ for (AttributeAlias attributeAlias : getAttributeAliases(deviceId)) {
+ aliasesById.remove(attributeAlias.getId());
+ }
+ deviceAliases.remove(deviceId);
+ }
+
+ public void addAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
+ dataManager.addAttributeAlias(attributeAlias);
+ aliasesById.put(attributeAlias.getId(), attributeAlias);
+ getAttributeAliases(attributeAlias.getDeviceId()).add(attributeAlias);
+ }
+
+ public void updateAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
+ dataManager.updateAttributeAlias(attributeAlias);
+ AttributeAlias cachedAlias = aliasesById.get(attributeAlias.getId());
+ if (cachedAlias.getDeviceId() != attributeAlias.getDeviceId()) {
+ getAttributeAliases(cachedAlias.getDeviceId()).remove(cachedAlias);
+ cachedAlias.setDeviceId(attributeAlias.getDeviceId());
+ getAttributeAliases(cachedAlias.getDeviceId()).add(cachedAlias);
+ }
+ cachedAlias.setAttribute(attributeAlias.getAttribute());
+ cachedAlias.setAlias(attributeAlias.getAlias());
+ }
+
+ public void removeArrtibuteAlias(long attributeAliasId) throws SQLException {
+ dataManager.removeAttributeAlias(attributeAliasId);
+ AttributeAlias cachedAlias = aliasesById.get(attributeAliasId);
+ getAttributeAliases(cachedAlias.getDeviceId()).remove(cachedAlias);
+ aliasesById.remove(attributeAliasId);
+ }
+
+ public AttributeAlias getAttributeAlias(long deviceId, String attribute) {
+ for (AttributeAlias alias : getAttributeAliases(deviceId)) {
+ if (alias.getAttribute().equals(attribute)) {
+ return alias;
+ }
+ }
+ return null;
+ }
+
+ public Collection<AttributeAlias> getAllAttributeAliases(long userId) {
+ Collection<AttributeAlias> userDevicesAliases = new ArrayList<>();
+ for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
+ userDevicesAliases.addAll(getAttributeAliases(deviceId));
+ }
+ return userDevicesAliases;
+ }
+
+ public AttributeAlias getAttributeAlias(long id) {
+ return aliasesById.get(id);
+ }
+
+}
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index abc48f063..c58fe40a5 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -37,6 +37,7 @@ import liquibase.resource.ResourceAccessor;
import org.traccar.Config;
import org.traccar.helper.Log;
+import org.traccar.model.AttributeAlias;
import org.traccar.model.Device;
import org.traccar.model.DevicePermission;
import org.traccar.model.Event;
@@ -47,6 +48,7 @@ import org.traccar.model.GroupPermission;
import org.traccar.model.Notification;
import org.traccar.model.Position;
import org.traccar.model.Server;
+import org.traccar.model.Statistics;
import org.traccar.model.User;
import org.traccar.model.DeviceGeofence;
import org.traccar.model.GeofencePermission;
@@ -460,4 +462,39 @@ public class DataManager {
.setLong("id", notification.getId())
.executeUpdate();
}
+
+ public Collection<AttributeAlias> getAttributeAliases() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectAttributeAliases"))
+ .executeQuery(AttributeAlias.class);
+ }
+
+ public void addAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
+ attributeAlias.setId(QueryBuilder.create(dataSource, getQuery("database.insertAttributeAlias"), true)
+ .setObject(attributeAlias)
+ .executeUpdate());
+ }
+
+ public void updateAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.updateAttributeAlias"))
+ .setObject(attributeAlias)
+ .executeUpdate();
+ }
+
+ public void removeAttributeAlias(long attributeAliasId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.deleteAttributeAlias"))
+ .setLong("id", attributeAliasId)
+ .executeUpdate();
+ }
+
+ public Collection<Statistics> getStatistics() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectStatistics"))
+ .executeQuery(Statistics.class);
+ }
+
+ public void addStatistics(Statistics statistics) throws SQLException {
+ statistics.setId(QueryBuilder.create(dataSource, getQuery("database.insertStatistics"), true)
+ .setObject(statistics)
+ .executeUpdate());
+ }
+
}
diff --git a/src/org/traccar/database/DeviceManager.java b/src/org/traccar/database/DeviceManager.java
index 5f68df831..f32c7edd2 100644
--- a/src/org/traccar/database/DeviceManager.java
+++ b/src/org/traccar/database/DeviceManager.java
@@ -316,7 +316,87 @@ public class DeviceManager implements IdentityManager {
groupsById.remove(groupId);
}
- public String lookupAttribute(long deviceId, String attributeName) {
+ public boolean lookupServerBoolean(long deviceId, String attributeName, boolean defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, true);
+ if (result != null) {
+ return Boolean.parseBoolean(result);
+ }
+ return defaultValue;
+ }
+
+ public String lookupServerString(long deviceId, String attributeName, String defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, true);
+ if (result != null) {
+ return result;
+ }
+ return defaultValue;
+ }
+
+ public int lookupServerInteger(long deviceId, String attributeName, int defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, true);
+ if (result != null) {
+ return Integer.parseInt(result);
+ }
+ return defaultValue;
+ }
+
+ public long lookupServerLong(long deviceId, String attributeName, long defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, true);
+ if (result != null) {
+ return Long.parseLong(result);
+ }
+ return defaultValue;
+ }
+
+ public double lookupServerDouble(long deviceId, String attributeName, double defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, true);
+ if (result != null) {
+ return Double.parseDouble(result);
+ }
+ return defaultValue;
+ }
+
+ public boolean lookupConfigBoolean(long deviceId, String attributeName, boolean defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, false);
+ if (result != null) {
+ return Boolean.parseBoolean(result);
+ }
+ return defaultValue;
+ }
+
+ public String lookupConfigString(long deviceId, String attributeName, String defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, false);
+ if (result != null) {
+ return result;
+ }
+ return defaultValue;
+ }
+
+ public int lookupConfigInteger(long deviceId, String attributeName, int defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, false);
+ if (result != null) {
+ return Integer.parseInt(result);
+ }
+ return defaultValue;
+ }
+
+ public long lookupConfigLong(long deviceId, String attributeName, long defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, false);
+ if (result != null) {
+ return Long.parseLong(result);
+ }
+ return defaultValue;
+ }
+
+ public double lookupConfigDouble(long deviceId, String attributeName, double defaultValue) {
+ String result = lookupAttribute(deviceId, attributeName, false);
+ if (result != null) {
+ return Double.parseDouble(result);
+ }
+ return defaultValue;
+ }
+
+ private String lookupAttribute(long deviceId, String attributeName, boolean lookupServer) {
String result = null;
Device device = getDeviceById(deviceId);
if (device != null) {
@@ -338,8 +418,12 @@ public class DeviceManager implements IdentityManager {
}
}
if (result == null) {
- Server server = Context.getPermissionsManager().getServer();
- result = (String) server.getAttributes().get(attributeName);
+ if (lookupServer) {
+ Server server = Context.getPermissionsManager().getServer();
+ result = (String) server.getAttributes().get(attributeName);
+ } else {
+ result = Context.getConfig().getString(attributeName);
+ }
}
}
return result;
diff --git a/src/org/traccar/database/StatisticsManager.java b/src/org/traccar/database/StatisticsManager.java
new file mode 100644
index 000000000..b9af4d9b7
--- /dev/null
+++ b/src/org/traccar/database/StatisticsManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Statistics;
+
+import java.sql.SQLException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+public class StatisticsManager {
+
+ private static final int SPLIT_MODE = Calendar.DAY_OF_MONTH;
+
+ private int lastUpdate = Calendar.getInstance().get(SPLIT_MODE);
+
+ private Set<Long> users = new HashSet<>();
+ private Set<Long> devices = new HashSet<>();
+
+ private int requests;
+ private int messagesReceived;
+ private int messagesStored;
+
+ private void checkSplit() {
+ int currentUpdate = Calendar.getInstance().get(SPLIT_MODE);
+ if (lastUpdate != currentUpdate) {
+ Statistics statistics = new Statistics();
+ statistics.setCaptureTime(new Date());
+ statistics.setActiveUsers(users.size());
+ statistics.setActiveDevices(devices.size());
+ statistics.setRequests(requests);
+ statistics.setMessagesReceived(messagesReceived);
+ statistics.setMessagesStored(messagesStored);
+
+ try {
+ Context.getDataManager().addStatistics(statistics);
+ } catch (SQLException e) {
+ Log.warning(e);
+ }
+
+ users.clear();
+ devices.clear();
+ requests = 0;
+ messagesReceived = 0;
+ messagesStored = 0;
+ lastUpdate = currentUpdate;
+ }
+ }
+
+ public synchronized void registerRequest(long userId) {
+ checkSplit();
+ requests += 1;
+ if (userId != 0) {
+ users.add(userId);
+ }
+ }
+
+ public synchronized void registerMessageReceived() {
+ checkSplit();
+ messagesReceived += 1;
+ }
+
+ public synchronized void registerMessageStored(long deviceId) {
+ checkSplit();
+ messagesStored += 1;
+ if (deviceId != 0) {
+ devices.add(deviceId);
+ }
+ }
+
+}
diff --git a/src/org/traccar/events/OverspeedEventHandler.java b/src/org/traccar/events/OverspeedEventHandler.java
index 302fa87cd..57f60d864 100644
--- a/src/org/traccar/events/OverspeedEventHandler.java
+++ b/src/org/traccar/events/OverspeedEventHandler.java
@@ -47,11 +47,8 @@ public class OverspeedEventHandler extends BaseEventHandler {
Collection<Event> events = new ArrayList<>();
double speed = position.getSpeed();
- double speedLimit = 0;
- String speedLimitAttribute = Context.getDeviceManager().lookupAttribute(device.getId(), ATTRIBUTE_SPEED_LIMIT);
- if (speedLimitAttribute != null) {
- speedLimit = Double.parseDouble(speedLimitAttribute);
- }
+ double speedLimit = Context.getDeviceManager()
+ .lookupServerDouble(device.getId(), ATTRIBUTE_SPEED_LIMIT, 0);
if (speedLimit == 0) {
return null;
}
diff --git a/src/org/traccar/model/AttributeAlias.java b/src/org/traccar/model/AttributeAlias.java
new file mode 100644
index 000000000..023925ac3
--- /dev/null
+++ b/src/org/traccar/model/AttributeAlias.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 AttributeAlias {
+
+ private long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ private long deviceId;
+
+ public long getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(long deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ private String attribute;
+
+ public String getAttribute() {
+ return attribute;
+ }
+
+ public void setAttribute(String attribute) {
+ this.attribute = attribute;
+ }
+
+ private String alias;
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+}
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index 710dd1e83..c1058aef9 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -57,6 +57,7 @@ public class Position extends Message {
public static final String KEY_THROTTLE = "throttle";
public static final String KEY_MOTION = "motion";
public static final String KEY_ARMED = "armed";
+ public static final String KEY_ACCURACY = "accuracy";
public static final String KEY_OBD_SPEED = "obdSpeed";
public static final String KEY_OBD_ODOMETER = "obdOdometer";
diff --git a/src/org/traccar/model/Statistics.java b/src/org/traccar/model/Statistics.java
new file mode 100644
index 000000000..0461ebd7e
--- /dev/null
+++ b/src/org/traccar/model/Statistics.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+import java.util.Date;
+
+public class Statistics extends Extensible {
+
+ private Date captureTime;
+
+ public Date getCaptureTime() {
+ if (captureTime != null) {
+ return new Date(captureTime.getTime());
+ } else {
+ return null;
+ }
+ }
+
+ public void setCaptureTime(Date captureTime) {
+ if (captureTime != null) {
+ this.captureTime = new Date(captureTime.getTime());
+ } else {
+ this.captureTime = null;
+ }
+ }
+
+ private int activeUsers;
+
+ public int getActiveUsers() {
+ return activeUsers;
+ }
+
+ public void setActiveUsers(int activeUsers) {
+ this.activeUsers = activeUsers;
+ }
+
+ private int activeDevices;
+
+ public int getActiveDevices() {
+ return activeDevices;
+ }
+
+ public void setActiveDevices(int activeDevices) {
+ this.activeDevices = activeDevices;
+ }
+
+ private int requests;
+
+ public int getRequests() {
+ return requests;
+ }
+
+ public void setRequests(int requests) {
+ this.requests = requests;
+ }
+
+ private int messagesReceived;
+
+ public int getMessagesReceived() {
+ return messagesReceived;
+ }
+
+ public void setMessagesReceived(int messagesReceived) {
+ this.messagesReceived = messagesReceived;
+ }
+
+ private int messagesStored;
+
+ public int getMessagesStored() {
+ return messagesStored;
+ }
+
+ public void setMessagesStored(int messagesStored) {
+ this.messagesStored = messagesStored;
+ }
+
+}
diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java
index 23397b51c..d1f92ea0a 100644
--- a/src/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/org/traccar/protocol/AplicomProtocolDecoder.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.
@@ -27,6 +27,7 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
@@ -75,11 +76,12 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
return unitId;
}
- private static final int DEFAULT_SELECTOR = 0x0002FC;
+ private static final int DEFAULT_SELECTOR_D = 0x0002fC;
+ private static final int DEFAULT_SELECTOR_E = 0x007ffc;
private static final int EVENT_DATA = 119;
- private void decodeEventData(int event, ChannelBuffer buf) {
+ private void decodeEventData(Position position, ChannelBuffer buf, int event) {
switch (event) {
case 2:
case 40:
@@ -106,6 +108,9 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
case 130:
buf.readUnsignedInt(); // incorrect
break;
+ case 188:
+ decodeEB(position, buf);
+ break;
default:
break;
}
@@ -191,44 +196,12 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
}
- @Override
- protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.readUnsignedByte(); // marker
- int version = buf.readUnsignedByte();
-
- String imei;
- if ((version & 0x80) != 0) {
- imei = String.valueOf((buf.readUnsignedInt() << (3 * 8)) | buf.readUnsignedMedium());
- } else {
- imei = String.valueOf(imeiFromUnitId(buf.readUnsignedMedium()));
- }
-
- buf.readUnsignedShort(); // length
-
- int selector = DEFAULT_SELECTOR;
- if ((version & 0x40) != 0) {
- selector = buf.readUnsignedMedium();
- }
-
- Position position = new Position();
- position.setProtocol(getProtocolName());
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
- int event = buf.readUnsignedByte();
- position.set(Position.KEY_EVENT, event);
- position.set("eventInfo", buf.readUnsignedByte());
+ private void decodeD(Position position, ChannelBuffer buf, int selector, int event) {
if ((selector & 0x0008) != 0) {
position.setValid((buf.readUnsignedByte() & 0x40) != 0);
} else {
- return null; // no location data
+ getLastLocation(position, null);
}
if ((selector & 0x0004) != 0) {
@@ -313,14 +286,261 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x1000) != 0) {
- decodeEventData(event, buf);
+ decodeEventData(position, buf, event);
}
if (Context.getConfig().getBoolean(getProtocolName() + ".can")
&& buf.readable() && (selector & 0x1000) != 0 && event == EVENT_DATA) {
-
decodeCanData(buf, position);
}
+ }
+
+ private void decodeE(Position position, ChannelBuffer buf, int selector) {
+
+ if ((selector & 0x0008) != 0) {
+ position.set("tachographEvent", buf.readUnsignedShort());
+ }
+
+ if ((selector & 0x0004) != 0) {
+ getLastLocation(position, new Date(buf.readUnsignedInt() * 1000));
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if ((selector & 0x0010) != 0) {
+ String time = buf.readUnsignedByte() + "s " + buf.readUnsignedByte() + "m " + buf.readUnsignedByte() + "h "
+ + buf.readUnsignedByte() + "M " + buf.readUnsignedByte() + "D " + buf.readUnsignedByte() + "Y "
+ + buf.readByte() + "m " + buf.readByte() + "h";
+ position.set("tachographTime", time);
+ }
+
+ position.set("workState", buf.readUnsignedByte());
+ position.set("driver1State", buf.readUnsignedByte());
+ position.set("driver2State", buf.readUnsignedByte());
+
+ if ((selector & 0x0020) != 0) {
+ position.set("tachographStatus", buf.readUnsignedByte());
+ }
+
+ if ((selector & 0x0040) != 0) {
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() / 256.0);
+ }
+
+ if ((selector & 0x0080) != 0) {
+ position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 5);
+ }
+
+ if ((selector & 0x0100) != 0) {
+ position.set(Position.KEY_TRIP_ODOMETER, buf.readUnsignedInt() * 5);
+ }
+
+ if ((selector & 0x8000) != 0) {
+ position.set("kFactor", buf.readUnsignedShort() * 0.001 + " pulses/m");
+ }
+
+ if ((selector & 0x0200) != 0) {
+ position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125);
+ }
+
+ if ((selector & 0x0400) != 0) {
+ position.set("extraInfo", buf.readUnsignedShort());
+ }
+
+ if ((selector & 0x0800) != 0) {
+ position.set(Position.KEY_VIN, buf.readBytes(18).toString(StandardCharsets.US_ASCII).trim());
+ }
+ }
+
+ private void decodeH(Position position, ChannelBuffer buf, int selector) {
+
+ if ((selector & 0x0004) != 0) {
+ getLastLocation(position, new Date(buf.readUnsignedInt() * 1000));
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if ((selector & 0x0040) != 0) {
+ buf.readUnsignedInt(); // reset time
+ }
+
+ if ((selector & 0x2000) != 0) {
+ buf.readUnsignedShort(); // snapshot counter
+ }
+
+ int index = 1;
+ while (buf.readableBytes() > 0) {
+
+ position.set("h" + index + "Index", buf.readUnsignedByte());
+
+ buf.readUnsignedShort(); // length
+
+ int n = buf.readUnsignedByte();
+ int m = buf.readUnsignedByte();
+
+ position.set("h" + index + "XLength", n);
+ position.set("h" + index + "YLength", m);
+
+ if ((selector & 0x0008) != 0) {
+ position.set("h" + index + "XType", buf.readUnsignedByte());
+ position.set("h" + index + "YType", buf.readUnsignedByte());
+ position.set("h" + index + "Parameters", buf.readUnsignedByte());
+ }
+
+ boolean percentageFormat = (selector & 0x0020) != 0;
+
+ StringBuilder data = new StringBuilder();
+ for (int i = 0; i < n * m; i++) {
+ if (percentageFormat) {
+ data.append(buf.readUnsignedByte() * 0.5).append("%").append(" ");
+ } else {
+ data.append(buf.readUnsignedShort()).append(" ");
+ }
+ }
+
+ position.set("h" + index + "Data", data.toString().trim());
+
+ position.set("h" + index + "Total", buf.readUnsignedInt());
+
+ if ((selector & 0x0010) != 0) {
+ int k = buf.readUnsignedByte();
+
+ data = new StringBuilder();
+ for (int i = 1; i < n; i++) {
+ if (k == 1) {
+ data.append(buf.readByte()).append(" ");
+ } else if (k == 2) {
+ data.append(buf.readShort()).append(" ");
+ }
+ }
+ position.set("h" + index + "XLimits", data.toString().trim());
+
+ data = new StringBuilder();
+ for (int i = 1; i < m; i++) {
+ if (k == 1) {
+ data.append(buf.readByte()).append(" ");
+ } else if (k == 2) {
+ data.append(buf.readShort()).append(" ");
+ }
+ }
+ position.set("h" + index + "YLimits", data.toString().trim());
+ }
+
+ index += 1;
+ }
+ }
+
+ private void decodeEB(Position position, ChannelBuffer buf) {
+
+ if (buf.readByte() != (byte) 'E' || buf.readByte() != (byte) 'B') {
+ return;
+ }
+
+ buf.readUnsignedByte(); // version
+ buf.readUnsignedShort(); // event
+ buf.readUnsignedByte(); // data validity
+ buf.readUnsignedByte(); // towed
+ buf.readUnsignedShort(); // length
+
+ while (buf.readableBytes() > 0) {
+ buf.readUnsignedByte(); // towed position
+ int type = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+
+ if (type == 0x01) {
+ position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length)));
+ } else if (type == 0x02) {
+ position.set("wheelSpeed", buf.readUnsignedShort() / 256.0);
+ position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0);
+ position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5);
+ position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0);
+ } else if (type == 0x03) {
+ position.set("axleLoadSum", buf.readUnsignedShort() * 2);
+ } else if (type == 0x04) {
+ position.set("tyrePressure", buf.readUnsignedByte() * 10);
+ position.set("pneumaticPressure", buf.readUnsignedByte() * 5);
+ } else if (type == 0x05) {
+ position.set("brakeLining", buf.readUnsignedByte() * 0.4);
+ position.set("brakeTemperature", buf.readUnsignedByte() * 10);
+ } else if (type == 0x06) {
+ position.set("totalDistance", buf.readUnsignedInt() * 5);
+ position.set("tripDistance", buf.readUnsignedInt() * 5);
+ position.set("serviceDistance", (buf.readUnsignedInt() - 2105540607) * 5);
+ } else if (type == 0x0A) {
+ position.set("brakeData", ChannelBuffers.hexDump(buf.readBytes(length)));
+ } else if (type == 0x0B) {
+ position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length)));
+ } else if (type == 0x0C) {
+ position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length)));
+ } else if (type == 0x0D) {
+ switch (buf.readUnsignedByte()) {
+ case 1:
+ position.set("brakeManufacturer", "Wabco");
+ break;
+ case 2:
+ position.set("brakeManufacturer", "Knorr");
+ break;
+ case 3:
+ position.set("brakeManufacturer", "Haldex");
+ break;
+ default:
+ position.set("brakeManufacturer", "Unknown");
+ break;
+ }
+ buf.readUnsignedByte();
+ buf.readBytes(17); // vin
+ position.set("towedDetectionStatus", buf.readUnsignedByte());
+ } else if (type == 0x0E) {
+ buf.skipBytes(length);
+ }
+ }
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ char protocol = (char) buf.readByte();
+ int version = buf.readUnsignedByte();
+
+ String imei;
+ if ((version & 0x80) != 0) {
+ imei = String.valueOf((buf.readUnsignedInt() << (3 * 8)) | buf.readUnsignedMedium());
+ } else {
+ imei = String.valueOf(imeiFromUnitId(buf.readUnsignedMedium()));
+ }
+
+ buf.readUnsignedShort(); // length
+
+ int selector = DEFAULT_SELECTOR_D;
+ if (protocol == 'E') {
+ selector = DEFAULT_SELECTOR_E;
+ }
+ if ((version & 0x40) != 0) {
+ selector = buf.readUnsignedMedium();
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int event = buf.readUnsignedByte();
+ position.set(Position.KEY_EVENT, event);
+ position.set("eventInfo", buf.readUnsignedByte());
+
+ if (protocol == 'D') {
+ decodeD(position, buf, selector, event);
+ } else if (protocol == 'E') {
+ decodeE(position, buf, selector);
+ } else if (protocol == 'H') {
+ decodeH(position, buf, selector);
+ } else {
+ return null;
+ }
return position;
}
diff --git a/src/org/traccar/protocol/CellocatorProtocol.java b/src/org/traccar/protocol/CellocatorProtocol.java
index 6d8eea8f5..e5f6ac8f6 100644
--- a/src/org/traccar/protocol/CellocatorProtocol.java
+++ b/src/org/traccar/protocol/CellocatorProtocol.java
@@ -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;
@@ -40,6 +41,15 @@ public class CellocatorProtocol extends BaseProtocol {
};
server.setEndianness(ByteOrder.LITTLE_ENDIAN);
serverList.add(server);
+
+ server = new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("objectDecoder", new CellocatorProtocolDecoder(CellocatorProtocol.this));
+ }
+ };
+ server.setEndianness(ByteOrder.LITTLE_ENDIAN);
+ serverList.add(server);
}
}
diff --git a/src/org/traccar/protocol/CguardProtocol.java b/src/org/traccar/protocol/CguardProtocol.java
new file mode 100644
index 000000000..1e6d212c8
--- /dev/null
+++ b/src/org/traccar/protocol/CguardProtocol.java
@@ -0,0 +1,47 @@
+/*
+ * 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.LineBasedFrameDecoder;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.jboss.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class CguardProtocol extends BaseProtocol {
+
+ public CguardProtocol() {
+ super("cguard");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectDecoder", new CguardProtocolDecoder(CguardProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/CguardProtocolDecoder.java b/src/org/traccar/protocol/CguardProtocolDecoder.java
new file mode 100644
index 000000000..1646363e5
--- /dev/null
+++ b/src/org/traccar/protocol/CguardProtocolDecoder.java
@@ -0,0 +1,91 @@
+/*
+ * 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.DeviceSession;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class CguardProtocolDecoder extends BaseProtocolDecoder {
+
+ public CguardProtocolDecoder(CguardProtocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("NV:")
+ .number("(dd)(dd)(dd) ") // date
+ .number("(dd)(dd)(dd):") // time
+ .number("(-?d+.d+):") // longitude
+ .number("(-?d+.d+):") // latitude
+ .number("(d+.?d*):") // speed
+ .number("(?:NAN|(d+.?d*)):") // accuracy
+ .number("(?:NAN|(d+.?d*)):") // course
+ .number("(?:NAN|(d+.?d*))") // altitude
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (sentence.startsWith("ID:") || sentence.startsWith("IDRO:")) {
+ getDeviceSession(channel, remoteAddress, sentence.substring(sentence.indexOf(':') + 1));
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.setValid(true);
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+
+ position.set(Position.KEY_ACCURACY, parser.nextDouble());
+
+ position.setCourse(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/org/traccar/protocol/Gt06ProtocolEncoder.java
index e478424f9..e78a1b388 100644
--- a/src/org/traccar/protocol/Gt06ProtocolEncoder.java
+++ b/src/org/traccar/protocol/Gt06ProtocolEncoder.java
@@ -66,9 +66,9 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return encodeContent(alternative ? "DYD,123456#\r\n" : "Relay,1#");
+ return encodeContent(alternative ? "DYD,123456#" : "Relay,1#");
case Command.TYPE_ENGINE_RESUME:
- return encodeContent(alternative ? "HFYD,123456#\r\n" : "Relay,0#");
+ return encodeContent(alternative ? "HFYD,123456#" : "Relay,0#");
default:
Log.warning(new UnsupportedOperationException(command.getType()));
break;
diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
index b7193d24b..b30bd7b85 100644
--- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.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.
@@ -43,7 +43,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes / 60;
}
- private Position decodeNormalMessage(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ private Position decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
Position position = new Position();
position.setProtocol(getProtocolName());
@@ -120,7 +120,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private static final Pattern PATTERN = new PatternBuilder()
+ private static final Pattern PATTERN_W01 = new PatternBuilder()
.text("(")
.number("(d+),") // id
.text("W01,") // type
@@ -138,25 +138,22 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // gsm signal
.number("(d+),") // alert type
.any()
- .text(")")
.compile();
- private Position decodeAlertMessage(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ private Position decodeW01(String sentence, Channel channel, SocketAddress remoteAddress) {
- Parser parser = new Parser(PATTERN, buf.toString(StandardCharsets.US_ASCII));
+ Parser parser = new Parser(PATTERN_W01, sentence);
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setLongitude(parser.nextCoordinate());
@@ -176,6 +173,68 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private static final Pattern PATTERN_U01 = new PatternBuilder()
+ .text("(")
+ .number("(d+),") // id
+ .number("Udd,") // type
+ .number("d+,").optional() // alarm
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time
+ .expression("([TF]),") // validity
+ .number("(d+.d+),([NS]),") // latitude
+ .number("(d+.d+),([EW]),") // longitude
+ .number("(d+.?d*),") // speed
+ .number("(d+),") // course
+ .number("(d+),") // satellites
+ .number("(d+%),") // battery
+ .expression("([01]+),") // status
+ .number("(d+),") // cid
+ .number("(d+),") // lac
+ .number("(d+),") // gsm signal
+ .number("(d+),") // odometer
+ .number("(d+)") // index
+ .any()
+ .compile();
+
+ private Position decodeU01(String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_U01, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.setValid(parser.next().equals("T"));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+
+ position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_BATTERY, parser.next());
+ position.set(Position.KEY_STATUS, parser.nextInt(2));
+ position.set(Position.KEY_CID, parser.nextInt());
+ position.set(Position.KEY_LAC, parser.nextInt());
+ position.set(Position.KEY_GSM, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextLong() * 1000);
+ position.set(Position.KEY_INDEX, parser.nextInt());
+
+ return position;
+ }
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -184,9 +243,14 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
char first = (char) buf.getByte(0);
if (first == '$') {
- return decodeNormalMessage(buf, channel, remoteAddress);
+ return decodeBinary(buf, channel, remoteAddress);
} else if (first == '(') {
- return decodeAlertMessage(buf, channel, remoteAddress);
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
+ if (sentence.contains("W01")) {
+ return decodeW01(sentence, channel, remoteAddress);
+ } else {
+ return decodeU01(sentence, channel, remoteAddress);
+ }
}
return null;
diff --git a/src/org/traccar/protocol/OigoProtocolDecoder.java b/src/org/traccar/protocol/OigoProtocolDecoder.java
index 799f47ea3..bbea38183 100644
--- a/src/org/traccar/protocol/OigoProtocolDecoder.java
+++ b/src/org/traccar/protocol/OigoProtocolDecoder.java
@@ -54,7 +54,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
DeviceSession deviceSession;
switch (BitUtil.to(tag, 3)) {
case 0:
- String imei = ChannelBuffers.hexDump(buf.readBytes(9)).substring(1, 1 + 15);
+ String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
deviceSession = getDeviceSession(channel, remoteAddress, imei);
break;
case 1:
@@ -75,6 +75,8 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+
int mask = buf.readInt();
if (BitUtil.check(mask, 0)) {
diff --git a/src/org/traccar/reports/Summary.java b/src/org/traccar/reports/Summary.java
index 44fb1dd4c..d4171f644 100644
--- a/src/org/traccar/reports/Summary.java
+++ b/src/org/traccar/reports/Summary.java
@@ -37,7 +37,7 @@ public final class Summary {
private static SummaryReport calculateSummaryResult(long deviceId, Date from, Date to) throws SQLException {
SummaryReport result = new SummaryReport();
result.setDeviceId(deviceId);
- result.setDeviceName(Context.getDeviceManager().getDeviceById(deviceId).getName());
+ result.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to);
if (positions != null && !positions.isEmpty()) {
Position firstPosition = null;
@@ -60,7 +60,9 @@ public final class Summary {
speedSum += position.getSpeed();
result.setMaxSpeed(position.getSpeed());
}
- result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition));
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupConfigBoolean(deviceId, "report.ignoreOdometer", false);
+ result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition, !ignoreOdometer));
result.setAverageSpeed(speedSum / positions.size());
}
return result;
diff --git a/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java
index 2171d5f93..f0a10edbd 100644
--- a/src/org/traccar/reports/Trips.java
+++ b/src/org/traccar/reports/Trips.java
@@ -53,15 +53,16 @@ public final class Trips {
long tripDuration = endTrip.getFixTime().getTime() - positions.get(startIndex).getFixTime().getTime();
long deviceId = startTrip.getDeviceId();
trip.setDeviceId(deviceId);
- String deviceName = Context.getDeviceManager().getDeviceById(deviceId).getName();
- trip.setDeviceName(deviceName);
+ trip.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
trip.setStartPositionId(startTrip.getId());
trip.setStartTime(startTrip.getFixTime());
trip.setStartAddress(startTrip.getAddress());
trip.setEndPositionId(endTrip.getId());
trip.setEndTime(endTrip.getFixTime());
trip.setEndAddress(endTrip.getAddress());
- trip.setDistance(ReportUtils.calculateDistance(startTrip, endTrip));
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupConfigBoolean(deviceId, "report.ignoreOdometer", false);
+ trip.setDistance(ReportUtils.calculateDistance(startTrip, endTrip, !ignoreOdometer));
trip.setDuration(tripDuration);
trip.setAverageSpeed(speedSum / (endIndex - startIndex));
trip.setMaxSpeed(speedMax);
diff --git a/src/org/traccar/web/CsvBuilder.java b/src/org/traccar/web/CsvBuilder.java
index a25b839a9..f59aabc7f 100644
--- a/src/org/traccar/web/CsvBuilder.java
+++ b/src/org/traccar/web/CsvBuilder.java
@@ -37,10 +37,10 @@ public class CsvBuilder {
SortedSet<Method> methods = new TreeSet<Method>(new Comparator<Method>() {
@Override
public int compare(Method m1, Method m2) {
- if (m1.getName().equals("getAttributes") & !m1.getName().equals(m2.getName())) {
+ if (m1.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
return 1;
}
- if (m2.getName().equals("getAttributes") & !m1.getName().equals(m2.getName())) {
+ if (m2.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
return -1;
}
return m1.getName().compareTo(m2.getName());
diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java
index e022a9285..ec15ea2be 100644
--- a/src/org/traccar/web/WebServer.java
+++ b/src/org/traccar/web/WebServer.java
@@ -34,6 +34,7 @@ import org.traccar.api.CorsResponseFilter;
import org.traccar.api.ObjectMapperProvider;
import org.traccar.api.ResourceErrorHandler;
import org.traccar.api.SecurityRequestFilter;
+import org.traccar.api.resource.AttributeAliasResource;
import org.traccar.api.resource.CommandResource;
import org.traccar.api.resource.GroupPermissionResource;
import org.traccar.api.resource.ServerResource;
@@ -161,7 +162,7 @@ public class WebServer {
GroupResource.class, DeviceResource.class, PositionResource.class,
CommandTypeResource.class, EventResource.class, GeofenceResource.class,
DeviceGeofenceResource.class, GeofencePermissionResource.class, GroupGeofenceResource.class,
- NotificationResource.class, ReportResource.class);
+ NotificationResource.class, ReportResource.class, AttributeAliasResource.class);
servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
handlers.addHandler(servletHandler);
diff --git a/test/org/traccar/protocol/AplicomProtocolDecoderTest.java b/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
index 26844994e..cb39b5d2e 100644
--- a/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
@@ -10,6 +10,24 @@ public class AplicomProtocolDecoderTest extends ProtocolTest {
AplicomProtocolDecoder decoder = new AplicomProtocolDecoder(new AplicomProtocol());
+ verifyAttributes(decoder, binary(
+ "48C1014143B4493145004900203F6D014B5557C20003000015060110FF00C800000000000000003D01141E283C500100260404010200000000000000000000000000C8000000000000010200110019001E0064019003E8"));
+
+ verifyAttributes(decoder, binary(
+ "48c10144b9de54e6b2008700205f710a57d23ec957d23b8d00000000300d0106ff00000000000000000000000000000000000000000000000000000000000000010a141e28323c46505a646e7801000f020104ff000000000000000000010102000f020104ff000000000000000000010103000f020104ff000000000000000000010105000f020104ff0000000000000000000101"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6dd1970230d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6dd9170250d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "45c20145931876ffb2007100ffff6d00000057c6de0970270d087b1f7d7f0000d0c1000000003580000035801f40ffff5001574442393036363035533132333435363700014142432d333435202020202020000000000000000000000000000000000000000000000001123130343632343639373030303030303100000000"));
+
+ verifyAttributes(decoder, binary(
+ "44c3014645e8ecff3c00ea03ffffbc00f457d68a6557d68a6303bb55fa018843da1100009881000000000000000000000000000000000000000000000000000000000000000000000000000000ff0056007600000000000000014542016d0001010095070e14014645e8ecff3c57d68a6403bb55fa018843dac0010d14ff050102030405060708090a0b0c0d0e0f10112a01010730343f3c1ff5cf01020700007d007d23010103022f2e01060c67452301efcdab8967452301010b10000000007d007d007d7dffffffffffff010a2400000000000000010000000000000000ffffffffffffffff00010001ffff00000000ffff010c02fec6"));
+
verifyPosition(decoder, binary(
"44c3014645e8e91b66002300a21f0b01f056d3e62856d3e626031f845f00c6ee440800000000000000000017bd1cb30000"));
@@ -19,7 +37,7 @@ public class AplicomProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, binary(
"44c3014645e8e91b66001f00221f0b01f456ba1e0d56ba1e0b031f842200c6ef550c000000000017bd1cb30004"));
- verifyNothing(decoder, binary(
+ verifyAttributes(decoder, binary(
"44c3014645e8e9bada003e03fff7070055a4f24200000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00000001000000000000000044c3014645e8e9bada003e03fff77bff55a4f24300000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff00300002000000000000000044c3014645e8e9bada003e03fff7690655a4f24500000081000000000000000000000000000000000000000000000000000000000000000000000000000000ff003000030000000000000000"));
verifyPosition(decoder, binary(
diff --git a/test/org/traccar/protocol/CguardProtocolDecoderTest.java b/test/org/traccar/protocol/CguardProtocolDecoderTest.java
new file mode 100644
index 000000000..49d037f8f
--- /dev/null
+++ b/test/org/traccar/protocol/CguardProtocolDecoderTest.java
@@ -0,0 +1,54 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class CguardProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ CguardProtocolDecoder decoder = new CguardProtocolDecoder(new CguardProtocol());
+
+ verifyNothing(decoder, text(
+ "IDRO:354868050655283"));
+
+ verifyPosition(decoder, text(
+ "NV:160711 044023:54.342907:48.582590:0:NAN:0:110.1"));
+
+ verifyPosition(decoder, text(
+ "NV:160711 044023:54.342907:-148.582590:0:NAN:0:110.1"));
+
+ verifyNothing(decoder, text(
+ "BC:160711 044023:CSQ1:48:NSQ1:7:NSQ2:1:BAT1:98:PWR1:11.7:CLG1:NAN"));
+
+ verifyNothing(decoder, text(
+ "BC:160711 044524:CSQ1:61:NSQ1:18:BAT1:98:PWR1:11.7:CLG1:NAN"));
+
+ verifyNothing(decoder, text(
+ "VERSION:3.3"));
+
+ verifyPosition(decoder, text(
+ "NV:160420 101902:55.799425:37.674033:0.94:NAN:213.59:156.6"));
+
+ verifyNothing(decoder, text(
+ "BC:160628 081024:CSQ1:32:NSQ1:10:BAT1:100"));
+
+ verifyNothing(decoder, text(
+ "BC:160628 081033:NSQ2:0"));
+
+ verifyPosition(decoder, text(
+ "NV:160630 151537:55.799913:37.674267:0.7:NAN:10.21:174.9"));
+
+ verifyNothing(decoder, text(
+ "BC:160630 153316:BAT1:76"));
+
+ verifyNothing(decoder, text(
+ "BC:160630 153543:NSQ2:0"));
+
+ verifyNothing(decoder, text(
+ "PING"));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java
index b95d8ddce..4bbfb5627 100644
--- a/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java
@@ -11,6 +11,36 @@ public class Jt600ProtocolDecoderTest extends ProtocolTest {
Jt600ProtocolDecoder decoder = new Jt600ProtocolDecoder(new Jt600Protocol());
+ verifyPosition(decoder, buffer(
+ "(3301210003,U01,040812,185302,T,22.564025,N,113.242329,E,5.21,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U02,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U03,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,00000000000011,10133,5173,22,100,1)"));
+
+ verifyNothing(decoder, buffer(
+ "(3301210003,U04)"));
+
+ verifyPosition(decoder, buffer(
+ "(3301210003,U06,1,040812,185302,T,22.564025,N,113.242329,E,5,152,9,32%,0000000000011,10133,5173,22,100,1,300,100,10)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U01,220916,135251,T,9.552607,N,13.658292,W,0.31,0,9,0%,00001001000000,11012,10,27,0,0,33)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U01,010100,000024,F,0.000000,N,0.000000,E,0.00,0,0,100%,00000001000000,263,1,18,0,0,33)"));
+
+ verifyNothing(decoder, buffer(
+ "(3460311327,@JT)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U06,11,220916,135643,T,9.552553,N,13.658265,W,0.61,0,9,100%,00000001000000,11012,10,30,0,0,126,0,30)"));
+
+ verifyPosition(decoder, buffer(
+ "(3460311327,U06,10,220916,140619,T,9.552495,N,13.658227,W,0.43,0,7,0%,00101001000000,11012,10,0,0,0,126,0,30)"));
+
verifyPosition(decoder, binary(
"24311021600111001B16021105591022329862114046227B0598095080012327951435161F"),
position("2011-02-16 05:59:10.000", true, 22.54977, -114.07705));
@@ -31,11 +61,11 @@ public class Jt600ProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, buffer(
"(3120820029,W01,02553.3555,E,2438.0997,S,A,171012,053339,0,8,20,6,31,5,20,20)"));
- /*verifyPosition(decoder, text( ChannelBuffers.copiedBuffer(
- "(3330104377,U01,010100,010228,F,00.000000,N,000.000000,E,0,0,0,0%,00001000000000,741,14,22,0,206)", StandardCharsets.US_ASCII)));
+ verifyPosition(decoder, buffer(
+ "(3330104377,U01,010100,010228,F,00.000000,N,000.000000,E,0,0,0,0%,00001000000000,741,14,22,0,206)"));
- verifyPosition(decoder, text( ChannelBuffers.copiedBuffer(
- "(6221107674,2,U09,129,2,A,280513113036,E,02711.0500,S,1721.0876,A,030613171243,E,02756.7618,S,2300.0325,3491,538200,14400,1)",StandardCharsets.US_ASCII))));*/
+ verifyNothing(decoder, buffer(
+ "(6221107674,2,U09,129,2,A,280513113036,E,02711.0500,S,1721.0876,A,030613171243,E,02756.7618,S,2300.0325,3491,538200,14400,1)"));
}
diff --git a/test/org/traccar/protocol/OigoProtocolDecoderTest.java b/test/org/traccar/protocol/OigoProtocolDecoderTest.java
index b75f3162b..14c34ae7c 100644
--- a/test/org/traccar/protocol/OigoProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/OigoProtocolDecoderTest.java
@@ -16,6 +16,9 @@ public class OigoProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, binary(
"7e004200000014631000258257000000ffff02d1690e00051f690e00051f0696dbd204bdfde31a070000b307100f35c0106305f500000000010908010402200104ffff8001"));
+ verifyPosition(decoder, binary(
+ "7e004200000014631000258257000000ffff0d82691300001669130000160696dbd804bdfdbb1a0800000007101035a2106905f500000000010908010402200104ffff8001"));
+
}
}
diff --git a/tools/hex.sh b/tools/hex.sh
index cfae14bb6..78cd8fa38 100755
--- a/tools/hex.sh
+++ b/tools/hex.sh
@@ -10,26 +10,34 @@
if [ $# -lt 2 ]
then
- echo "USAGE: $0 <port> <hex>"
+ echo "USAGE: $0 <host> <port> <hex>"
+ echo "If only <port> and <hex> are present, <host> defaults to localhost."
exit 1
fi
+host="$1"; port="$2"; hex="$3";
+
+if [ $# -eq 2 ]
+then
+ host="localhost"; port="$1"; hex="$2";
+fi
+
send_hex_udp () {
- echo $2 | xxd -r -p | nc -u -w 0 localhost $1
+ echo "$hex" | xxd -r -p | nc -u -w 0 "$host" "$port"
}
send_hex_tcp () {
- echo $2 | xxd -r -p | nc localhost $1
+ echo "$hex" | xxd -r -p | nc "$host" "$port"
}
send_text_udp () {
- echo -n -e $2 | nc -u -w 0 localhost $1
+ echo -n -e "$hex" | nc -u -w 0 "$host" "$port"
}
send_text_tcp () {
- echo -n -e $2 | nc localhost $1
+ echo -n -e "$hex" | nc "$host" "$port"
}
-send_hex_tcp $1 $2
+send_hex_tcp "$host" "$port" "$hex"
exit $?