Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/iNavFlight/inav-configurator.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tabs
diff options
context:
space:
mode:
authormirko-it <mirko-it@users.noreply.github.com>2019-04-29 18:23:32 +0300
committergiacomo892 <giacomo1989@gmail.com>2019-04-29 18:23:32 +0300
commit3dcffdd2928903f63c9f856968c5055bd4e8dfa3 (patch)
tree7c55b1a84e33d2af1a357c639ff168a650b9a08a /tabs
parentcec7997bfc2d0c6ff452d6c1a73e8530970d6094 (diff)
Offline missions (with FC disconnected) on file (#720)
* Offline missions (with FC disconnected) are now available for load and save to file. Supports XML file format, should be compatible enough with other software * removed default file name * removed code in early stage of future developments * code style and fc buttons * map resize handling
Diffstat (limited to 'tabs')
-rw-r--r--tabs/mission_control.html23
-rw-r--r--tabs/mission_control.js380
2 files changed, 331 insertions, 72 deletions
diff --git a/tabs/mission_control.html b/tabs/mission_control.html
index 5a0767ad..42c2d116 100644
--- a/tabs/mission_control.html
+++ b/tabs/mission_control.html
@@ -14,16 +14,17 @@
</div>
<div class="spacer">
<div class="point">
- <label class="point-label" for="pointAlt">Alt (cm): </label>
+ <label class="point-label" for="MPdefaultPointAlt">Alt (cm): </label>
<input id="MPdefaultPointAlt" type="text" value="0" required>
</div>
<div class="point">
- <label class="point-label" for="pointSpeed">Speed (cm/s): </label>
+ <label class="point-label" for="MPdefaultPointSpeed">Speed (cm/s): </label>
<input id="MPdefaultPointSpeed" type="text" value="0" required>
</div>
<div>
- <div id="saveSettings" class="btn save_btn" style="padding-top: 10px; display: inline-block">
- <a class="save" href="#" data-i18n="editPointButtonSave" style="float: left">Save</a>
+ <div class="btn save_btn" style="padding-top: 10px;">
+ <a id="saveSettings" class="save" href="#" data-i18n="missionSettingsSave" style="float: left">Save</a>
+ <a id="cancelSettings" class="save" href="#" data-i18n="missionSettingsCancel" style="float: right">Cancel</a>
</div>
</div>
</div>
@@ -33,31 +34,35 @@
<div class="spacer_box_title i18n-replaced" data-i18n="missionTotalInformationHead">Total information</div>
</div>
<div class="spacer">
- <div style="padding-bottom: 2px;">
+ <div id="infoMissionDistance" style="padding-bottom: 2px;">
<span>Distance (m):</span>
<span id="missionDistance"></span>
</div>
- <div style="padding-bottom: 2px;">
+ <div id="infoAvailablePoints" style="padding-bottom: 2px;">
<span>Available Points</span>
<span id="availablePoints">0/0</span>
</div>
- <div style="padding-bottom: 2px;">
+ <div id="infoMissionValid" style="padding-bottom: 2px;">
<span>Mission valid</span>
<div id="missionValid" style="display: inline-block"></div>
</div>
<br>
<div style="display: flex;">
- <input type="checkbox" style="width: 18px;margin-left: 5px;" id="rthEndMission">
+ <input type="checkbox" style="width: 18px; margin-left: 5px;" id="rthEndMission">
<label for="rthEndMission" style="padding: 2px;">RTH at the end of the mission</label>
</div>
<div id="rthSettings" style="display: none">
<div style="display: flex">
- <input type="checkbox" id="rthLanding" style="width: 18px;margin-left: 5px;">
+ <input type="checkbox" id="rthLanding" style="width: 18px; margin-left: 5px;">
<label for="rthLanding" style="padding: 2px">Landing</label>
</div>
</div>
<hr>
<div class="btn save_btn">
+ <a id="loadFileMissionButton" class="save" href="#" data-i18n="loadFileMissionButton">Load file mission</a>
+ <a id="saveFileMissionButton" class="save" href="#" data-i18n="saveFileMissionButton">Save file mission</a>
+ </div>
+ <div class="btn save_btn">
<a id="loadMissionButton" class="save" href="#" data-i18n="loadMissionButton">Load mission from FC</a>
<a id="saveMissionButton" class="save" href="#" data-i18n="saveMissionButton">Save mission to FC</a>
<a id="loadEepromMissionButton" class="save" href="#" data-i18n="loadEepromMissionButton">Load Eeprom mission</a>
diff --git a/tabs/mission_control.js b/tabs/mission_control.js
index e8242496..f931d3e0 100644
--- a/tabs/mission_control.js
+++ b/tabs/mission_control.js
@@ -1,5 +1,21 @@
'use strict';
+// MultiWii NAV Protocol
+var MWNP = MWNP || {};
+
+// WayPoint type
+MWNP.WPTYPE = {
+ WAYPOINT: 1,
+ PH_UNLIM: 2,
+ PH_TIME: 3,
+ RTH: 4,
+ SET_POI: 5,
+ JUMP: 6,
+ SET_HEAD: 7,
+ LAND: 8
+};
+
+
TABS.mission_control = {};
TABS.mission_control.isYmapLoad = false;
TABS.mission_control.initialize = function (callback) {
@@ -9,16 +25,24 @@ TABS.mission_control.initialize = function (callback) {
googleAnalytics.sendAppView('Mission Control');
}
- var loadChainer = new MSPChainerClass();
- loadChainer.setChain([
- mspHelper.getMissionInfo
- ]);
- loadChainer.setExitPoint(loadHtml);
- loadChainer.execute();
+ if (CONFIGURATOR.connectionValid) {
+ var loadChainer = new MSPChainerClass();
+ loadChainer.setChain([
+ mspHelper.getMissionInfo
+ ]);
+ loadChainer.setExitPoint(loadHtml);
+ loadChainer.execute();
+ } else {
+
+ // FC not connected, load page anyway
+ loadHtml();
+ }
function updateTotalInfo() {
- $('#availablePoints').text(MISSION_PLANER.countBusyPoints + '/' + MISSION_PLANER.maxWaypoints);
- $('#missionValid').html(MISSION_PLANER.isValidMission ? chrome.i18n.getMessage('armingCheckPass') : chrome.i18n.getMessage('armingCheckFail'));
+ if (CONFIGURATOR.connectionValid) {
+ $('#availablePoints').text(MISSION_PLANER.countBusyPoints + '/' + MISSION_PLANER.maxWaypoints);
+ $('#missionValid').html(MISSION_PLANER.isValidMission ? chrome.i18n.getMessage('armingCheckPass') : chrome.i18n.getMessage('armingCheckFail'));
+ }
}
function loadHtml() {
@@ -26,20 +50,21 @@ TABS.mission_control.initialize = function (callback) {
}
function process_html() {
- if (typeof require !== "undefined") {
- chrome.storage.local.get('missionPlanerSettings', function (result) {
- if (result.missionPlanerSettings) {
- $('#MPdefaultPointAlt').val(result.missionPlanerSettings.alt);
- $('#MPdefaultPointSpeed').val(result.missionPlanerSettings.speed);
- } else {
- chrome.storage.local.set({'missionPlanerSettings': {speed: 0, alt: 5000}});
- $('#MPdefaultPointAlt').val(5000);
- $('#MPdefaultPointSpeed').val(0);
- }
- });
+ // set GUI for offline operations
+ if (!CONFIGURATOR.connectionValid) {
+ $('#infoAvailablePoints').hide();
+ $('#infoMissionValid').hide();
+ $('#loadMissionButton').hide();
+ $('#saveMissionButton').hide();
+ $('#loadEepromMissionButton').hide();
+ $('#saveEepromMissionButton').hide();
+ }
- initMap();
+ if (typeof require !== "undefined") {
+ loadSettings();
+ // let the dom load finish, avoiding the resizing of the map
+ setTimeout(initMap, 200);
} else {
$('#missionMap, #missionControls').hide();
$('#notLoadMap').show();
@@ -54,6 +79,7 @@ TABS.mission_control.initialize = function (callback) {
var map;
var selectedMarker = null;
var pointForSend = 0;
+ var settings = { speed: 0, alt: 5000 };
function clearEditForm() {
$('#pointLat').val('');
@@ -64,6 +90,25 @@ TABS.mission_control.initialize = function (callback) {
$('#MPeditPoint').fadeOut(300);
}
+ function loadSettings() {
+ chrome.storage.local.get('missionPlanerSettings', function (result) {
+ if (result.missionPlanerSettings) {
+ settings = result.missionPlanerSettings;
+ }
+
+ refreshSettings();
+ });
+ }
+
+ function saveSettings() {
+ chrome.storage.local.set({'missionPlanerSettings': settings});
+ }
+
+ function refreshSettings() {
+ $('#MPdefaultPointAlt').val(settings.alt);
+ $('#MPdefaultPointSpeed').val(settings.speed);
+ }
+
function repaint() {
var oldPos;
for (var i in lines) {
@@ -122,16 +167,16 @@ TABS.mission_control.initialize = function (callback) {
scale: 0.5,
src: '../images/icons/cf_icon_position' + (isEdit ? '_edit' : '') + '.png'
}))
-// text: new ol.style.Text({
-// text: '10',
-// offsetX: -1,
-// offsetY: -30,
-// overflow: true,
-// scale: 2,
-// fill: new ol.style.Fill({
-// color: 'black'
-// })
-// })
+ /*
+ text: new ol.style.Text({
+ text: '10',
+ offsetX: -1,
+ offsetY: -30,
+ overflow: true,
+ scale: 2,
+ fill: new ol.style.Fill({ color: 'black' })
+ })
+ */
});
}
@@ -315,8 +360,8 @@ TABS.mission_control.initialize = function (callback) {
return false;
};
- var lat = GPS_DATA.lat / 10000000;
- var lon = GPS_DATA.lon / 10000000;
+ var lat = (GPS_DATA ? (GPS_DATA.lat / 10000000) : 0);
+ var lon = (GPS_DATA ? (GPS_DATA.lon / 10000000) : 0);
let mapLayer;
@@ -358,15 +403,26 @@ TABS.mission_control.initialize = function (callback) {
// Set the attribute link to open on an external browser window, so
// it doesn't interfere with the configurator.
- var interval;
- interval = setInterval(function() {
- var anchor = $('.ol-attribution a');
- if (anchor.length) {
- anchor.attr('target', '_blank');
- clearInterval(interval);
- }
+ setTimeout(function() {
+ $('.ol-attribution a').attr('target', '_blank');
}, 100);
+ // save map view settings when user moves it
+ map.on('moveend', function (evt) {
+ chrome.storage.local.set({'missionPlanerLastValues': {
+ center: ol.proj.toLonLat(map.getView().getCenter()),
+ zoom: map.getView().getZoom()
+ }});
+ });
+
+ // load map view settings on startup
+ chrome.storage.local.get('missionPlanerLastValues', function (result) {
+ if (result.missionPlanerLastValues && result.missionPlanerLastValues.center) {
+ map.getView().setCenter(ol.proj.fromLonLat(result.missionPlanerLastValues.center));
+ map.getView().setZoom(result.missionPlanerLastValues.zoom);
+ }
+ });
+
map.on('click', function (evt) {
if (selectedMarker != null) {
try {
@@ -392,14 +448,14 @@ TABS.mission_control.initialize = function (callback) {
selectedFeature.setStyle(getPointIcon(true));
- $('#pointLon').val(coord[0]);
- $('#pointLat').val(coord[1]);
+ $('#pointLon').val(Math.round(coord[0] * 10000000) / 10000000);
+ $('#pointLat').val(Math.round(coord[1] * 10000000) / 10000000);
$('#pointAlt').val(selectedMarker.alt);
$('#pointType').val(selectedMarker.action);
$('#pointSpeed').val(selectedMarker.speedValue);
$('#MPeditPoint').fadeIn(300);
} else {
- map.addLayer(addMarker(evt.coordinate, $('#MPdefaultPointAlt').val(), 1, $('#MPdefaultPointSpeed').val()));
+ map.addLayer(addMarker(evt.coordinate, settings.alt, MWNP.WPTYPE.WAYPOINT, settings.speed));
repaint();
}
});
@@ -417,8 +473,15 @@ TABS.mission_control.initialize = function (callback) {
}
});
+ // handle map size on container resize
+ setInterval(function () {
+ let width = $("#missionMap canvas").width(), height = $("#missionMap canvas").height();
+ if ((map.width_ != width) || (map.height_ != height)) map.updateSize();
+ map.width_ = width; map.height_ = height;
+ }, 200);
+
$('#removeAllPoints').on('click', function () {
- if (confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) {
+ if (markers.length && confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) {
removeAllPoints();
}
});
@@ -460,13 +523,28 @@ TABS.mission_control.initialize = function (callback) {
}
});
+ $('#loadFileMissionButton').on('click', function () {
+ if (markers.length && !confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) return;
+ removeAllPoints();
+ var dialog = require('nw-dialog');
+ dialog.setContext(document);
+ dialog.openFileDialog(function(result) {
+ loadMissionFile(result);
+ })
+ });
+
+ $('#saveFileMissionButton').on('click', function () {
+ //if (!markers.length) return;
+ var dialog = require('nw-dialog');
+ dialog.setContext(document);
+ dialog.saveFileDialog('', '.mission', function(result) {
+ saveMissionFile(result);
+ })
+ });
+
$('#loadMissionButton').on('click', function () {
- if (markers.length) {
- if (!confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) {
- return;
- }
- removeAllPoints();
- }
+ if (markers.length && !confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) return;
+ removeAllPoints();
$(this).addClass('disabled');
GUI.log('Start get point');
@@ -483,12 +561,8 @@ TABS.mission_control.initialize = function (callback) {
});
$('#loadEepromMissionButton').on('click', function () {
- if (markers.length) {
- if (!confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) {
- return;
- }
- removeAllPoints();
- }
+ if (markers.length && !confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) return;
+ removeAllPoints();
GUI.log(chrome.i18n.getMessage('eeprom_load_ok'));
MSP.send_message(MSPCodes.MSP_WP_MISSION_LOAD, [0], getPointsFromEprom);
@@ -507,26 +581,206 @@ TABS.mission_control.initialize = function (callback) {
});
$('#saveSettings').on('click', function () {
- chrome.storage.local.set({'missionPlanerSettings': {speed: $('#MPdefaultPointSpeed').val(), alt: $('#MPdefaultPointAlt').val()}});
- $('#missionPlanerSettings').hide();
- $('#missionPalnerTotalInfo').fadeIn(300);
- if (selectedMarker !== null) {
- $('#MPeditPoint').fadeIn(300);
- }
+ settings = { speed: $('#MPdefaultPointSpeed').val(), alt: $('#MPdefaultPointAlt').val() };
+ saveSettings();
+ closeSettingsPanel();
+ });
+
+ $('#cancelSettings').on('click', function () {
+ loadSettings();
+ closeSettingsPanel();
});
updateTotalInfo();
}
+ function closeSettingsPanel() {
+ $('#missionPlanerSettings').hide();
+ $('#missionPalnerTotalInfo').fadeIn(300);
+ if (selectedMarker !== null) {
+ $('#MPeditPoint').fadeIn(300);
+ }
+ }
+
function removeAllPoints() {
for (var i in markers) {
map.removeLayer(markers[i]);
}
markers = [];
clearEditForm();
+ updateTotalInfo();
+ $('#rthEndMission').prop('checked', false);
+ $('#rthSettings').fadeOut(300);
+ $('#rthLanding').prop('checked', false);
repaint();
}
+ function loadMissionFile(filename) {
+ const fs = require('fs-extra');
+ const xml2js = require('xml2js');
+
+ fs.readFile(filename, (err, data) => {
+ if (err) {
+ GUI.log('<span style="color: red">Error reading file</span>');
+ return console.error(err);
+ }
+
+ xml2js.Parser({ 'explicitChildren': true, 'preserveChildrenOrder': true }).parseString(data, (err, result) => {
+ if (err) {
+ GUI.log('<span style="color: red">Error parsing file</span>');
+ return console.error(err);
+ }
+
+ // parse mission file
+ var mission = { points: [] };
+ var node = null;
+ var nodemission = null;
+ for (var noderoot in result) {
+ if (!nodemission && noderoot.match(/mission/i)) {
+ nodemission = result[noderoot];
+ if (nodemission.$$ && nodemission.$$.length) {
+ for (var i = 0; i < nodemission.$$.length; i++) {
+ node = nodemission.$$[i];
+ if (node['#name'].match(/version/i) && node.$) {
+ for (var attr in node.$) {
+ if (attr.match(/value/i)) {
+ mission.version = node.$[attr]
+ }
+ }
+ } else if (node['#name'].match(/mwp/i) && node.$) {
+ mission.center = {};
+ for (var attr in node.$) {
+ if (attr.match(/zoom/i)) {
+ mission.center.zoom = parseInt(node.$[attr]);
+ } else if (attr.match(/cx/i)) {
+ mission.center.lon = parseFloat(node.$[attr]);
+ } else if (attr.match(/cy/i)) {
+ mission.center.lat = parseFloat(node.$[attr]);
+ }
+ }
+ } else if (node['#name'].match(/missionitem/i) && node.$) {
+ var point = {};
+ for (var attr in node.$) {
+ if (attr.match(/no/i)) {
+ point.index = parseInt(node.$[attr]);
+ } else if (attr.match(/action/i)) {
+ if (node.$[attr].match(/WAYPOINT/i)) {
+ point.action = MWNP.WPTYPE.WAYPOINT;
+ } else if (node.$[attr].match(/PH_UNLIM/i) || node.$[attr].match(/POSHOLD_UNLIM/i)) {
+ point.action = MWNP.WPTYPE.PH_UNLIM;
+ } else if (node.$[attr].match(/PH_TIME/i) || node.$[attr].match(/POSHOLD_TIME/i)) {
+ point.action = MWNP.WPTYPE.PH_TIME;
+ } else if (node.$[attr].match(/RTH/i)) {
+ point.action = MWNP.WPTYPE.RTH;
+ } else if (node.$[attr].match(/SET_POI/i)) {
+ point.action = MWNP.WPTYPE.SET_POI;
+ } else if (node.$[attr].match(/JUMP/i)) {
+ point.action = MWNP.WPTYPE.JUMP;
+ } else if (node.$[attr].match(/SET_HEAD/i)) {
+ point.action = MWNP.WPTYPE.SET_HEAD;
+ } else if (node.$[attr].match(/LAND/i)) {
+ point.action = MWNP.WPTYPE.LAND;
+ } else {
+ point.action = 0;
+ }
+ } else if (attr.match(/lat/i)) {
+ point.lat = parseFloat(node.$[attr]);
+ } else if (attr.match(/lon/i)) {
+ point.lon = parseFloat(node.$[attr]);
+ } else if (attr.match(/alt/i)) {
+ point.alt = (parseInt(node.$[attr]) * 100);
+ } else if (attr.match(/parameter1/i)) {
+ point.p1 = parseInt(node.$[attr]);
+ } else if (attr.match(/parameter2/i)) {
+ point.p2 = parseInt(node.$[attr]);
+ } else if (attr.match(/parameter3/i)) {
+ point.p3 = parseInt(node.$[attr]);
+ }
+ }
+ mission.points.push(point);
+ }
+ }
+ }
+ }
+ }
+
+ // draw actual mission
+ removeAllPoints();
+ for (var i = 0; i < mission.points.length; i++) {
+ //if ([MWNP.WPTYPE.WAYPOINT,MWNP.WPTYPE.PH_UNLIM,MWNP.WPTYPE.PH_TIME,MWNP.WPTYPE.LAND].includes(mission.points[i].action)) {
+ if (mission.points[i].action == MWNP.WPTYPE.WAYPOINT) {
+ var coord = ol.proj.fromLonLat([mission.points[i].lon, mission.points[i].lat]);
+ map.addLayer(addMarker(coord, mission.points[i].alt, mission.points[i].action, mission.points[i].p1));
+ if (i == 0) {
+ map.getView().setCenter(coord);
+ map.getView().setZoom(16);
+ }
+ } else if (mission.points[i].action == MWNP.WPTYPE.RTH) {
+ $('#rthEndMission').prop('checked', true);
+ $('#rthSettings').fadeIn(300);
+ if (mission.points[i].p1 > 0) {
+ $('#rthLanding').prop('checked', true);
+ }
+ }
+ }
+
+ if (mission.center) {
+ var coord = ol.proj.fromLonLat([mission.center.lon, mission.center.lat]);
+ map.getView().setCenter(coord);
+ if (mission.center.zoom) map.getView().setZoom(mission.center.zoom);
+ }
+
+ repaint();
+ updateTotalInfo();
+
+ });
+
+ });
+ }
+
+ function saveMissionFile(filename) {
+ const fs = require('fs-extra');
+ const xml2js = require('xml2js');
+
+ var center = ol.proj.toLonLat(map.getView().getCenter());
+ var zoom = map.getView().getZoom();
+
+ var data = {
+ 'version': { $: { 'value': '2.3-pre8' } },
+ 'mwp': { $: { 'cx': (Math.round(center[0] * 10000000) / 10000000), 'cy': (Math.round(center[1] * 10000000) / 10000000), 'zoom': zoom } },
+ 'missionitem': []
+ };
+
+ for (var i = 0; i < markers.length; i++) {
+ var geometry = markers[i].getSource().getFeatures()[0].getGeometry();
+ var coordinate = ol.proj.toLonLat(geometry.getCoordinates());
+ var point = { $: {
+ 'no': (i + 1),
+ 'action': ((markers[i].action == MWNP.WPTYPE.WAYPOINT) ? 'WAYPOINT' : markers[i].action),
+ 'lon': (Math.round(coordinate[0] * 10000000) / 10000000),
+ 'lat': (Math.round(coordinate[1] * 10000000) / 10000000),
+ 'alt': (markers[i].alt / 100)
+ } };
+ if ((markers[i].action == MWNP.WPTYPE.WAYPOINT) && (markers[i].speedValue > 0)) point.$['parameter1'] = markers[i].speedValue;
+ data.missionitem.push(point);
+ }
+
+ // add last RTH point
+ if ($('#rthEndMission').is(':checked')) {
+ data.missionitem.push({ $: { 'no': (markers.length + 1), 'action': 'RTH', 'lon': 0, 'lat': 0, 'alt': (settings.alt / 100), 'parameter1': ($('#rthLanding').is(':checked') ? 1 : 0) } });
+ }
+
+ var builder = new xml2js.Builder({ 'rootName': 'mission', 'renderOpts': { 'pretty': true, 'indent': '\t', 'newline': '\n' } });
+ var xml = builder.buildObject(data);
+ fs.writeFile(filename, xml, (err) => {
+ if (err) {
+ GUI.log('<span style="color: red">Error writing file</span>');
+ return console.error(err);
+ }
+ GUI.log('File saved');
+ });
+ }
+
function getPointsFromEprom() {
pointForSend = 0;
MSP.send_message(MSPCodes.MSP_WP_GETINFO, false, false, getNextPoint);
@@ -551,10 +805,10 @@ TABS.mission_control.initialize = function (callback) {
// console.log(MISSION_PLANER.bufferPoint.alt);
// console.log(MISSION_PLANER.bufferPoint.action);
if (MISSION_PLANER.bufferPoint.action == 4) {
- $('#rthEndMission').attr('checked', true);
+ $('#rthEndMission').prop('checked', true);
$('#rthSettings').fadeIn(300);
if (MISSION_PLANER.bufferPoint.p1 > 0) {
- $('#rthLanding').attr('checked', true);
+ $('#rthLanding').prop('checked', true);
}
} else {
var coord = ol.proj.fromLonLat([MISSION_PLANER.bufferPoint.lon, MISSION_PLANER.bufferPoint.lat]);