aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/traccar')
-rw-r--r--src/org/traccar/BasePipelineFactory.java34
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java2
-rw-r--r--src/org/traccar/Context.java23
-rw-r--r--src/org/traccar/FilterHandler.java17
-rw-r--r--src/org/traccar/LocationProviderHandler.java71
-rw-r--r--src/org/traccar/MainEventHandler.java4
-rw-r--r--src/org/traccar/database/ConnectionManager.java78
-rw-r--r--src/org/traccar/database/DataManager.java6
-rw-r--r--src/org/traccar/location/BaseLocationProvider.java52
-rw-r--r--src/org/traccar/location/LocationProvider.java32
-rw-r--r--src/org/traccar/location/MozillaLocationProvider.java75
-rw-r--r--src/org/traccar/location/OpenCellIdLocationProvider.java63
-rw-r--r--src/org/traccar/model/Event.java3
-rw-r--r--src/org/traccar/model/Position.java10
-rw-r--r--src/org/traccar/protocol/EelinkProtocolDecoder.java5
-rw-r--r--src/org/traccar/protocol/FlextrackProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/Gl200ProtocolDecoder.java14
-rw-r--r--src/org/traccar/protocol/Gps103ProtocolDecoder.java36
-rw-r--r--src/org/traccar/protocol/Gt02ProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/HuabaoProtocolDecoder.java113
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolDecoder.java9
-rw-r--r--src/org/traccar/protocol/MegastekProtocolDecoder.java28
-rw-r--r--src/org/traccar/protocol/MeiligaoFrameDecoder.java4
-rw-r--r--src/org/traccar/protocol/MeitrackProtocolDecoder.java10
-rw-r--r--src/org/traccar/protocol/SuntechProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/T800xProtocolDecoder.java12
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Tk103ProtocolDecoder.java36
-rw-r--r--src/org/traccar/protocol/Tlt2hProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TotemProtocolDecoder.java21
-rw-r--r--src/org/traccar/protocol/WatchProtocolDecoder.java8
-rw-r--r--src/org/traccar/web/AsyncServlet.java47
-rw-r--r--src/org/traccar/web/JsonConverter.java2
33 files changed, 738 insertions, 91 deletions
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index c0952db86..c011fb015 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -19,6 +19,7 @@ import java.net.InetSocketAddress;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
@@ -39,6 +40,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
private FilterHandler filterHandler;
private DistanceHandler distanceHandler;
private ReverseGeocoderHandler reverseGeocoderHandler;
+ private LocationProviderHandler locationProviderHandler;
private static final class OpenChannelHandler extends SimpleChannelHandler {
@@ -102,6 +104,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
Context.getReverseGeocoder(), Context.getConfig().getBoolean("geocode.processInvalidPositions"));
}
+ if (Context.getLocationProvider() != null) {
+ locationProviderHandler = new LocationProviderHandler(Context.getLocationProvider());
+ }
+
if (Context.getConfig().getBoolean("distance.enable")) {
distanceHandler = new DistanceHandler();
}
@@ -119,17 +125,26 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
if (Context.isLoggerEnabled()) {
pipeline.addLast("logger", new StandardLoggingHandler());
}
+
addSpecificHandlers(pipeline);
- if (filterHandler != null) {
- pipeline.addLast("filter", filterHandler);
- }
+
if (distanceHandler != null) {
pipeline.addLast("distance", distanceHandler);
}
if (reverseGeocoderHandler != null) {
pipeline.addLast("geocoder", reverseGeocoderHandler);
}
+ if (locationProviderHandler != null) {
+ pipeline.addLast("location", locationProviderHandler);
+ }
pipeline.addLast("remoteAddress", new RemoteAddressHandler());
+
+ addDynamicHandlers(pipeline);
+
+ if (filterHandler != null) {
+ pipeline.addLast("filter", filterHandler);
+ }
+
if (Context.getDataManager() != null) {
pipeline.addLast("dataHandler", new DefaultDataHandler());
}
@@ -140,4 +155,17 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
return pipeline;
}
+ private void addDynamicHandlers(ChannelPipeline pipeline) {
+ if (Context.getConfig().hasKey("extra.handlers")) {
+ String[] handlers = Context.getConfig().getString("extra.handlers").split(",");
+ for (int i = 0; i < handlers.length; i++) {
+ try {
+ pipeline.addLast("extraHandler." + i, (ChannelHandler) Class.forName(handlers[i]).newInstance());
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException error) {
+ Log.warning(error);
+ }
+ }
+ }
+ }
+
}
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index f8abdcc85..bd91f5e09 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -75,6 +75,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
public void getLastLocation(Position position, Date deviceTime) {
+ position.setOutdated(true);
+
Position last = Context.getConnectionManager().getLastPosition(getDeviceId());
if (last != null) {
position.setFixTime(last.getFixTime());
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 113ed02ec..c833d0c2e 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -29,6 +29,9 @@ import org.traccar.geocode.NominatimReverseGeocoder;
import org.traccar.geocode.OpenCageReverseGeocoder;
import org.traccar.geocode.ReverseGeocoder;
import org.traccar.helper.Log;
+import org.traccar.location.LocationProvider;
+import org.traccar.location.MozillaLocationProvider;
+import org.traccar.location.OpenCellIdLocationProvider;
import org.traccar.web.WebServer;
public final class Context {
@@ -78,6 +81,12 @@ public final class Context {
return reverseGeocoder;
}
+ private static LocationProvider locationProvider;
+
+ public static LocationProvider getLocationProvider() {
+ return locationProvider;
+ }
+
private static WebServer webServer;
public static WebServer getWebServer() {
@@ -144,6 +153,20 @@ public final class Context {
}
}
+ if (config.getBoolean("location.enable")) {
+ String type = config.getString("location.type", "opencellid");
+ String key = config.getString("location.key");
+
+ switch (type) {
+ case "mozilla":
+ locationProvider = new MozillaLocationProvider();
+ break;
+ default:
+ locationProvider = new OpenCellIdLocationProvider(key);
+ break;
+ }
+ }
+
if (config.getBoolean("web.enable")) {
if (config.getString("web.type", "new").equals("new")
|| config.getString("web.type", "new").equals("api")) {
diff --git a/src/org/traccar/FilterHandler.java b/src/org/traccar/FilterHandler.java
index 79f40c979..75f2bfd2c 100644
--- a/src/org/traccar/FilterHandler.java
+++ b/src/org/traccar/FilterHandler.java
@@ -17,6 +17,7 @@ package org.traccar;
import org.traccar.helper.DistanceCalculator;
import org.traccar.helper.Log;
+import org.traccar.model.Event;
import org.traccar.model.Position;
public class FilterHandler extends BaseDataHandler {
@@ -27,18 +28,20 @@ public class FilterHandler extends BaseDataHandler {
private final boolean filterZero;
private final boolean filterDuplicate;
private final boolean filterFuture;
+ private final boolean filterApproximate;
private final int filterDistance;
private final long filterLimit;
public FilterHandler(
- boolean filterInvalid, boolean filterZero, boolean filterDuplicate,
- boolean filterFuture, int filterDistance, long filterLimit) {
+ boolean filterInvalid, boolean filterZero, boolean filterDuplicate, boolean filterFuture,
+ boolean filterApproximate, int filterDistance, long filterLimit) {
this.filterInvalid = filterInvalid;
this.filterZero = filterZero;
this.filterDuplicate = filterDuplicate;
this.filterDistance = filterDistance;
this.filterFuture = filterFuture;
+ this.filterApproximate = filterApproximate;
this.filterLimit = filterLimit;
}
@@ -49,6 +52,7 @@ public class FilterHandler extends BaseDataHandler {
filterZero = config.getBoolean("filter.zero");
filterDuplicate = config.getBoolean("filter.duplicate");
filterFuture = config.getBoolean("filter.future");
+ filterApproximate = config.getBoolean("filter.approximate");
filterDistance = config.getInteger("filter.distance");
filterLimit = config.getLong("filter.limit") * 1000;
}
@@ -85,6 +89,11 @@ public class FilterHandler extends BaseDataHandler {
return filterFuture && position.getFixTime().getTime() > System.currentTimeMillis() + FILTER_FUTURE_LIMIT;
}
+ private boolean filterApproximate(Position position) {
+ Boolean approximate = (Boolean) position.getAttributes().get(Event.KEY_APPROXIMATE);
+ return filterApproximate && approximate != null && approximate;
+ }
+
private boolean filterDistance(Position position) {
if (filterDistance != 0) {
Position last = getLastPosition(position.getDeviceId());
@@ -116,8 +125,8 @@ public class FilterHandler extends BaseDataHandler {
private boolean filter(Position p) {
- boolean result = filterInvalid(p) || filterZero(p)
- || filterDuplicate(p) || filterFuture(p) || filterDistance(p);
+ boolean result = filterInvalid(p) || filterZero(p) || filterDuplicate(p)
+ || filterFuture(p) || filterApproximate(p) || filterDistance(p);
if (filterLimit(p)) {
result = false;
diff --git a/src/org/traccar/LocationProviderHandler.java b/src/org/traccar/LocationProviderHandler.java
new file mode 100644
index 000000000..62d5f1d04
--- /dev/null
+++ b/src/org/traccar/LocationProviderHandler.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015 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;
+
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelUpstreamHandler;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.MessageEvent;
+import org.traccar.location.LocationProvider;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class LocationProviderHandler implements ChannelUpstreamHandler {
+
+ private final LocationProvider locationProvider;
+
+ public LocationProviderHandler(LocationProvider locationProvider) {
+ this.locationProvider = locationProvider;
+ }
+
+ @Override
+ public void handleUpstream(final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
+ if (!(evt instanceof MessageEvent)) {
+ ctx.sendUpstream(evt);
+ return;
+ }
+
+ final MessageEvent e = (MessageEvent) evt;
+ Object message = e.getMessage();
+ if (message instanceof Position) {
+ final Position position = (Position) message;
+ if (locationProvider != null && position.getOutdated()) {
+ locationProvider.getLocation(position.getAttributes(), new LocationProvider.LocationProviderCallback() {
+ @Override
+ public void onSuccess(double latitude, double longitude) {
+ position.set(Event.KEY_APPROXIMATE, true);
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+ Channels.fireMessageReceived(ctx, position, e.getRemoteAddress());
+ }
+
+ @Override
+ public void onFailure() {
+ Channels.fireMessageReceived(ctx, position, e.getRemoteAddress());
+ }
+ });
+ } else {
+ Channels.fireMessageReceived(ctx, position, e.getRemoteAddress());
+ }
+ } else {
+ Channels.fireMessageReceived(ctx, message, e.getRemoteAddress());
+ }
+ }
+
+}
diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java
index 37f0ee387..f0ef36e5b 100644
--- a/src/org/traccar/MainEventHandler.java
+++ b/src/org/traccar/MainEventHandler.java
@@ -66,7 +66,9 @@ public class MainEventHandler extends IdleStateAwareChannelHandler {
Log.info(formatChannel(e.getChannel()) + " disconnected");
closeChannel(e.getChannel());
- Context.getConnectionManager().removeActiveDevice(e.getChannel());
+ if (ctx.getPipeline().get("httpDecoder") == null) {
+ Context.getConnectionManager().removeActiveDevice(e.getChannel());
+ }
}
@Override
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java
index 450f2f61f..e03fd1663 100644
--- a/src/org/traccar/database/ConnectionManager.java
+++ b/src/org/traccar/database/ConnectionManager.java
@@ -25,7 +25,13 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
import org.jboss.netty.channel.Channel;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.traccar.Context;
+import org.traccar.GlobalTimer;
import org.traccar.Protocol;
import org.traccar.helper.Log;
import org.traccar.model.Device;
@@ -33,15 +39,21 @@ import org.traccar.model.Position;
public class ConnectionManager {
+ private static final long DEFAULT_TIMEOUT = 600;
+
+ private final long deviceTimeout;
+
private final Map<Long, ActiveDevice> activeDevices = new HashMap<>();
private final Map<Long, Position> positions = new HashMap<>();
- private final Map<Long, Set<DataCacheListener>> listeners = new HashMap<>();
+ private final Map<Long, Set<UpdateListener>> listeners = new HashMap<>();
+ private final Map<Long, Timeout> timeouts = new HashMap<>();
public ConnectionManager(DataManager dataManager) {
+ deviceTimeout = Context.getConfig().getLong("status.timeout", DEFAULT_TIMEOUT) * 1000;
if (dataManager != null) {
try {
for (Position position : dataManager.getLatestPositions()) {
- this.positions.put(position.getDeviceId(), position);
+ positions.put(position.getDeviceId(), position);
}
} catch (SQLException error) {
Log.warning(error);
@@ -56,7 +68,7 @@ public class ConnectionManager {
public void removeActiveDevice(Channel channel) {
for (ActiveDevice activeDevice : activeDevices.values()) {
if (activeDevice.getChannel() == channel) {
- updateDevice(activeDevice.getDeviceId(), Device.STATUS_OFFLINE, new Date());
+ updateDevice(activeDevice.getDeviceId(), Device.STATUS_OFFLINE, null);
activeDevices.remove(activeDevice.getDeviceId());
break;
}
@@ -67,17 +79,52 @@ public class ConnectionManager {
return activeDevices.get(deviceId);
}
- public synchronized void updateDevice(long deviceId, String status, Date time) {
- // TODO update cache and call listener
- /*Log.debug(deviceId + " " + status + " "
- + new SimpleDateFormat(Log.DATE_FORMAT).format(time));*/
+ public synchronized void updateDevice(final long deviceId, String status, Date time) {
+ Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ if (device == null) {
+ return;
+ }
+
+ device.setStatus(status);
+ if (time != null) {
+ device.setLastUpdate(time);
+ }
+
+ Timeout timeout = timeouts.remove(deviceId);
+ if (timeout != null) {
+ timeout.cancel();
+ }
+
+ if (status.equals(Device.STATUS_ONLINE)) {
+ timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(new TimerTask() {
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ if (!timeout.isCancelled()) {
+ updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
+ }
+ }
+ }, deviceTimeout, TimeUnit.MILLISECONDS));
+ }
+
+ try {
+ Context.getDataManager().updateDeviceStatus(device);
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+
+ if (listeners.containsKey(deviceId)) {
+ for (UpdateListener listener : listeners.get(deviceId)) {
+ listener.onUpdateDevice(device);
+ }
+ }
}
public synchronized void updatePosition(Position position) {
long deviceId = position.getDeviceId();
positions.put(deviceId, position);
+
if (listeners.containsKey(deviceId)) {
- for (DataCacheListener listener : listeners.get(deviceId)) {
+ for (UpdateListener listener : listeners.get(deviceId)) {
listener.onUpdatePosition(position);
}
}
@@ -100,32 +147,33 @@ public class ConnectionManager {
return result;
}
- public interface DataCacheListener {
+ public interface UpdateListener {
+ void onUpdateDevice(Device device);
void onUpdatePosition(Position position);
}
- public void addListener(Collection<Long> devices, DataCacheListener listener) {
+ public void addListener(Collection<Long> devices, UpdateListener listener) {
for (long deviceId : devices) {
addListener(deviceId, listener);
}
}
- public synchronized void addListener(long deviceId, DataCacheListener listener) {
+ public synchronized void addListener(long deviceId, UpdateListener listener) {
if (!listeners.containsKey(deviceId)) {
- listeners.put(deviceId, new HashSet<DataCacheListener>());
+ listeners.put(deviceId, new HashSet<UpdateListener>());
}
listeners.get(deviceId).add(listener);
}
- public void removeListener(Collection<Long> devices, DataCacheListener listener) {
+ public void removeListener(Collection<Long> devices, UpdateListener listener) {
for (long deviceId : devices) {
removeListener(deviceId, listener);
}
}
- public synchronized void removeListener(long deviceId, DataCacheListener listener) {
+ public synchronized void removeListener(long deviceId, UpdateListener listener) {
if (!listeners.containsKey(deviceId)) {
- listeners.put(deviceId, new HashSet<DataCacheListener>());
+ listeners.put(deviceId, new HashSet<UpdateListener>());
}
listeners.get(deviceId).remove(listener);
}
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index 767582604..530ec1779 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -310,6 +310,12 @@ public class DataManager implements IdentityManager {
.executeUpdate();
}
+ public void updateDeviceStatus(Device device) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.updateDeviceStatus"))
+ .setObject(device)
+ .executeUpdate();
+ }
+
public void removeDevice(Device device) throws SQLException {
QueryBuilder.create(dataSource, getQuery("database.deleteDevice"))
.setObject(device)
diff --git a/src/org/traccar/location/BaseLocationProvider.java b/src/org/traccar/location/BaseLocationProvider.java
new file mode 100644
index 000000000..c422ed50a
--- /dev/null
+++ b/src/org/traccar/location/BaseLocationProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 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.location;
+
+import org.traccar.Config;
+import org.traccar.Context;
+import org.traccar.model.Event;
+
+import java.util.Map;
+
+public abstract class BaseLocationProvider implements LocationProvider {
+
+ @Override
+ public void getLocation(Map<String, Object> attributes, LocationProviderCallback callback) {
+
+ Config config = Context.getConfig();
+
+ Number mcc = (Number) attributes.get(Event.KEY_MCC);
+ if (mcc == null && config.hasKey("location.mcc")) {
+ mcc = config.getInteger("location.mcc");
+ }
+
+ Number mnc = (Number) attributes.get(Event.KEY_MNC);
+ if (mnc == null && config.hasKey("location.mnc")) {
+ mnc = config.getInteger("location.mnc");
+ }
+
+ Number lac = (Number) attributes.get(Event.KEY_LAC);
+ Number cid = (Number) attributes.get(Event.KEY_CID);
+
+ if (mcc != null && mnc != null && lac != null && cid != null) {
+ getLocation(mcc.intValue(), mnc.intValue(), lac.longValue(), cid.longValue(), callback);
+ }
+
+ }
+
+ protected abstract void getLocation(int mcc, int mnc, long lac, long cid, LocationProviderCallback callback);
+
+}
diff --git a/src/org/traccar/location/LocationProvider.java b/src/org/traccar/location/LocationProvider.java
new file mode 100644
index 000000000..2bff1a7ca
--- /dev/null
+++ b/src/org/traccar/location/LocationProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 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.location;
+
+import java.util.Map;
+
+public interface LocationProvider {
+
+ interface LocationProviderCallback {
+
+ void onSuccess(double latitude, double longitude);
+
+ void onFailure();
+
+ }
+
+ void getLocation(Map<String, Object> attributes, LocationProviderCallback callback);
+
+}
diff --git a/src/org/traccar/location/MozillaLocationProvider.java b/src/org/traccar/location/MozillaLocationProvider.java
new file mode 100644
index 000000000..d30fbf642
--- /dev/null
+++ b/src/org/traccar/location/MozillaLocationProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 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.location;
+
+import com.ning.http.client.AsyncCompletionHandler;
+import com.ning.http.client.Response;
+import org.traccar.Context;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+
+public class MozillaLocationProvider extends BaseLocationProvider {
+
+ private String url;
+ private String template;
+
+ public MozillaLocationProvider() {
+ this("https://location.services.mozilla.com/v1/geolocate", "test");
+ }
+
+ public MozillaLocationProvider(String url, String key) {
+ this.url = url + "?key=" + key;
+
+ template = new StringBuilder()
+ .append("{\"cellTowers\":[{")
+ .append("\"radioType\":\"gsm\",")
+ .append("\"mobileCountryCode\":%d,")
+ .append("\"mobileNetworkCode\":%d,")
+ .append("\"locationAreaCode\":%d,")
+ .append("\"cellId\":%d")
+ .append("}]}")
+ .toString();
+ }
+
+ protected void getLocation(int mcc, int mnc, long lac, long cid, final LocationProviderCallback callback) {
+ String body = String.format(template, mcc, mnc, lac, cid);
+ Context.getAsyncHttpClient().preparePost(url).setBody(body).execute(new AsyncCompletionHandler() {
+ @Override
+ public Object onCompleted(Response response) throws Exception {
+ try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
+ JsonObject json = reader.readObject().getJsonObject("location");
+ if (json.containsKey("lat") && json.containsKey("lon")) {
+ callback.onSuccess(
+ json.getJsonNumber("lat").doubleValue(),
+ json.getJsonNumber("lon").doubleValue());
+ } else {
+ callback.onFailure();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onThrowable(Throwable t) {
+ callback.onFailure();
+ }
+ });
+
+ }
+
+}
diff --git a/src/org/traccar/location/OpenCellIdLocationProvider.java b/src/org/traccar/location/OpenCellIdLocationProvider.java
new file mode 100644
index 000000000..94cc1a4e4
--- /dev/null
+++ b/src/org/traccar/location/OpenCellIdLocationProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 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.location;
+
+import com.ning.http.client.AsyncCompletionHandler;
+import com.ning.http.client.Response;
+import org.traccar.Context;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+
+public class OpenCellIdLocationProvider extends BaseLocationProvider {
+
+ private String url;
+
+ public OpenCellIdLocationProvider(String key) {
+ this("http://opencellid.org/cell/get", key);
+ }
+
+ public OpenCellIdLocationProvider(String url, String key) {
+ this.url = url + "?format=json&mcc=%d&mnc=%d&lac=%d&cellid=%d&key=" + key;
+ }
+
+ protected void getLocation(int mcc, int mnc, long lac, long cid, final LocationProviderCallback callback) {
+ Context.getAsyncHttpClient().prepareGet(String.format(url, mcc, mnc, lac, cid))
+ .execute(new AsyncCompletionHandler() {
+ @Override
+ public Object onCompleted(Response response) throws Exception {
+ try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
+ JsonObject json = reader.readObject();
+ if (json.containsKey("lat") && json.containsKey("lon")) {
+ callback.onSuccess(
+ json.getJsonNumber("lat").doubleValue(),
+ json.getJsonNumber("lon").doubleValue());
+ } else {
+ callback.onFailure();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onThrowable(Throwable t) {
+ callback.onFailure();
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/model/Event.java b/src/org/traccar/model/Event.java
index 1dbfe5812..6e3aa7f22 100644
--- a/src/org/traccar/model/Event.java
+++ b/src/org/traccar/model/Event.java
@@ -47,10 +47,9 @@ public abstract class Event extends Extensible {
public static final String KEY_IP = "ip";
public static final String KEY_ARCHIVE = "archive";
public static final String KEY_DISTANCE = "distance";
- public static final String KEY_DOOR = "door";
public static final String KEY_RPM = "rpm";
- public static final String KEY_HOURS = "hours";
public static final String KEY_VIN = "vin";
+ public static final String KEY_APPROXIMATE = "approximate";
public static final String KEY_OBD_SPEED = "obd-speed";
public static final String KEY_OBD_ODOMETER = "obd-odometer";
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index 1ed559ecd..9494da042 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -47,6 +47,16 @@ public class Position extends Event implements Factory {
setFixTime(time);
}
+ private boolean outdated;
+
+ public boolean getOutdated() {
+ return outdated;
+ }
+
+ public void setOutdated(boolean outdated) {
+ this.outdated = outdated;
+ }
+
private boolean valid;
public boolean getValid() {
diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java
index b3a062db3..b75587743 100644
--- a/src/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -86,7 +86,10 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
position.setCourse(buf.readUnsignedShort());
- position.set(Event.KEY_CID, ChannelBuffers.hexDump(buf.readBytes(9)));
+ position.set(Event.KEY_MCC, buf.readUnsignedShort());
+ position.set(Event.KEY_MNC, buf.readUnsignedShort());
+ position.set(Event.KEY_LAC, buf.readUnsignedShort());
+ position.set(Event.KEY_CID, buf.readUnsignedShort());
position.setValid((buf.readUnsignedByte() & 0x01) != 0);
diff --git a/src/org/traccar/protocol/FlextrackProtocolDecoder.java b/src/org/traccar/protocol/FlextrackProtocolDecoder.java
index 363d5502c..bc9825b49 100644
--- a/src/org/traccar/protocol/FlextrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/FlextrackProtocolDecoder.java
@@ -125,8 +125,8 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextInt());
position.set(Event.KEY_HDOP, parser.nextInt() * 0.1);
- position.set(Event.KEY_CID, parser.next());
- position.set(Event.KEY_LAC, parser.next());
+ position.set(Event.KEY_CID, parser.nextInt(16));
+ position.set(Event.KEY_LAC, parser.nextInt(16));
position.set(Event.KEY_ODOMETER, parser.nextInt());
return position;
diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
index 29884bb00..42786d4fe 100644
--- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -116,7 +116,9 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
.text(",")
.number("(0ddd)?,") // mcc
.number("(0ddd)?,") // mnc
- .number("(xxxx|x{8})?,") // loc
+ .number("(?:xxxx)?")
+ .number("(xxxx)").optional(2) // lac
+ .text(",")
.number("(xxxx)?,") // cell
.groupBegin()
.number("(d+.d)?,") // odometer
@@ -215,10 +217,12 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
}
- position.set(Event.KEY_MCC, parser.next());
- position.set(Event.KEY_MNC, parser.next());
- position.set(Event.KEY_LAC, parser.next());
- position.set(Event.KEY_CID, parser.next());
+ 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_ODOMETER, parser.next());
position.set(Event.KEY_BATTERY, parser.next());
diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
index aa693f42e..a91848f5b 100644
--- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -63,6 +63,17 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_NETWORK = new PatternBuilder()
+ .text("imei:")
+ .number("(d+),") // imei
+ .expression("[^,]+,") // alarm
+ .number("d+,,")
+ .text("L,,,")
+ .number("(x+),,") // lac
+ .number("(x+),,,") // cid
+ .any()
+ .compile();
+
private static final Pattern PATTERN_HANDSHAKE = new PatternBuilder()
.number("##,imei:(d+),A")
.compile();
@@ -93,14 +104,31 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Parser parser = new Parser(PATTERN, sentence);
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ Parser parser = new Parser(PATTERN_NETWORK, sentence);
+ if (parser.matches()) {
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Event.KEY_LAC, parser.nextInt(16));
+ position.set(Event.KEY_CID, parser.nextInt(16));
+
+ return position;
+
+ }
+
+ parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
String imei = parser.next();
if (!identify(imei, channel, remoteAddress)) {
return null;
diff --git a/src/org/traccar/protocol/Gt02ProtocolDecoder.java b/src/org/traccar/protocol/Gt02ProtocolDecoder.java
index d15d999cf..53739ee1c 100644
--- a/src/org/traccar/protocol/Gt02ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gt02ProtocolDecoder.java
@@ -100,6 +100,10 @@ public class Gt02ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(latitude);
position.setLongitude(longitude);
+ } else {
+
+ return null;
+
}
return position;
diff --git a/src/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/org/traccar/protocol/HuabaoProtocolDecoder.java
index 931f985a5..792c89c8b 100644
--- a/src/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -16,10 +16,20 @@
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.ChannelBufferTools;
+import org.traccar.helper.Checksum;
+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.TimeZone;
public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
@@ -27,7 +37,38 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public static final int MSG_GENERAL_RESPONSE = 0x8001;
public static final int MSG_TERMINAL_REGISTER = 0x0100;
+ public static final int MSG_TERMINAL_REGISTER_RESPONSE = 0x8100;
+ public static final int MSG_TERMINAL_AUTH = 0x0102;
+ public static final int MSG_LOCATION_REPORT = 0x0200;
+
+ public static final int RESULT_SUCCESS = 0;
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, int type, ChannelBuffer id, ChannelBuffer data) {
+ ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ response.writeByte(0x7e);
+ response.writeShort(type);
+ response.writeShort(data.readableBytes());
+ response.writeBytes(id);
+ response.writeShort(1); // index
+ response.writeBytes(data);
+ response.writeByte(Checksum.xor(response.toByteBuffer(1, response.readableBytes() - 1)));
+ response.writeByte(0x7e);
+ if (channel != null) {
+ channel.write(response, remoteAddress);
+ }
+ }
+
+ private void sendGeneralResponse(
+ Channel channel, SocketAddress remoteAddress, ChannelBuffer id, int type, int index) {
+ ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ response.writeShort(index);
+ response.writeShort(type);
+ response.writeByte(RESULT_SUCCESS);
+ sendResponse(channel, remoteAddress, MSG_GENERAL_RESPONSE, id, response);
+ }
@Override
protected Object decode(
@@ -36,10 +77,74 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
ChannelBuffer buf = (ChannelBuffer) msg;
buf.readUnsignedByte(); // start marker
- //int type = buf.readUnsignedShort();
- //int flags = buf.readUnsignedShort();
- buf.skipBytes(6); // phone number
- buf.readUnsignedShort(); // index
+ int type = buf.readUnsignedShort();
+ buf.readUnsignedShort(); // body length
+ ChannelBuffer id = buf.readBytes(6); // phone number
+ int index = buf.readUnsignedShort();
+
+ if (!identify(id.toString(Charset.defaultCharset()), channel, remoteAddress)) {
+ return null;
+ }
+
+ if (type == MSG_TERMINAL_REGISTER) {
+
+ ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ response.writeShort(index);
+ response.writeByte(RESULT_SUCCESS);
+ response.writeBytes("authentication".getBytes(Charset.defaultCharset()));
+ sendResponse(channel, remoteAddress, MSG_TERMINAL_REGISTER_RESPONSE, id, response);
+
+ } else if (type == MSG_TERMINAL_AUTH) {
+
+ sendGeneralResponse(channel, remoteAddress, id, type, index);
+
+ } else if (type == MSG_LOCATION_REPORT) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(getDeviceId());
+
+ position.set(Event.KEY_ALARM, buf.readUnsignedInt());
+
+ int flags = buf.readInt();
+
+ position.set(Event.KEY_IGNITION, BitUtil.check(flags, 0));
+
+ position.setValid(BitUtil.check(flags, 1));
+
+ double lat = buf.readUnsignedInt() * 0.000001;
+ double lon = buf.readUnsignedInt() * 0.000001;
+
+ if (BitUtil.check(flags, 2)) {
+ position.setLatitude(-lat);
+ } else {
+ position.setLatitude(lat);
+ }
+
+ if (BitUtil.check(flags, 3)) {
+ position.setLongitude(-lon);
+ } else {
+ position.setLongitude(lon);
+ }
+
+ position.setAltitude(buf.readShort());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+
+ DateBuilder dateBuilder = new DateBuilder(TimeZone.getTimeZone("GMT+8"))
+ .setYear(ChannelBufferTools.readHexInteger(buf, 2))
+ .setMonth(ChannelBufferTools.readHexInteger(buf, 2))
+ .setDay(ChannelBufferTools.readHexInteger(buf, 2))
+ .setHour(ChannelBufferTools.readHexInteger(buf, 2))
+ .setMinute(ChannelBufferTools.readHexInteger(buf, 2))
+ .setSecond(ChannelBufferTools.readHexInteger(buf, 2));
+ position.setTime(dateBuilder.getDate());
+
+ // additional information
+
+ return position;
+
+ }
return null;
}
diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
index d9e31fe48..b576b063d 100644
--- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -94,8 +94,13 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(buf.readUnsignedShort());
- position.set(Event.KEY_CID, buf.readUnsignedShort());
- position.set(Event.KEY_LAC, buf.readUnsignedShort());
+ int cid = buf.readUnsignedShort();
+ int lac = buf.readUnsignedShort();
+ if (cid != 0 && lac != 0) {
+ position.set(Event.KEY_CID, cid);
+ position.set(Event.KEY_LAC, lac);
+ }
+
position.set(Event.KEY_GSM, buf.readUnsignedByte());
} else if (version == 2) {
diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java
index a58e57703..dc9e6056e 100644
--- a/src/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -53,14 +53,16 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.number("(d)?,") // charger
.number("(d+)?,") // mcc
.number("(d+)?,") // mnc
- .number("(xxxx,xxxx);") // location code
+ .number("(xxxx),") // lac
+ .number("(xxxx);") // cid
.any() // checksum
.compile();
private static final Pattern PATTERN_ALTERNATIVE = new PatternBuilder()
.number("(d+),") // mcc
.number("(d+),") // mnc
- .number("(xxxx,xxxx),") // location code
+ .number("(xxxx),") // lac
+ .number("(xxxx),") // cid
.number("(d+),") // gsm signal
.number("(d+),") // battery
.number("(d+),") // flags
@@ -171,9 +173,12 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_CHARGE, Integer.parseInt(charger) == 1);
}
- position.set(Event.KEY_MCC, parser.next());
- position.set(Event.KEY_MNC, parser.next());
- position.set(Event.KEY_LAC, parser.next());
+ if (parser.hasNext(3)) {
+ 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));
+ }
} else {
@@ -194,9 +199,10 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(getDeviceId());
- position.set(Event.KEY_MCC, parser.next());
- position.set(Event.KEY_MNC, parser.next());
- position.set(Event.KEY_LAC, parser.next());
+ 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_GSM, parser.next());
position.set(Event.KEY_BATTERY, Double.parseDouble(parser.next()));
@@ -236,7 +242,8 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+),") // odometer
.number("(d+),") // mcc
.number("(d+),") // mnc
- .number("(xxxx,xxxx),") // cell
+ .number("(xxxx),") // lac
+ .number("(xxxx),") // cid
.number("(d+)?,") // gsm
.expression("([01]+),") // input
.expression("([01]+),") // output
@@ -295,7 +302,8 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_ODOMETER, parser.nextDouble());
position.set(Event.KEY_MCC, parser.nextInt());
position.set(Event.KEY_MNC, parser.nextInt());
- position.set(Event.KEY_CID, parser.next());
+ position.set(Event.KEY_LAC, parser.nextInt(16));
+ position.set(Event.KEY_CID, parser.nextInt(16));
String gsm = parser.next();
if (gsm != null) {
diff --git a/src/org/traccar/protocol/MeiligaoFrameDecoder.java b/src/org/traccar/protocol/MeiligaoFrameDecoder.java
index 6dcc6fd9c..9fb530f8a 100644
--- a/src/org/traccar/protocol/MeiligaoFrameDecoder.java
+++ b/src/org/traccar/protocol/MeiligaoFrameDecoder.java
@@ -26,9 +26,7 @@ public class MeiligaoFrameDecoder extends FrameDecoder {
@Override
protected Object decode(
- ChannelHandlerContext ctx,
- Channel channel,
- ChannelBuffer buf) throws Exception {
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
// Strip not '$' (0x24) bytes from the beginning
while (buf.readable() && buf.getUnsignedByte(buf.readerIndex()) != 0x24) {
diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
index 2b8460fc7..4bde5cf75 100644
--- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -119,10 +119,10 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_ODOMETER, parser.next());
position.set("runtime", parser.next());
- position.set(Event.KEY_MCC, parser.next());
- position.set(Event.KEY_MCC, parser.next());
- position.set(Event.KEY_LAC, parser.next());
- position.set(Event.KEY_CID, parser.next());
+ 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_STATUS, parser.next());
for (int i = 1; i <= 3; i++) {
@@ -196,7 +196,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_ODOMETER, buf.readUnsignedInt());
position.set("runtime", buf.readUnsignedInt());
position.set(Event.KEY_MCC, buf.readUnsignedShort());
- position.set(Event.KEY_MCC, buf.readUnsignedShort());
+ position.set(Event.KEY_MNC, buf.readUnsignedShort());
position.set(Event.KEY_LAC, buf.readUnsignedShort());
position.set(Event.KEY_CID, buf.readUnsignedShort());
position.set(Event.KEY_STATUS, buf.readUnsignedShort());
diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java
index b0d85b101..38f771eec 100644
--- a/src/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -83,7 +83,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
- position.set(Event.KEY_CID, parser.next());
+ parser.next(); // location code + bsic
position.setValid(true);
position.setLatitude(parser.nextDouble());
diff --git a/src/org/traccar/protocol/T800xProtocolDecoder.java b/src/org/traccar/protocol/T800xProtocolDecoder.java
index 2127be331..83d815e0f 100644
--- a/src/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/org/traccar/protocol/T800xProtocolDecoder.java
@@ -143,10 +143,14 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, dateBuilder.getDate());
- position.set(Event.KEY_MCC, buf.readUnsignedShort());
- position.set(Event.KEY_MNC, buf.readUnsignedShort());
- position.set(Event.KEY_LAC, buf.readUnsignedShort());
- position.set(Event.KEY_CID, buf.readUnsignedShort());
+ byte[] array = new byte[16];
+ buf.readBytes(array);
+ ChannelBuffer swapped = ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, array);
+
+ position.set(Event.KEY_MCC, swapped.readUnsignedShort());
+ position.set(Event.KEY_MNC, swapped.readUnsignedShort());
+ position.set(Event.KEY_LAC, swapped.readUnsignedShort());
+ position.set(Event.KEY_CID, swapped.readUnsignedShort());
// two more cell towers
diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 3592cae79..2217b5ce4 100644
--- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -114,7 +114,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(locationMask, 5)) {
- position.set("area", buf.readUnsignedShort());
+ position.set(Event.KEY_LAC, buf.readUnsignedShort());
position.set(Event.KEY_CID, buf.readUnsignedShort());
}
diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/org/traccar/protocol/Tk103ProtocolDecoder.java
index 95728c447..6fa4edb06 100644
--- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -64,6 +64,16 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.number("d+") // installed
.compile();
+ private static final Pattern PATTERN_NETWORK = new PatternBuilder()
+ .number("(d{12})") // device id
+ .text("BZ00,")
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+),") // cid
+ .any()
+ .compile();
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -88,11 +98,11 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
}
}
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
Parser parser = new Parser(PATTERN_BATTERY, sentence);
if (parser.matches()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
if (!identify(parser.next(), channel)) {
return null;
}
@@ -117,14 +127,28 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ parser = new Parser(PATTERN_NETWORK, sentence);
+ if (parser.matches()) {
+ if (!identify(parser.next(), channel)) {
+ return null;
+ }
+ position.setDeviceId(getDeviceId());
+
+ getLastLocation(position, null);
+
+ 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;
+ }
+
parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
if (!identify(parser.next(), channel)) {
return null;
}
diff --git a/src/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/org/traccar/protocol/Tlt2hProtocolDecoder.java
index d9d50947e..24e9893bb 100644
--- a/src/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -86,7 +86,7 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
position.setProtocol(getProtocolName());
position.setDeviceId(getDeviceId());
- position.set(Event.KEY_CID, parser.next());
+ parser.next(); // base station info
DateBuilder dateBuilder = new DateBuilder()
.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java
index 321b78b7c..88eecab70 100644
--- a/src/org/traccar/protocol/TotemProtocolDecoder.java
+++ b/src/org/traccar/protocol/TotemProtocolDecoder.java
@@ -55,7 +55,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)") // battery
.number("(dddd)|") // power
.number("(d+)|").optional() // adc
- .number("(x+)|") // location code
+ .number("x*(xxxx)") // lac
+ .number("(xxxx)|") // cid
.number("(d+)|") // temperature
.number("(d+.d+)|") // odometer
.number("d+|") // serial number
@@ -84,7 +85,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)") // battery
.number("(dd)|") // external power
.number("(d+)|") // adc
- .number("(x{8})|") // location code
+ .number("(xxxx)") // lac
+ .number("(xxxx)|") // cid
.number("(d+)|") // temperature
.number("(d+.d+)|") // odometer
.number("d+|") // serial number
@@ -107,7 +109,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
.number("(dddd)") // adc 2
.number("(ddd)") // temperature 1
.number("(ddd)") // temperature 2
- .number("(x{8})") // location code
+ .number("(xxxx)") // lac
+ .number("(xxxx)") // cid
.expression("([AV])") // validity
.number("(dd)") // satellites
.number("(ddd)") // course
@@ -185,7 +188,14 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.KEY_BATTERY, parser.next());
position.set(Event.KEY_POWER, parser.nextDouble());
position.set(Event.PREFIX_ADC + 1, parser.next());
- position.set(Event.KEY_LAC, parser.next());
+
+ int lac = parser.nextInt(16);
+ int cid = parser.nextInt(16);
+ if (lac != 0 && cid != 0) {
+ position.set(Event.KEY_LAC, lac);
+ position.set(Event.KEY_CID, cid);
+ }
+
position.set(Event.PREFIX_TEMP + 1, parser.next());
position.set(Event.KEY_ODOMETER, parser.next());
@@ -203,7 +213,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
position.set(Event.PREFIX_ADC + 2, parser.next());
position.set(Event.PREFIX_TEMP + 1, parser.next());
position.set(Event.PREFIX_TEMP + 2, parser.next());
- position.set(Event.KEY_LAC, parser.next());
+ position.set(Event.KEY_LAC, parser.nextInt(16));
+ position.set(Event.KEY_CID, parser.nextInt(16));
position.setValid(parser.next().equals("A"));
position.set(Event.KEY_SATELLITES, parser.next());
diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java
index a24d0a56b..92b7638fb 100644
--- a/src/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/org/traccar/protocol/WatchProtocolDecoder.java
@@ -64,8 +64,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
private void sendResponse(Channel channel, String manufacturer, String id, String content) {
if (channel != null) {
- channel.write(
- String.format("[%s*%s*%04x*%s]", manufacturer, id, content.length(), content));
+ channel.write(String.format(
+ "[%s*%s*%04x*%s]", manufacturer, id, content.length(), content));
}
}
@@ -138,6 +138,10 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type.equals("TKQ")) {
+
+ sendResponse(channel, manufacturer, id, "TKQ");
+
}
return null;
diff --git a/src/org/traccar/web/AsyncServlet.java b/src/org/traccar/web/AsyncServlet.java
index 63b08ff3e..c55fd6e75 100644
--- a/src/org/traccar/web/AsyncServlet.java
+++ b/src/org/traccar/web/AsyncServlet.java
@@ -36,6 +36,7 @@ import org.traccar.Context;
import org.traccar.GlobalTimer;
import org.traccar.database.ConnectionManager;
import org.traccar.helper.Log;
+import org.traccar.model.Device;
import org.traccar.model.Position;
public class AsyncServlet extends BaseServlet {
@@ -50,7 +51,7 @@ public class AsyncServlet extends BaseServlet {
public static class AsyncSession {
- private static final boolean DEBUG_ASYNC = true;
+ public static final boolean DEBUG_ASYNC = false;
private static final long SESSION_TIMEOUT = 30;
private static final long REQUEST_TIMEOUT = 20;
@@ -60,7 +61,8 @@ public class AsyncServlet extends BaseServlet {
private final Set<Long> devices = new HashSet<>();
private Timeout sessionTimeout;
private Timeout requestTimeout;
- private final Map<Long, Position> positions = new HashMap<>();
+ private final Set<Device> deviceUpdates = new HashSet<>();
+ private final Set<Position> positionUpdates = new HashSet<>();
private AsyncContext activeContext;
private void logEvent(String message) {
@@ -76,7 +78,7 @@ public class AsyncServlet extends BaseServlet {
Collection<Position> initialPositions = Context.getConnectionManager().getInitialState(devices);
for (Position position : initialPositions) {
- positions.put(position.getDeviceId(), position);
+ positionUpdates.add(position);
}
Context.getConnectionManager().addListener(devices, dataListener);
@@ -86,17 +88,34 @@ public class AsyncServlet extends BaseServlet {
return devices.contains(deviceId);
}
- private final ConnectionManager.DataCacheListener dataListener = new ConnectionManager.DataCacheListener() {
+ private final ConnectionManager.UpdateListener dataListener = new ConnectionManager.UpdateListener() {
+ @Override
+ public void onUpdateDevice(Device device) {
+ synchronized (AsyncSession.this) {
+ logEvent("onUpdateDevice deviceId: " + device.getId());
+ if (!destroyed) {
+ if (requestTimeout != null) {
+ requestTimeout.cancel();
+ requestTimeout = null;
+ }
+ deviceUpdates.add(device);
+ if (activeContext != null) {
+ response();
+ }
+ }
+ }
+ }
+
@Override
public void onUpdatePosition(Position position) {
synchronized (AsyncSession.this) {
- logEvent("onUpdate deviceId: " + position.getDeviceId());
+ logEvent("onUpdatePosition deviceId: " + position.getDeviceId());
if (!destroyed) {
if (requestTimeout != null) {
requestTimeout.cancel();
requestTimeout = null;
}
- positions.put(position.getDeviceId(), position);
+ positionUpdates.add(position);
if (activeContext != null) {
response();
}
@@ -142,7 +161,7 @@ public class AsyncServlet extends BaseServlet {
sessionTimeout = null;
}
- if (!positions.isEmpty()) {
+ if (!deviceUpdates.isEmpty() || !positionUpdates.isEmpty()) {
response();
} else {
requestTimeout = GlobalTimer.getTimer().newTimeout(
@@ -158,8 +177,18 @@ public class AsyncServlet extends BaseServlet {
JsonObjectBuilder result = Json.createObjectBuilder();
result.add("success", true);
- result.add("data", JsonConverter.arrayToJson(positions.values()));
- positions.clear();
+
+ if (Context.getConfig().getBoolean("web.oldAsyncFormat")) {
+ result.add("data", JsonConverter.arrayToJson(positionUpdates));
+ } else {
+ JsonObjectBuilder data = Json.createObjectBuilder();
+ data.add("devices", JsonConverter.arrayToJson(deviceUpdates));
+ data.add("positions", JsonConverter.arrayToJson(positionUpdates));
+ result.add("data", data.build());
+ }
+
+ deviceUpdates.clear();
+ positionUpdates.clear();
try {
response.getWriter().println(result.build().toString());
diff --git a/src/org/traccar/web/JsonConverter.java b/src/org/traccar/web/JsonConverter.java
index d39e6b488..a8b68613b 100644
--- a/src/org/traccar/web/JsonConverter.java
+++ b/src/org/traccar/web/JsonConverter.java
@@ -64,7 +64,7 @@ public final class JsonConverter {
final String name = Introspector.decapitalize(method.getName().substring(3));
Class<?> parameterType = method.getParameterTypes()[0];
- if (json.containsKey(name)) {
+ if (json.containsKey(name) && !json.isNull(name)) {
try {
if (parameterType.equals(boolean.class)) {
method.invoke(object, json.getBoolean(name));