'use strict' const path = require('path'); const { GUI, TABS } = require('./../js/gui'); const i18n = require('./../js/localization'); const { SITLProcess, SitlSerialPortUtils } = require('./../js/sitl'); const Store = require('electron-store'); const store = new Store(); const localhost = "127.0.0.1" const simulators = [ { name: "X-Plane", port: 49001, isPortFixed: false, inputChannels: 4, fixedChanMap: ["Throttle", "Roll", "Pitch", "Yaw" ] }, { name: "RealFlight", port: 18083, isPortFixed: true, inputChannels: 12, fixedChanMap: false } ]; const stdProfiles = [ { name: "[Standard] Configurator", sim: "X-Plane", eepromFileName: "standard-configurator.bin", isStdProfile: true, simEnabled: false, port: 49001, ip: "127.0.0.1", useImu: false, channelMap: [ 1, 15, 13, 16], useSerialReceiver: true, serialPort: "", serialUart: 3, serialProtocol: "SBus", baudRate: false, stopBits: false, parity: false }, { name: "[Standard] X-Plane", sim: "X-Plane", eepromFileName: "standard-x-plane.bin", isStdProfile: true, simEnabled: true, port: 49001, ip: "127.0.0.1", useImu: false, channelMap: [ 1, 15, 13, 16], useSerialReceiver: true, serialPort: "", serialUart: 3, serialProtocol: "SBus", baudRate: false, stopBits: false, parity: false }, { name: "[Standard] RealFlight Flying Wing", sim: "RealFlight", eepromFileName: "standard-realflight.bin", isStdProfile: true, simEnabled: true, port: 49001, ip: "127.0.0.1", useImu: false, channelMap: [ 1, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0], useSerialReceiver: true, serialPort: "", serialUart: 3, serialProtocol: "SBus", baudRate: false, stopBits: false, parity: false } ]; var SITL_LOG = ""; TABS.sitl = {}; TABS.sitl.initialize = (callback) => { if (GUI.active_tab != 'sitl') { GUI.active_tab = 'sitl'; } GUI.load(path.join(__dirname, "sitl.html"), function () { i18n.localize(); var currentSim, currentProfile, profiles; var mapping = new Array(28).fill(0); var serialProtocolls = SitlSerialPortUtils.getProtocolls(); var sim_e = $('#simulator'); var enableSim_e = $('#sitlEnableSim'); var port_e = $('#simPort'); var simIp_e = $('#simIP'); var useImu_e = $('#sitlUseImu'); var profiles_e = $('#sitlProfile'); var profileSaveBtn_e = $('#sitlProfileSave'); var profileNewBtn_e = $('#sitlProfileNew'); var profileDeleteBtn_e = $('#sitlProfileDelete'); var serialPorts_e = $('#sitlSerialPort'); var serialReceiverEnable_e = $('#serialReceiverEnable'); var serialUart_e = $('#sitlSerialUART'); var protocollPreset_e = $('#serialProtocoll'); var baudRate_e = $('#sitlBaud'); var stopBits_e = $('#serialStopbits'); var parity_e = $('#serialParity'); if (SITLProcess.isRunning) { $('.sitlStart').addClass('disabled'); $('.sitlStop').removeClass('disabled'); } else { $('.sitlStop').addClass('disabled'); $('.sitlStart').removeClass('disabled'); } var $sitlLog = $('#sitlLog'); $sitlLog.val(SITL_LOG); if ($sitlLog && $sitlLog.length == 1) { $sitlLog.val(SITL_LOG); $sitlLog.animate({scrollTop: $sitlLog[0].scrollHeight - $sitlLog.height()}, "fast"); } profiles = stdProfiles.slice(0); var sitlProfiles = store.get('sitlProfiles', false); if (sitlProfiles) { profiles.push(...sitlProfiles); } initElements(true); SitlSerialPortUtils.resetPortsList(); SitlSerialPortUtils.pollSerialPorts(ports => { serialPorts_e.find('*').remove(); ports.forEach(port => { serialPorts_e.append(``) }); }); enableSim_e.on('change', () => { currentProfile.simEnabled = enableSim_e.is(':checked'); updateSim(); }); sim_e.on('change', () => { updateSim(); }); profiles_e.on('change', () => { updateCurrentProfile(); }); port_e.on('change', () => { if (!currentSim.isPortFixed) { var port = parseInt(port_e.val()); if (port != NaN) currentProfile.port = parseInt(port_e.val()); } }); simIp_e.on('change', () => { currentProfile.ip = simIp_e.val(); }); useImu_e.on('change', () => { currentProfile.useImu = useImu_e.is(':checked'); }); $('.sitlStart').on('click', ()=> { $('.sitlStart').addClass('disabled'); $('.sitlStop').removeClass('disabled'); var sim, simPort, simIp, channelMap = ""; if (enableSim_e.is(':checked')) { switch(currentSim.name) { case 'X-Plane': sim = 'xp'; break; case 'RealFlight': sim = 'rf' break; } } if (port_e.val() !== "") { simPort = port_e.val(); } if (simIp_e.val() !== "") { simIp = simIp_e.val(); } const zeroPad = (num, places) => String(num).padStart(places, '0'); for (let i = 0; i < currentSim.inputChannels; i++) { var inavChan = mapping[i]; if (inavChan == 0) { continue; } else if (inavChan < 13) { channelMap += `M${zeroPad(inavChan, 2)}-${zeroPad(i + 1, 2)},`; } else { channelMap += `S${zeroPad(inavChan - 12, 2)}-${zeroPad(i + 1, 2)},`; } } channelMap = channelMap.substring(0, channelMap.length - 1); var serialOptions = null; if ( serialReceiverEnable_e.is(':checked') && !!serialPorts_e.val()) { var selectedProtocoll = protocollPreset_e.find(':selected').val(); if (selectedProtocoll == "manual") { serialOptions = { protocollName: "manual", baudRate: baudRate_e.val() || currentProfile.baudRate || "115200", stopBits: stopBits_e.val() || currentProfile.stopBits || "One", parity: parity_e.val() || currentProfile.parity || "None", serialPort: serialPorts_e.val() || currentProfile.serialPort || "", serialUart: serialUart_e.val() || currentProfile.serialUart || -1 } } else {; serialOptions = { protocollName: selectedProtocoll || "SBus", serialPort: serialPorts_e.val() || currentProfile.serialPort || "" , serialUart: serialUart_e.val() || currentProfile.serialUart || -1 } } } appendLog("\n"); SITLProcess.start(currentProfile.eepromFileName, sim, useImu_e.is(':checked'), simIp, simPort, channelMap, serialOptions,result => { appendLog(result); }); }); $('.sitlStop').on('click', ()=> { $('.sitlStop').addClass('disabled'); $('.sitlStart').removeClass('disabled'); SITLProcess.stop(); appendLog(i18n.getMessage('sitlStopped')); }); profileSaveBtn_e.on('click', function () { saveProfiles(); }); profileNewBtn_e.on('click', function () { var name = prompt(i18n.getMessage('sitlNewProfile'), i18n.getMessage('sitlEnterName')); if (!name) return; if (profiles.find(e => { return e.name == name })) { GUI.alert(i18n.getMessage('sitlProfileExists')) return; } var eerpromName = name.replace(/[^a-z0-9]/gi, '_').toLowerCase() + ".bin"; var profile = { name: name, sim: "RealFlight", isStdProfile: false, simEnabled: false, eepromFileName: eerpromName, port: 49001, ip: "127.0.0.1", useImu: false, channelMap: [ 1, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0], useSerialReceiver: true, serialPort: serialPorts_e.val(), serialUart: 3, serialProtocol: "SBus", baudRate: false, stopBits: false, parity: false } profiles.push(profile); profiles_e.append(``) profiles_e.val(name); updateCurrentProfile(); saveProfiles(); }); profileDeleteBtn_e.on('click', function () { if (currentProfile.isStdProfile) { GUI.alert(i18n.getMessage('sitlStdProfileCantDeleted')); return; } var selected = profiles_e.find(':selected').val(); profiles = profiles.filter(profile => { return profile.name != selected; }); SITLProcess.deleteEepromFile(currentProfile.eepromFileName); profiles_e.find('*').remove(); initElements(false); saveProfiles(); }); serialReceiverEnable_e.on('change', () => { currentProfile.useSerialReceiver = serialReceiverEnable_e.is(':checked'); }); protocollPreset_e.on('change', () => { var selectedProtocoll = protocollPreset_e.find(':selected').val(); var protocoll = serialProtocolls.find(protocoll => { return protocoll.name == selectedProtocoll; }); if (selectedProtocoll != 'manual'){ baudRate_e.prop('disabled', true); baudRate_e.val(protocoll.baudRate); stopBits_e.prop('disabled', true); stopBits_e.val(protocoll.stopBits); parity_e.prop('disabled', true); parity_e.val(protocoll.parity); serialUart_e.prop('disabled', selectedProtocoll == "Flight Controller Proxy"); } else { baudRate_e.prop('disabled', false); stopBits_e.prop('disabled', false); parity_e.prop('disabled', false); serialUart_e.prop('disabled', false); } currentProfile.serialProtocol = selectedProtocoll; }); serialPorts_e.on('change', () => { currentProfile.serialPort = serialPorts_e.val(); }); serialUart_e.on('change', () => { currentProfile.serialUart = parseInt(serialUart_e.val()); }); baudRate_e.on('change', () => { var baud = parseInt(baudRate_e.val()); if (baud != NaN) currentProfile.baudRate = baud }); stopBits_e.on('change', () => { currentProfile.stopBits = stopBits_e.val(); }); parity_e.on('change', () => { currentProfile.parity = parity_e.val(); }); function initElements(init) { profiles.forEach(profile => { profiles_e.append(``) }); if (init) { simulators.forEach(simulator => { sim_e.append(``) }); protocollPreset_e.append(''); serialProtocolls.forEach(protocoll => { protocollPreset_e.append(``); }); var sitlLastProfile = store.get('sitlLastProfile', false); if (sitlLastProfile) { var element = profiles.find(profile => { return profile.name == sitlLastProfile; }); if (element) profiles_e.val(element.name).trigger('change'); } } updateCurrentProfile(); } function saveProfiles() { if (currentProfile.isStdProfile) { GUI.alert(i18n.getMessage('sitlStdProfileCantOverwritten')); return; } var profilesToSave = []; profiles.forEach(profile => { if (!profile.isStdProfile) profilesToSave.push(profile); }); store.set('sitlProfiles', profilesToSave); } function updateSim() { simulators.forEach(simulator => { if (simulator.name == sim_e.find(':selected').text()) { currentSim = simulator; currentProfile.sim = currentSim.name; if (currentSim.isPortFixed) { port_e.val(currentSim.port).trigger('change'); } else { port_e.val(currentProfile.port); } sim_e.prop('disabled', !currentProfile.simEnabled); simIp_e.prop('disabled', !currentProfile.simEnabled); port_e.prop('disabled', simulator.isPortFixed || !currentProfile.simEnabled); useImu_e.prop('disabled', !currentProfile.simEnabled); renderChanMapTable(); return; } }); } function updateCurrentProfile() { var selected = profiles_e.find(':selected').val(); var selectedIndex = profiles.findIndex(element => { return element.name == selected; }); currentProfile = profiles[selectedIndex]; protocollPreset_e.val(currentProfile.serialProtocol); if (currentProfile.serialProtocol == "manual") { baudRate_e.val(currentProfile.baudRate); baudRate_e.prop('disabled', false); stopBits_e.val(currentProfile.stopBits); stopBits_e.prop('disabled', false); parity_e.val(currentProfile.parity); parity_e.prop('disabled', false); serialUart_e.prop('disabled', false); } else { var protocoll = serialProtocolls.find(protocoll => { return protocoll.name == currentProfile.serialProtocol; }); baudRate_e.prop('disabled', true); baudRate_e.val(protocoll.baudRate); stopBits_e.prop('disabled', true); stopBits_e.val(protocoll.stopBits); parity_e.prop('disabled', true); parity_e.val(protocoll.parity); serialUart_e.prop('disabled', currentProfile.serialProtocol == "Flight Controller Proxy"); } if (currentProfile.serialPort != "") serialPorts_e.val(currentProfile.serialPort); enableSim_e.prop('checked', currentProfile.simEnabled).trigger('change'); serialReceiverEnable_e.prop('checked', currentProfile.useSerialReceiver).trigger('change'); serialUart_e.val(currentProfile.serialUart); mapping = currentProfile.channelMap; sim_e.val(currentProfile.sim); updateSim(); simIp_e.val(currentProfile.ip).trigger('change'); useImu_e.prop('checked', currentProfile.useImu).trigger('change'); store.set('sitlLastProfile', selected); } function renderChanMapTable() { var mapTableBody = $('.mapTableBody'); mapTableBody.find('*').remove(); for (let i = 0; i < currentSim.inputChannels; i++) { var output; if (currentSim.fixedChanMap) { output = currentSim.fixedChanMap[i]; } else { output = i + 1; } mapTableBody.append("" + output + ""); const row = mapTableBody.find('tr:last'); GUI.fillSelect(row.find(".inavChannel"), getInavChannels(), mapping[i]); row.find(".inavChannel").val(mapping[i]).on('change', (sender) => { mapping[$(sender.target).data('out')] = parseInt($(sender.target).val()); store.set('sitlMapping', mapping); }); } } function getInavChannels() { var channels = []; for (var i = 0; i <= 12; i++) { if (i == 0) channels.push("None"); else channels.push("Motor " + i); } for (var i = 1; i <= 16; i++) { if (i == 0) channels.push("None"); else channels.push("Servo " + i); } return channels; } function appendLog(message){ SITL_LOG += message; var $sitlLog = $('#sitlLog'); if ($sitlLog && $sitlLog.length == 1) { $sitlLog.val(SITL_LOG); $sitlLog.animate({scrollTop: $sitlLog[0].scrollHeight - $sitlLog.height()}, "fast"); } } GUI.content_ready(callback); }); }; TABS.sitl.cleanup = (callback) => { SitlSerialPortUtils.stopPollSerialPorts(); if (callback) callback(); };