diff options
author | mirko-it <mirko-it@users.noreply.github.com> | 2019-04-29 18:23:32 +0300 |
---|---|---|
committer | giacomo892 <giacomo1989@gmail.com> | 2019-04-29 18:23:32 +0300 |
commit | 3dcffdd2928903f63c9f856968c5055bd4e8dfa3 (patch) | |
tree | 7c55b1a84e33d2af1a357c639ff168a650b9a08a /tabs | |
parent | cec7997bfc2d0c6ff452d6c1a73e8530970d6094 (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.html | 23 | ||||
-rw-r--r-- | tabs/mission_control.js | 380 |
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]); |