'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) { let cursorInitialized = false; let curPosStyle; let curPosGeo; let rthGeo; let breadCrumbLS; let breadCrumbFeature; let breadCrumbStyle; let breadCrumbSource; let breadCrumbVector; let textStyle; let textFeature; var textGeom; let isOffline = false; let rthUpdateInterval = 0; if (GUI.active_tab != 'mission_control') { GUI.active_tab = 'mission_control'; googleAnalytics.sendAppView('Mission Control'); } 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() { 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() { GUI.load("./tabs/mission_control.html", process_html); } function process_html() { // set GUI for offline operations if (!CONFIGURATOR.connectionValid) { $('#infoAvailablePoints').hide(); $('#infoMissionValid').hide(); $('#loadMissionButton').hide(); $('#saveMissionButton').hide(); $('#loadEepromMissionButton').hide(); $('#saveEepromMissionButton').hide(); isOffline = true; } 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(); } localize(); function get_raw_gps_data() { MSP.send_message(MSPCodes.MSP_RAW_GPS, false, false, get_comp_gps_data); } function get_comp_gps_data() { MSP.send_message(MSPCodes.MSP_COMP_GPS, false, false, get_altitude_data); } function get_altitude_data() { MSP.send_message(MSPCodes.MSP_ALTITUDE, false, false, get_attitude_data); } function get_attitude_data() { MSP.send_message(MSPCodes.MSP_ATTITUDE, false, false, update_gpsTrack); } function update_gpsTrack() { let lat = GPS_DATA.lat / 10000000; let lon = GPS_DATA.lon / 10000000; //Update map if (GPS_DATA.fix >= 2) { if (!cursorInitialized) { cursorInitialized = true; ///////////////////////////////////// //create layer for current position curPosStyle = new ol.style.Style({ image: new ol.style.Icon(({ anchor: [0.5, 0.5], opacity: 1, scale: 0.6, src: '../images/icons/icon_mission_airplane.png' })) }); let currentPositionLayer; curPosGeo = new ol.geom.Point(ol.proj.fromLonLat([lon, lat])); let curPosFeature = new ol.Feature({ geometry: curPosGeo }); curPosFeature.setStyle(curPosStyle); let vectorSource = new ol.source.Vector({ features: [curPosFeature] }); currentPositionLayer = new ol.layer.Vector({ source: vectorSource }); /////////////////////////// //create layer for RTH Marker let rthStyle = new ol.style.Style({ image: new ol.style.Icon(({ anchor: [0.5, 1.0], opacity: 1, scale: 0.5, src: '../images/icons/cf_icon_RTH.png' })) }); rthGeo = new ol.geom.Point(ol.proj.fromLonLat([90, 0])); let rthFeature = new ol.Feature({ geometry: rthGeo }); rthFeature.setStyle(rthStyle); let rthVector = new ol.source.Vector({ features: [rthFeature] }); let rthLayer = new ol.layer.Vector({ source: rthVector }); ////////////////////////////// //create layer for bread crumbs breadCrumbLS = new ol.geom.LineString([ol.proj.fromLonLat([lon, lat]), ol.proj.fromLonLat([lon, lat])]); breadCrumbStyle = new ol.style.Style({ stroke: new ol.style.Stroke({ color: '#ffcc33', width: 6 }) }); breadCrumbFeature = new ol.Feature({ geometry: breadCrumbLS }); breadCrumbFeature.setStyle(breadCrumbStyle); breadCrumbSource = new ol.source.Vector({ features: [breadCrumbFeature] }); breadCrumbVector = new ol.layer.Vector({ source: breadCrumbSource }); ///////////////////////////// //create layer for heading, alt, groundspeed textGeom = new ol.geom.Point([0,0]); textStyle = new ol.style.Style({ text: new ol.style.Text({ font: 'bold 35px Calibri,sans-serif', fill: new ol.style.Fill({ color: '#fff' }), offsetX: map.getSize()[0]-260, offsetY: 80, textAlign: 'left', backgroundFill: new ol.style.Fill({ color: '#000' }), stroke: new ol.style.Stroke({ color: '#fff', width: 2 }), text: 'H: XXX\nAlt: XXXm\nSpeed: XXXcm/s' }) }); textFeature = new ol.Feature({ geometry: textGeom }); textFeature.setStyle(textStyle); var textSource = new ol.source.Vector({ features: [textFeature] }); var textVector = new ol.layer.Vector({ source: textSource }); map.addLayer(rthLayer); map.addLayer(breadCrumbVector); map.addLayer(currentPositionLayer); map.addControl(textVector); } let gpsPos = ol.proj.fromLonLat([lon, lat]); curPosGeo.setCoordinates(gpsPos); breadCrumbLS.appendCoordinate(gpsPos); var coords = breadCrumbLS.getCoordinates(); if(coords.length > 100) { coords.shift(); breadCrumbLS.setCoordinates(coords); } curPosStyle.getImage().setRotation((SENSOR_DATA.kinematics[2]/360.0) * 6.28318); //update data text textGeom.setCoordinates(map.getCoordinateFromPixel([0,0])); let tmpText = textStyle.getText(); tmpText.setText(' \n' + 'H: ' + SENSOR_DATA.kinematics[2] + '\nAlt: ' + SENSOR_DATA.altitude + 'm\nSpeed: ' + GPS_DATA.speed + 'cm/s\n' + 'Dist: ' + GPS_DATA.distanceToHome + 'm'); //update RTH every 5th GPS update since it really shouldn't change if(rthUpdateInterval >= 5) { MISSION_PLANER.bufferPoint.number = -1; //needed to get point 0 which id RTH MSP.send_message(MSPCodes.MSP_WP, mspHelper.crunch(MSPCodes.MSP_WP), false, function rth_update() { var coord = ol.proj.fromLonLat([MISSION_PLANER.bufferPoint.lon, MISSION_PLANER.bufferPoint.lat]); rthGeo.setCoordinates(coord); }); rthUpdateInterval = 0; } rthUpdateInterval++; } } /* * enable data pulling if not offline * Refreshing data at 5Hz... Could slow this down if we have performance issues */ if(!isOffline) { helper.mspBalancedInterval.add('gps_pull', 200, 3, function gps_update() { // avoid usage of the GPS commands until a GPS sensor is detected for targets that are compiled without GPS support. if (!have_sensor(CONFIG.activeSensors, 'gps')) { update_gpsTrack(); return; } if (helper.mspQueue.shouldDrop()) { return; } get_raw_gps_data(); }); } GUI.content_ready(callback); } var markers = []; var lines = []; var map; var selectedMarker = null; var pointForSend = 0; var settings = { speed: 0, alt: 5000 }; function clearEditForm() { $('#pointLat').val(''); $('#pointLon').val(''); $('#pointAlt').val(''); $('#pointSpeed').val(''); $('[name=pointNumber]').val(''); $('#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) { map.removeLayer(lines[i]); } lines = []; $('#missionDistance').text(0); map.getLayers().forEach(function (t) { //feature.getGeometry().getType() if (t instanceof ol.layer.Vector && typeof t.alt !== 'undefined') { var geometry = t.getSource().getFeatures()[0].getGeometry(); if (typeof oldPos !== 'undefined') { paintLine(oldPos, geometry.getCoordinates()); } oldPos = geometry.getCoordinates(); } }); //reset text position if (textGeom) { textGeom.setCoordinates(map.getCoordinateFromPixel([0,0])); } } function paintLine(pos1, pos2) { var line = new ol.geom.LineString([pos1, pos2]); var feature = new ol.Feature({ geometry: line }); feature.setStyle(new ol.style.Style({ stroke: new ol.style.Stroke({ color: '#1497f1', width: 3 }) })); var vectorSource = new ol.source.Vector({ features: [feature] }); var vectorLayer = new ol.layer.Vector({ source: vectorSource }); lines.push(vectorLayer); var length = ol.Sphere.getLength(line) + parseFloat($('#missionDistance').text()); $('#missionDistance').text(length.toFixed(3)); map.addLayer(vectorLayer); } function getPointIcon(isEdit) { return new ol.style.Style({ image: new ol.style.Icon(({ anchor: [0.5, 1], opacity: 1, 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' }) }) */ }); } function addMarker(_pos, _alt, _action, _speed) { var iconFeature = new ol.Feature({ geometry: new ol.geom.Point(_pos), name: 'Null Island', population: 4000, rainfall: 500 }); iconFeature.setStyle(getPointIcon()); var vectorSource = new ol.source.Vector({ features: [iconFeature] }); var vectorLayer = new ol.layer.Vector({ source: vectorSource }); vectorLayer.alt = _alt; vectorLayer.number = markers.length; vectorLayer.action = _action; vectorLayer.speedValue = _speed; markers.push(vectorLayer); return vectorLayer; } function initMap() { var app = {}; /** * @constructor * @extends {ol.interaction.Pointer} */ app.Drag = function () { ol.interaction.Pointer.call(this, { handleDownEvent: app.Drag.prototype.handleDownEvent, handleDragEvent: app.Drag.prototype.handleDragEvent, handleMoveEvent: app.Drag.prototype.handleMoveEvent, handleUpEvent: app.Drag.prototype.handleUpEvent }); /** * @type {ol.Pixel} * @private */ this.coordinate_ = null; /** * @type {string|undefined} * @private */ this.cursor_ = 'pointer'; /** * @type {ol.Feature} * @private */ this.feature_ = null; /** * @type {string|undefined} * @private */ this.previousCursor_ = undefined; }; ol.inherits(app.Drag, ol.interaction.Pointer); /** * @constructor * @extends {ol.control.Control} * @param {Object=} opt_options Control options. */ app.PlannerSettingsControl = function (opt_options) { var options = opt_options || {}; var button = document.createElement('button'); button.innerHTML = ' '; button.style = 'background: url(\'../images/CF_settings_white.svg\') no-repeat 1px -1px;background-color: rgba(0,60,136,.5);'; var handleShowSettings = function () { $('#MPeditPoint, #missionPalnerTotalInfo').hide(); $('#missionPlanerSettings').fadeIn(300); }; button.addEventListener('click', handleShowSettings, false); button.addEventListener('touchstart', handleShowSettings, false); var element = document.createElement('div'); element.className = 'mission-control-settings ol-unselectable ol-control'; element.appendChild(button); element.title = 'MP Settings'; ol.control.Control.call(this, { element: element, target: options.target }); }; ol.inherits(app.PlannerSettingsControl, ol.control.Control); /** * @param {ol.MapBrowserEvent} evt Map browser event. * @return {boolean} `true` to start the drag sequence. */ app.Drag.prototype.handleDownEvent = function (evt) { var map = evt.map; var feature = map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) { return feature; }); if (feature) { this.coordinate_ = evt.coordinate; this.feature_ = feature; } return !!feature; }; /** * @param {ol.MapBrowserEvent} evt Map browser event. */ app.Drag.prototype.handleDragEvent = function (evt) { var map = evt.map; var feature = map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) { return feature; }); var deltaX = evt.coordinate[0] - this.coordinate_[0]; var deltaY = evt.coordinate[1] - this.coordinate_[1]; var geometry = /** @type {ol.geom.SimpleGeometry} */ (this.feature_.getGeometry()); geometry.translate(deltaX, deltaY); this.coordinate_[0] = evt.coordinate[0]; this.coordinate_[1] = evt.coordinate[1]; repaint(); }; /** * @param {ol.MapBrowserEvent} evt Event. */ app.Drag.prototype.handleMoveEvent = function (evt) { if (this.cursor_) { var map = evt.map; var feature = map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) { return feature; }); var element = evt.map.getTargetElement(); if (feature) { if (element.style.cursor != this.cursor_) { this.previousCursor_ = element.style.cursor; element.style.cursor = this.cursor_; } } else if (this.previousCursor_ !== undefined) { element.style.cursor = this.previousCursor_; this.previousCursor_ = undefined; } } }; /** * @param {ol.MapBrowserEvent} evt Map browser event. * @return {boolean} `false` to stop the drag sequence. */ app.Drag.prototype.handleUpEvent = function (evt) { this.coordinate_ = null; this.feature_ = null; return false; }; var lat = (GPS_DATA ? (GPS_DATA.lat / 10000000) : 0); var lon = (GPS_DATA ? (GPS_DATA.lon / 10000000) : 0); let mapLayer; if (globalSettings.mapProviderType == 'bing') { mapLayer = new ol.source.BingMaps({ key: globalSettings.mapApiKey, imagerySet: 'AerialWithLabels', maxZoom: 19 }); } else if ( globalSettings.mapProviderType == 'mapproxy' ) { mapLayer = new ol.source.TileWMS({ url: globalSettings.proxyURL, params: {'LAYERS':globalSettings.proxyLayer} }) } else { mapLayer = new ol.source.OSM(); } map = new ol.Map({ controls: ol.control.defaults({ attributionOptions: { collapsible: false } }).extend([ new app.PlannerSettingsControl() ]), interactions: ol.interaction.defaults().extend([new app.Drag()]), layers: [ new ol.layer.Tile({ source: mapLayer }) ], target: document.getElementById('missionMap'), view: new ol.View({ center: ol.proj.fromLonLat([lon, lat]), zoom: 2 }) }); // Set the attribute link to open on an external browser window, so // it doesn't interfere with the configurator. 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 { selectedMarker.getSource().getFeatures()[0].setStyle(getPointIcon()); selectedMarker = null; clearEditForm(); } catch (e) { GUI.log(e); } } var selectedFeature = map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) { return feature; }); var tempMarker = map.forEachFeatureAtPixel(evt.pixel, function (feature, layer) { return layer; }); if (selectedFeature) { for (var i in markers) { if (markers[i] == tempMarker) { selectedMarker = tempMarker; var geometry = selectedFeature.getGeometry(); var coord = ol.proj.toLonLat(geometry.getCoordinates()); selectedFeature.setStyle(getPointIcon(true)); $('#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, settings.alt, MWNP.WPTYPE.WAYPOINT, settings.speed)); repaint(); } }); // change mouse cursor when over marker $(map.getViewport()).on('mousemove', function (e) { var pixel = map.getEventPixel(e.originalEvent); var hit = map.forEachFeatureAtPixel(pixel, function (feature, layer) { return true; }); if (hit) { map.getTarget().style.cursor = 'pointer'; } else { map.getTarget().style.cursor = ''; } }); // 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 (markers.length && confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) { removeAllPoints(); } }); $('#removePoint').on('click', function () { if (selectedMarker) { var tmp = []; for (var i in markers) { if (markers[i] !== selectedMarker && typeof markers[i].action !== "undefined") { tmp.push(markers[i]); } } map.removeLayer(selectedMarker); markers = tmp; selectedMarker = null; clearEditForm(); repaint(); } }); $('#savePoint').on('click', function () { if (selectedMarker) { map.getLayers().forEach(function (t) { if (t === selectedMarker) { var geometry = t.getSource().getFeatures()[0].getGeometry(); geometry.setCoordinates(ol.proj.fromLonLat([parseFloat($('#pointLon').val()), parseFloat($('#pointLat').val())])); t.alt = $('#pointAlt').val(); t.action = $('#pointType').val(); t.speedValue = $('#pointSpeed').val(); } }); selectedMarker.getSource().getFeatures()[0].setStyle(getPointIcon()); selectedMarker = null; clearEditForm(); repaint(); } }); $('#loadFileMissionButton').on('click', function () { if (markers.length && !confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) return; removeAllPoints(); nwdialog.setContext(document); nwdialog.openFileDialog(function(result) { loadMissionFile(result); }) }); $('#saveFileMissionButton').on('click', function () { //if (!markers.length) return; nwdialog.setContext(document); nwdialog.saveFileDialog('', '.mission', function(result) { saveMissionFile(result); }) }); $('#loadMissionButton').on('click', function () { if (markers.length && !confirm(chrome.i18n.getMessage('confirm_delete_all_points'))) return; removeAllPoints(); $(this).addClass('disabled'); GUI.log('Start get point'); pointForSend = 0; getNextPoint(); }); $('#saveMissionButton').on('click', function () { $(this).addClass('disabled'); GUI.log('Start send point'); pointForSend = 0; sendNextPoint(); }); $('#loadEepromMissionButton').on('click', function () { 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); }); $('#saveEepromMissionButton').on('click', function () { GUI.log(chrome.i18n.getMessage('eeprom_saved_ok')); MSP.send_message(MSPCodes.MSP_WP_MISSION_SAVE, [0], false); }); $('#rthEndMission').on('change', function () { if ($(this).is(':checked')) { $('#rthSettings').fadeIn(300); } else { $('#rthSettings').fadeOut(300); } }); $('#saveSettings').on('click', function () { 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'); if (!window.xml2js) return GUI.log('Error reading file (xml2js not found)'); fs.readFile(filename, (err, data) => { if (err) { GUI.log('Error reading file'); return console.error(err); } window.xml2js.Parser({ 'explicitChildren': true, 'preserveChildrenOrder': true }).parseString(data, (err, result) => { if (err) { GUI.log('Error parsing file'); 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'); if (!window.xml2js) return GUI.log('Error writing file (xml2js not found)'); 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 window.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('Error writing file'); return console.error(err); } GUI.log('File saved'); }); } function getPointsFromEprom() { pointForSend = 0; MSP.send_message(MSPCodes.MSP_WP_GETINFO, false, false, getNextPoint); } function endGetPoint() { GUI.log('End get point'); $('#loadMissionButton').removeClass('disabled'); repaint(); updateTotalInfo(); } function getNextPoint() { if (MISSION_PLANER.countBusyPoints == 0) { endGetPoint(); return; } if (pointForSend > 0) { // console.log(MISSION_PLANER.bufferPoint.lon); // console.log(MISSION_PLANER.bufferPoint.lat); // console.log(MISSION_PLANER.bufferPoint.alt); // console.log(MISSION_PLANER.bufferPoint.action); if (MISSION_PLANER.bufferPoint.action == 4) { $('#rthEndMission').prop('checked', true); $('#rthSettings').fadeIn(300); if (MISSION_PLANER.bufferPoint.p1 > 0) { $('#rthLanding').prop('checked', true); } } else { var coord = ol.proj.fromLonLat([MISSION_PLANER.bufferPoint.lon, MISSION_PLANER.bufferPoint.lat]); map.addLayer(addMarker(coord, MISSION_PLANER.bufferPoint.alt, MISSION_PLANER.bufferPoint.action, MISSION_PLANER.bufferPoint.p1)); if (pointForSend === 1) { map.getView().setCenter(coord); } } } if (pointForSend >= MISSION_PLANER.countBusyPoints) { endGetPoint(); return; } MISSION_PLANER.bufferPoint.number = pointForSend; pointForSend++; MSP.send_message(MSPCodes.MSP_WP, mspHelper.crunch(MSPCodes.MSP_WP), false, getNextPoint); } function sendNextPoint() { var isRTH = $('#rthEndMission').is(':checked'); if (pointForSend >= markers.length) { if (isRTH) { MISSION_PLANER.bufferPoint.number = pointForSend + 1; MISSION_PLANER.bufferPoint.action = 4; MISSION_PLANER.bufferPoint.lon = 0; MISSION_PLANER.bufferPoint.lat = 0; MISSION_PLANER.bufferPoint.alt = 0; MISSION_PLANER.bufferPoint.endMission = 0xA5; MISSION_PLANER.bufferPoint.p1 = $('#rthLanding').is(':checked') ? 1 : 0; MSP.send_message(MSPCodes.MSP_SET_WP, mspHelper.crunch(MSPCodes.MSP_SET_WP), false, endSendPoint); } else { endSendPoint(); } return; } var geometry = markers[pointForSend].getSource().getFeatures()[0].getGeometry(); var coordinate = ol.proj.toLonLat(geometry.getCoordinates()); MISSION_PLANER.bufferPoint.number = pointForSend + 1; MISSION_PLANER.bufferPoint.action = markers[pointForSend].action; MISSION_PLANER.bufferPoint.lon = parseInt(coordinate[0] * 10000000); MISSION_PLANER.bufferPoint.lat = parseInt(coordinate[1] * 10000000); MISSION_PLANER.bufferPoint.alt = markers[pointForSend].alt; MISSION_PLANER.bufferPoint.p1 = markers[pointForSend].speedValue; pointForSend++; if (pointForSend >= markers.length && !isRTH) { MISSION_PLANER.bufferPoint.endMission = 0xA5; } else { MISSION_PLANER.bufferPoint.endMission = 0; } MSP.send_message(MSPCodes.MSP_SET_WP, mspHelper.crunch(MSPCodes.MSP_SET_WP), false, sendNextPoint); } function endSendPoint() { GUI.log('End send point'); MSP.send_message(MSPCodes.MSP_WP_GETINFO, false, false, updateTotalInfo); $('#saveMissionButton').removeClass('disabled'); } }; TABS.mission_control.cleanup = function (callback) { if (callback) callback(); };