diff options
-rw-r--r-- | debug.xml | 6 | ||||
-rw-r--r-- | src/org/traccar/Main.java | 18 | ||||
-rw-r--r-- | src/org/traccar/database/DataManager.java | 27 | ||||
-rw-r--r-- | src/org/traccar/model/Position.java | 1 | ||||
-rw-r--r-- | src/org/traccar/protocol/H02ProtocolDecoder.java | 15 | ||||
-rw-r--r-- | src/org/traccar/web/WebServer.java | 3 | ||||
-rw-r--r-- | tools/minify.bat | 5 | ||||
-rwxr-xr-x | tools/test-generator.py | 7 | ||||
-rw-r--r-- | web/app/AttributeFormatter.js | 30 | ||||
-rw-r--r-- | web/app/view/Devices.js | 20 | ||||
-rw-r--r-- | web/app/view/Report.js | 10 | ||||
-rw-r--r-- | web/app/view/StateController.js | 49 |
12 files changed, 187 insertions, 4 deletions
@@ -77,6 +77,8 @@ <entry key='database.changelog'>./schema/changelog-master.xml</entry> + <entry key='database.positionsHistoryDays'>7</entry> + <entry key='database.selectServers'> SELECT * FROM server; </entry> @@ -314,6 +316,10 @@ DELETE FROM notifications WHERE id = :id; </entry> + <entry key='database.clearPositionsHistory'> + DELETE FROM positions WHERE deviceid = :deviceId and servertime < :serverTime; + </entry> + <!-- PROTOCOL CONFIG --> <entry key='gps103.port'>5001</entry> diff --git a/src/org/traccar/Main.java b/src/org/traccar/Main.java index 1b8d93e34..0070904ab 100644 --- a/src/org/traccar/Main.java +++ b/src/org/traccar/Main.java @@ -16,10 +16,13 @@ package org.traccar; import org.traccar.helper.Log; - +import java.util.Timer; +import java.util.TimerTask; import java.util.Locale; public final class Main { + static final long cleanDelay = 10*1000; //10 sec + static final long cleanPeriod = 24*60*60*1000; //24 hr private Main() { } @@ -35,6 +38,19 @@ public final class Main { Context.getWebServer().start(); } + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + // Clean positions history + try { + Context.getDataManager().clearPositionsHistory(); + } catch (Exception error) { + Log.warning(error); + } + } + }, cleanDelay, cleanPeriod); + Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index b3f24383f..a9fc09448 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -521,6 +521,33 @@ public class DataManager implements IdentityManager { .executeQuery(Position.class); } + public void clearPositionsHistory() throws SQLException { + //SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd"); + int histDays = config.getInteger("database.positionsHistoryDays"); + if (histDays == 0) { + return; + } + + String sql = getQuery("database.clearPositionsHistory"); + if (sql == null) { + return; + } + + for (Device device : getAllDevices()) { + Date lastUpdate = device.getLastUpdate(); + if(lastUpdate != null){ + + Date dateBefore = new Date(lastUpdate.getTime() - histDays * 24 * 3600 * 1000 ); //Subtract histDays days + //String dt = s.format(dateBefore); + + QueryBuilder.create(dataSource, sql) + .setLong("deviceId", device.getId()) + .setDate("serverTime", dateBefore) + .executeUpdate(); + } + } + } + public Server getServer() throws SQLException { return QueryBuilder.create(dataSource, getQuery("database.selectServers")) .executeQuerySingle(Server.class); diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java index 4e03b2097..c5a7889bd 100644 --- a/src/org/traccar/model/Position.java +++ b/src/org/traccar/model/Position.java @@ -28,6 +28,7 @@ public class Position extends Message { public static final String KEY_GPS = "gps"; public static final String KEY_EVENT = "event"; public static final String KEY_ALARM = "alarm"; + public static final String KEY_ALARM_TYPE = "alarm-type"; public static final String KEY_STATUS = "status"; public static final String KEY_ODOMETER = "odometer"; public static final String KEY_HOURS = "hours"; diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java index ec5d3adef..ffe16e975 100644 --- a/src/org/traccar/protocol/H02ProtocolDecoder.java +++ b/src/org/traccar/protocol/H02ProtocolDecoder.java @@ -64,8 +64,21 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { private void processStatus(Position position, long status) { if (!BitUtil.check(status, 0) || !BitUtil.check(status, 1) - || !BitUtil.check(status, 3) || !BitUtil.check(status, 4)) { + || !BitUtil.check(status, 3) || !BitUtil.check(status, 4) || !BitUtil.check(status, 7)) { position.set(Position.KEY_ALARM, true); + + if (!BitUtil.check(status, 0)){ + position.set(Position.KEY_ALARM_TYPE, "theft"); + } else if (!BitUtil.check(status, 1)){ + position.set(Position.KEY_ALARM_TYPE, "robbery"); + } else if (!BitUtil.check(status, 3)){ + position.set(Position.KEY_ALARM_TYPE, "illegal ignition"); + } else if (!BitUtil.check(status, 4)){ + position.set(Position.KEY_ALARM_TYPE, "entering"); + } else if (!BitUtil.check(status, 7)){ + position.set(Position.KEY_ALARM_TYPE, "out"); + } + } position.set(Position.KEY_IGNITION, !BitUtil.check(status, 10)); position.set(Position.KEY_STATUS, status); diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index 5527e80f3..4ef31b1df 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -118,6 +118,9 @@ public class WebServer { resourceHandler.setResourceBase(config.getString("web.path")); if (config.getBoolean("web.debug")) { resourceHandler.setWelcomeFiles(new String[] {"debug.html"}); + //Troubleshooting Locked UI Files on Windows while app is running (like html, js, css, etc...), + //you can make changes to the UI Files and refresh the page in the browser without stopping the app first + resourceHandler.setMinMemoryMappedContentLength(-1); } else { resourceHandler.setWelcomeFiles(new String[] {"release.html", "index.html"}); } diff --git a/tools/minify.bat b/tools/minify.bat new file mode 100644 index 000000000..6ab8fd94b --- /dev/null +++ b/tools/minify.bat @@ -0,0 +1,5 @@ +@echo off +cd C:\[traccar path]\traccar\web +set SDK=C:\[sencha path]\ext-6.0.0 + +sencha -sdk %SDK% compile -classpath=app.js,app,%SDK%\packages\core\src,%SDK%\packages\core\overrides,%SDK%\classic\classic\src,%SDK%\classic\classic\overrides exclude -all and include -recursive -file app.js and exclude -namespace=Ext and concatenate -closure app.min.js diff --git a/tools/test-generator.py b/tools/test-generator.py index 681d1755d..09fb65611 100755 --- a/tools/test-generator.py +++ b/tools/test-generator.py @@ -32,8 +32,10 @@ for i in range(0, len(waypoints)): lon = lon1 + (lon2 - lon1) * j / count points.append((lat, lon)) -def send(lat, lon, course): +def send(lat, lon, course, alarm): params = (('id', id), ('timestamp', int(time.time())), ('lat', lat), ('lon', lon), ('bearing', course)) + if alarm: + params = params + (('alarm', 'true'),) urllib2.urlopen(server + '?' + urllib.urlencode(params)).read() def course(lat1, lon1, lat2, lon2): @@ -50,6 +52,7 @@ index = 0 while True: (lat1, lon1) = points[index % len(points)] (lat2, lon2) = points[(index + 1) % len(points)] - send(lat1, lon1, course(lat1, lon1, lat2, lon2)) + alarm = False #((index % 10) == 0) + send(lat1, lon1, course(lat1, lon1, lat2, lon2), alarm) time.sleep(period) index += 1 diff --git a/web/app/AttributeFormatter.js b/web/app/AttributeFormatter.js index 3432ca1e0..253dadb53 100644 --- a/web/app/AttributeFormatter.js +++ b/web/app/AttributeFormatter.js @@ -34,6 +34,32 @@ Ext.define('Traccar.AttributeFormatter', { return Ext.getStore('DistanceUnits').formatValue(value, Traccar.app.getPreference('distanceUnit')); }, + alarmFormatter: function (attributes) { + if (attributes instanceof Object) { + if (attributes.hasOwnProperty('alarm')){ + var value = attributes.alarm; + if (typeof value === 'boolean') { + value = (value ? Ext.Msg.buttonText.yes : Ext.Msg.buttonText.no); + } + return '<span style="color:red;">' + value + '</span>'; + } + } + return ''; + }, + + alarmTypeFormatter: function (attributes) { + var alatmType = ''; + if (attributes instanceof Object) { + for (key in attributes) { + if (attributes.hasOwnProperty(key) && key.toLowerCase()=='alarm-type') { + alatmType = attributes[key]; + break; + } + } + } + return '<span style="color:red;">' + alatmType + '</span>'; + }, + defaultFormatter: function (value) { if (typeof value === 'number') { return Number(value.toFixed(Traccar.Style.numberPrecision)); @@ -58,6 +84,10 @@ Ext.define('Traccar.AttributeFormatter', { return this.courseFormatter; } else if (key === 'distance' || key === 'odometer') { return this.distanceFormatter; + } else if (key === 'alarm') { + return this.alarmFormatter; + } else if (key === 'alarm-type') { + return this.alarmTypeFormatter; } else { return this.defaultFormatter; } diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 1a70dfef8..b6e7d6ffd 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -57,6 +57,26 @@ Ext.define('Traccar.view.Devices', { tooltipType: 'title' }, { xtype: 'tbfill' + },{ + id: 'showAlarmButton', + glyph: 'xf0a2@FontAwesome', + tooltip: 'Show Alarms', + tooltipType: 'title', + pressed : true, + enableToggle: true, + listeners:{ + toggle: function(button, pressed){ + if(pressed){ + button.setGlyph('xf0a2@FontAwesome'); + //Ext.getCmp('useAlarmSoundButton').enable(); + } + else { + button.setGlyph('xf1f7@FontAwesome'); + //Ext.getCmp('useAlarmSoundButton').disable(); + } + }, + scope:this + } }, { id: 'deviceFollowButton', glyph: 'xf05b@FontAwesome', diff --git a/web/app/view/Report.js b/web/app/view/Report.js index 4261b9040..95f8144f2 100644 --- a/web/app/view/Report.js +++ b/web/app/view/Report.js @@ -115,5 +115,15 @@ Ext.define('Traccar.view.Report', { dataIndex: 'address', flex: 1, renderer: Traccar.AttributeFormatter.getFormatter('address') + }, { + text: 'Alarm', + dataIndex: 'attributes', + flex: 1, + renderer: Traccar.AttributeFormatter.getFormatter('alarm') + }, { + text: 'Alarm Type', + dataIndex: 'attributes', + flex: 1, + renderer: Traccar.AttributeFormatter.getFormatter('alarm-type') }] }); diff --git a/web/app/view/StateController.js b/web/app/view/StateController.js index 01df6645d..68ec3466d 100644 --- a/web/app/view/StateController.js +++ b/web/app/view/StateController.js @@ -62,6 +62,11 @@ Ext.define('Traccar.view.StateController', { if (this.deviceId === data[i].get('deviceId')) { this.updatePosition(data[i]); } + var position = data[i]; + var attributes = position.get('attributes'); + if (attributes instanceof Object){ + if (attributes.hasOwnProperty('alarm') && this.showAlarmSelected()) this.onAlarm(position.get('deviceId'), attributes); + } } }, @@ -104,6 +109,50 @@ Ext.define('Traccar.view.StateController', { } }, + showAlarmSelected: function () { + return Ext.getCmp('showAlarmButton') && Ext.getCmp('showAlarmButton').pressed; + }, + + onAlarm: function(deviceId, attributes){ + var alatmType = ''; + for (key in attributes) { + if (attributes.hasOwnProperty(key) && key.toLowerCase()=='alarm-type') { + alatmType = ' of type ' + attributes[key]; + break; + } + } + + var device = Ext.getStore('Devices').findRecord('id', deviceId, 0, false, false, true); + if(device){ + var deviceName = device.get('name'); + var msg = 'Alarm'+alatmType+' on device '+deviceName+'!'; + this.showAlarmMsg(msg); + } + }, + + showAlarmMsg: function(msg){ + this.beep(); + var alarmMsg = Ext.Msg.show({ + title:'Alarm', + message: msg, + modal: false, + buttons: Ext.Msg.YES, + icon: Ext.Msg.WARNING + }); + + window.setTimeout(function(){ + alarmMsg.close(); + }, 10000) + + }, + + beep: function() { + if(this.snd == null){ + this.snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU="); + } + this.snd.play(); + }, + selectDevice: function (device) { var position; this.deviceId = device.get('id'); |