1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-13 11:29:53 +03:00

SITL integration

This commit is contained in:
Andi Kanzler 2023-03-08 12:43:14 -03:00
parent 5a6c2d7040
commit 069d0f2019
26 changed files with 6597 additions and 5710 deletions

View file

@ -139,7 +139,123 @@
"tabAuxiliary": {
"message": "Modes"
},
"tabSitl": {
"message" : "SITL"
},
"sitlDemoMode": {
"message": "Demo Mode"
},
"sitlResetDemoModeData": {
"message": "Reset Demo Mode"
},
"sitlOSNotSupported": {
"message": "SITL is not supported on this operating system."
},
"sitlOptions": {
"message": "SITL Options"
},
"sitlEnableSim": {
"message": "Enable simulator"
},
"sitlSimulator": {
"message": "Simulator"
},
"sitlUseImu": {
"message": "Use IMU"
},
"sitlSimIP": {
"message" : "Simulator IP"
},
"sitlPort": {
"message" : "Simulator Port"
},
"sitlChannelMap": {
"message": "Channel Mapping"
},
"sitlSimInput": {
"message": "Simulator Input"
},
"sitlInavOutput": {
"message": "INAV Output"
},
"sitlLog": {
"message": "Log"
},
"sitlStart": {
"message": "Start"
},
"sitlStop": {
"message": "Stop"
},
"sitlStopped": {
"message": "SITL stopped\n"
},
"sitlProfiles": {
"message": "SITL Profiles"
},
"sitlNew": {
"message": "New"
},
"sitlSave": {
"message": "Save"
},
"sitlDelete": {
"message": "Delete"
},
"sitlNewProfile": {
"message": "New SITL Profile"
},
"sitlEnterName": {
"message": "(Profile name)"
},
"sitlProfileExists": {
"message": "A profile with this name already exists."
},
"sitlStdProfileCantDeleted": {
"message": "SITL standard profile can't be deleted."
},
"sitlSerialToTCP": {
"message": "Serial to TCP (UART)"
},
"sitlSerialProtocoll": {
"message": "Preset for RX Protocoll"
},
"sitlSerialStopbits": {
"message": "Stopbits"
},
"sitlSerialPort": {
"message": "Serial port"
},
"sitlSerialTCPPort": {
"message": "TCP port"
},
"sitlSerialParity": {
"message": "Parity"
},
"sitlSerialTcpEnable": {
"message": "Enable"
},
"sitlHelp": {
"message": "SITL (Software in the loop) allows to run INAV completely in software on the PC without using a flight controller and simulate complete FPV flights. For this, INAV is compiled with a normal PC compiler. The sensors are replaced by data provided by a simulator.<br/>Currently supported are:<br/><ul><li><a href=\"https://www.realflight.com\" target=\"_blank\">RealFlight</a><br/></li><li><a href=\"https://www.x-plane.com\" target=\"_blank\">X-Plane</a></li></ul>"
},
"sitlProfilesHelp": {
"message": "Profiles are saved locally. The profiles contain not only all data of this tab, but also the configuration file (\"EEPROM\") of INAV itself."
},
"sitlEnableSimulatorHelp": {
"message": "If this option is deactivated, only the Configurator can be used. Useful to configure INAV without having to start the simulator."
},
"sitlUseImuHelp": {
"message": "Use IMU sensor data from the simulator instead of using attitude data from the simulator directly (Experimental, not recommended)."
},
"sitlIpHelp": {
"message": "IP address of the computer on which the simulator is running. If the simulator is running on the same computer, leave it at \"127.0.0.1"
},
"sitlPortHelp": {
"message": "Port number of the interface of the simulator. Note: The RealFlight port is fixed and cannot be changed."
},
"sitlSer2TcpHelp": {
"message": "Devices with a UART interface can be used with SITL via a serial to USB interface. Especially intended for serial receivers to use the full number of channels. "
},
"serialPortOpened": {
"message": "MSP connection <span style=\"color: #37a8db\">successfully</span> opened with ID: $1"
},

View file

@ -136,6 +136,7 @@ sources.js = [
'./js/waypoint.js',
'./node_modules/openlayers/dist/ol.js',
'./js/libraries/plotly-latest.min.js',
'./js/sitl.js',
];
sources.receiverCss = [
@ -252,6 +253,8 @@ gulp.task('dist-build', gulp.series('build', function() {
'./resources/models/*',
'./resources/osd/analogue/*.mcm',
'./resources/motor_order/*.svg',
'./resources/sitl/windows/*',
'./resources/sitl/linux/*'
];
return gulp.src(distSources, { base: '.' })
.pipe(gulp.dest(distDir));
@ -268,7 +271,8 @@ gulp.task('apps', gulp.series('dist', function(done) {
flavor: 'normal',
macIcns: './images/inav.icns',
winIco: './images/inav.ico',
version: get_nw_version()
version: get_nw_version(),
zip: false
});
builder.on('log', console.log);
builder.build(function (err) {

View file

@ -1,12 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 141.7 141.7" enable-background="new 0 0 141.7 141.7" xml:space="preserve">
width="283.46px" height="141.73px" viewBox="0 0 283.46 141.73" enable-background="new 0 0 283.46 141.73" xml:space="preserve">
<g>
<path fill="#FFFFFF" d="M137.9,36.3c-0.1-1.2-0.9-2.2-2-2.6c-1.1-0.4-2.4-0.1-3.2,0.7l-17.4,17.4l-19.3-6.1l-6.1-19.3l17.4-17.4
c0.9-0.9,1.1-2.1,0.7-3.2c-0.4-1.1-1.4-1.9-2.6-2C94.7,2.7,84.3,6.5,76.8,14c-10.3,10.3-13,25.3-8.2,38c-0.5,0.4-1,0.9-1.6,1.4
L9,108.3c0,0,0,0-0.1,0.1c-6.8,6.8-6.8,18,0,24.8c6.8,6.8,17.9,6.8,24.7-0.1c0,0,0.1-0.1,0.1-0.1l54.4-58.6c0.5-0.5,1-1,1.4-1.6
c12.8,4.8,27.8,2.1,38-8.2C135,57.2,138.8,46.9,137.9,36.3z M24.9,125.8c-2.5,2.5-6.6,2.5-9.1,0c-2.5-2.5-2.5-6.6,0-9.1
c2.5-2.5,6.6-2.5,9.1,0C27.4,119.2,27.4,123.3,24.9,125.8z"/>
<circle fill="#999999" cx="141.73" cy="100.25" r="5.271"/>
<path fill="#999999" d="M160.347,114.468h-37.234c0.025,7.52-4.19,12.583-9.941,16.749h57.114
C163.099,126.682,160.447,120.906,160.347,114.468z"/>
<path fill="#999999" d="M213.125,6H70.336c-2.261,0-4.104,1.843-4.104,4.104v94.594c0,2.262,1.843,4.104,4.104,4.104h142.789
c2.262,0,4.104-1.844,4.104-4.104V10.104C217.228,7.843,215.386,6,213.125,6z M141.73,105.521c-2.912,0-5.271-2.359-5.271-5.271
s2.359-5.271,5.271-5.271s5.272,2.359,5.272,5.271S144.642,105.521,141.73,105.521z M208.515,88.036
c0,1.99-1.633,3.624-3.623,3.624H78.581c-1.99,0-3.625-1.634-3.625-3.624V17.354c-0.012-1.991,1.622-3.625,3.625-3.625h126.311
c2.002,0,3.623,1.634,3.623,3.625V88.036z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 934 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141.7px" height="141.7px" viewBox="0 0 141.7 141.7" enable-background="new 0 0 141.7 141.7" xml:space="preserve">
<g>
<circle fill="#9A9999" cx="70.85" cy="102.911" r="4.754"/>
<path fill="#9A9999" d="M87.639,115.732H54.062c0.021,6.782-3.779,11.349-8.966,15.105h51.508
C90.122,126.748,87.729,121.538,87.639,115.732z"/>
<path fill="#9A9999" d="M135.235,17.913H6.465c-2.039,0-3.701,1.663-3.701,3.702v85.308c0,2.04,1.662,3.7,3.701,3.7h128.771
c2.039,0,3.701-1.662,3.701-3.7V21.615C138.937,19.576,137.274,17.913,135.235,17.913z M70.85,107.665
c-2.626,0-4.754-2.128-4.754-4.754c0-2.627,2.128-4.755,4.754-4.755c2.628,0,4.756,2.128,4.756,4.755
C75.605,105.537,73.478,107.665,70.85,107.665z M131.079,91.896c0,1.795-1.474,3.269-3.27,3.269H13.9
c-1.796,0-3.269-1.474-3.269-3.269V28.153c-0.011-1.795,1.462-3.269,3.269-3.269H127.81c1.807,0,3.27,1.474,3.27,3.269V91.896z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141.7px" height="141.7px" viewBox="0 0 141.7 141.7" enable-background="new 0 0 141.7 141.7" xml:space="preserve">
<g>
<circle fill="#FFFFFF" cx="70.85" cy="102.911" r="4.754"/>
<path fill="#FFFFFF" d="M87.639,115.732H54.062c0.021,6.782-3.779,11.349-8.966,15.105h51.508
C90.122,126.748,87.729,121.538,87.639,115.732z"/>
<path fill="#FFFFFF" d="M135.235,17.913H6.465c-2.039,0-3.701,1.663-3.701,3.702v85.308c0,2.04,1.662,3.7,3.701,3.7h128.771
c2.039,0,3.701-1.662,3.701-3.7V21.615C138.937,19.576,137.274,17.913,135.235,17.913z M70.85,107.665
c-2.626,0-4.754-2.128-4.754-4.754c0-2.627,2.128-4.755,4.754-4.755c2.628,0,4.756,2.128,4.756,4.755
C75.605,105.537,73.478,107.665,70.85,107.665z M131.079,91.896c0,1.795-1.474,3.269-3.27,3.269H13.9
c-1.796,0-3.269-1.474-3.269-3.269V28.153c-0.011-1.795,1.462-3.269,3.269-3.269H127.81c1.807,0,3.27,1.474,3.27,3.269V91.896z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

View file

@ -596,7 +596,8 @@ var FC = {
'NMEA',
'UBLOX',
'UBLOX7',
'MSP'
'MSP',
'FAKE'
];
},
getGpsBaudRates: function () {

View file

@ -14,6 +14,7 @@ var GUI_control = function () {
'landing',
'firmware_flasher',
'mission_control',
'sitl',
'help'
];
this.defaultAllowedTabsWhenConnected = [

View file

@ -66,7 +66,7 @@ PortHandler.check = function () {
chrome.storage.local.get('last_used_port', function (result) {
// if last_used_port was set, we try to select it
if (result.last_used_port) {
if (result.last_used_port == "ble" || result.last_used_port == "tcp" || result.last_used_port == "udp") {
if (result.last_used_port == "ble" || result.last_used_port == "tcp" || result.last_used_port == "udp" || result.last_used_port == "sitl" || result.last_used_port == "sitl-demo") {
$('#port').val(result.last_used_port);
} else {
current_ports.forEach(function(port) {
@ -181,6 +181,8 @@ PortHandler.update_port_select = function (ports) {
$('div#port-picker #port').append($("<option/>", {value: 'ble', text: 'BLE', data: {isBle: true}}));
$('div#port-picker #port').append($("<option/>", {value: 'tcp', text: 'TCP', data: {isTcp: true}}));
$('div#port-picker #port').append($("<option/>", {value: 'udp', text: 'UDP', data: {isUdp: true}}));
$('div#port-picker #port').append($("<option/>", {value: 'sitl', text: 'SITL', data: {isSitl: true}}));
$('div#port-picker #port').append($("<option/>", {value: 'sitl-demo', text: 'Demo mode', data: {isSitl: true}}));
};
PortHandler.port_detected = function(name, code, timeout, ignore_timeout) {

View file

@ -5,7 +5,8 @@ $(document).ready(function () {
var $port = $('#port'),
$baud = $('#baud'),
$portOverride = $('#port-override');
$portOverride = $('#port-override'),
isDemoRunning = false;
/*
* Handle "Wireless" mode with strict queueing of messages
@ -89,14 +90,14 @@ $(document).ready(function () {
$('#port-override-label').text("Port");
}
if (selected_port.data().isDFU || selected_port.data().isBle || selected_port.data().isTcp || selected_port.data().isUdp) {
if (selected_port.data().isDFU || selected_port.data().isBle || selected_port.data().isTcp || selected_port.data().isUdp || selected_port.data().isSitl) {
$baud.hide();
}
else {
$baud.show();
}
if (selected_port.data().isBle || selected_port.data().isTcp || selected_port.data().isUdp) {
if (selected_port.data().isBle || selected_port.data().isTcp || selected_port.data().isUdp || selected_port.data().isSitl) {
$('.tab_firmware_flasher').hide();
} else {
$('.tab_firmware_flasher').show();
@ -104,7 +105,7 @@ $(document).ready(function () {
var type = ConnectionType.Serial;
if (selected_port.data().isBle) {
type = ConnectionType.BLE;
} else if (selected_port.data().isTcp) {
} else if (selected_port.data().isTcp || selected_port.data().isSitl) {
type = ConnectionType.TCP;
} else if (selected_port.data().isUdp) {
type = ConnectionType.UDP;
@ -150,10 +151,24 @@ $(document).ready(function () {
if (selected_port == 'tcp' || selected_port == 'udp') {
CONFIGURATOR.connection.connect($portOverride.val(), {}, onOpen);
} else if (selected_port == 'sitl') {
CONFIGURATOR.connection.connect("127.0.0.1:5760", {}, onOpen);
} else if (selected_port == 'sitl-demo') {
if (SITLProcess.isRunning) {
SITLProcess.stop();
}
SITLProcess.start("demo.bin");
this.isDemoRunning = true;
CONFIGURATOR.connection.connect("127.0.0.1:5760", {}, onOpen);
} else {
CONFIGURATOR.connection.connect(selected_port, {bitrate: selected_baud}, onOpen);
}
} else {
if (this.isDemoRunning) {
SITLProcess.stop();
this.isDemoRunning = false;
}
var wasConnected = CONFIGURATOR.connectionValid;
helper.timeout.killAll();

270
js/sitl.js Normal file
View file

@ -0,0 +1,270 @@
'use strict'
const { spawn } = require('node:child_process');
const pathMod = require('path');
const { chmod, rm } = require('node:fs');
const serialRXProtocolls = [
{
name : "SBus",
baudrate: 100000,
stopBits: "Two",
parity: "Even"
},
{
name : "SBus Fast",
baudrate: 200000,
stopBits: "Two",
parity: "Even"
},
{
name : "Crossfire/Ghost",
baudrate: 420000,
stopBits: "One",
parity: "None"
},
{
name : "FPort/IBus/Spektrum/SRXL2/SUMD",
baudrate: 115200,
stopBits: "One",
parity: "None"
},
{
name : "JETI EX Bus",
baudrate: 125000,
stopBits: "One",
parity: "None"
},
];
var Ser2TCP = {
isRunning: false,
process: null,
portsList: [],
stopPolling: false,
getProtocolls: function() {
return serialRXProtocolls;
},
start: function(comPort, serialPortOptions, ipAddress, tcpPort, callback) {
if (this.isRunning)
this.stop();
var path;
if (GUI.operating_system == 'Windows') {
path = './resources/sitl/windows/Ser2TCP.exe'
} else if (GUI.operating_system == 'Linux') {
path = './resources/sitl/linux/ser2TCP'
chmod(path, 0o755, (err) => {
if (err)
console.log(err);
});
} else {
return;
}
var protocoll = serialRXProtocolls.find(proto => {
return proto.name == serialPortOptions.protocollName;
});
var args = [];
if (protocoll && protocoll.name != "manual") {
args.push(`--comport=${comPort}`)
args.push(`--baudrate=${protocoll.baudrate}`);
args.push(`--stopbits=${protocoll.stopBits}`)
args.push(`--parity=${protocoll.parity}`)
args.push(`--ip=${ipAddress}`);
args.push(`--tcpport=${tcpPort}`);
} else {
args.push(`--comport=${comPort}`)
args.push(`--baudrate${proserialPortOptionstocoll.baudrate}`);
args.push(`--stopbits=${protserialPortOptionsocoll.stopBits}`)
args.push(`--parity=${serialPortOptions.parity}`)
args.push(`--ip=${ipAddress}`);
args.push(`--tcpport=${tcpPort}`);
}
var opts = undefined;
if (GUI.operating_system == 'Linux')
opts = { useShell: true };
this.process = spawn(path, args, opts);
this.isRunning = true;
this.process.stdout.on('data', (data) => {
if (callback)
callback(data);
});
this.process.stderr.on('data', (data) => {
if (callback)
callback(data);
});
this.process.on('error', (error) => {
if (callback)
callback(error);
this.isRunning = false;
});
this.process.on('exit', () => {
if (this.isRunning)
this.spawn(path, args, callback);
});
},
stop: function() {
if (this.isRunning) {
this.isRunning = false;
this.process.kill();
}
},
getDevices: function(callback) {
chrome.serial.getDevices((devices_array) => {
var devices = [];
devices_array.forEach((device) => {
if (GUI.operating_system == 'Windows') {
var m = device.path.match(/COM\d?\d/g)
if (m)
devices.push(m[0]);
} else
devices.push(device.displayName);
});
callback(devices);
});
},
pollSerialPorts: function(callback) {
this.getDevices(devices => {
if (!this.arraysEqual(this.portsList, devices)) {
this.portsList = devices;
if (callback)
callback(this.portsList);
}
});
if (!this.stopPolling) {
setTimeout(() => { this.pollSerialPorts(callback) }, 250);
} else {
this.stopPolling = false;
}
},
resetPortsList: function() {
this.portsList = [];
},
stopPollSerialPorts: function()
{
this.stopPolling = true;
},
arraysEqual: function(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
}
var SITLProcess = {
spawn : null,
isRunning: false,
process: null,
deleteEepromFile(filename) {
rm(`${nw.App.dataPath}/${filename}`, error => {
if (error) {
GUI.log(`Unable to reset Demo mode: ${error.message}`);
}
});
},
start: function(eepromFileName, sim, useIMU, simIp, simPort, channelMap, callback) {
if (this.isRunning)
this.stop();
var sitlExePath, eepromPath;
if (GUI.operating_system == 'Windows') {
sitlExePath = './resources/sitl/windows/inav_SITL.exe'
eepromPath = `${nw.App.dataPath}\\${eepromFileName}`
} else if (GUI.operating_system == 'Linux') {
sitlExePath = './resources/sitl/linux/inav_SITL';
eepromPath = `${nw.App.dataPath}/${eepromFileName}`
chmod(sitlExePath, 0o755, err => {
if (err)
console.log(err);
});
} else {
return;
}
var args = [];
args.push(`--path=${eepromPath}`);
if (sim) {
args.push(`--sim=${sim}`);
if (useIMU)
args.push("--useimu")
if (simIp)
args.push(`--simip=${simIp}`);
if (simPort)
args.push(`--simport=${simPort}`);
if (channelMap)
args.push(`--chanmap=${channelMap}`)
}
this.spawn(sitlExePath, args, callback);
},
spawn: function(path, args, callback) {
var opts = undefined;
if (GUI.operating_system == 'Linux')
opts = { useShell: true };
this.process = spawn(path, args, opts);
this.isRunning = true;
this.process.stdout.on('data', (data) => {
if (callback)
callback(data);
});
this.process.stderr.on('data', (data) => {
if (callback)
callback(data);
});
this.process.on('error', (error) => {
if (callback)
callback(error);
this.isRunning = false;
});
this.process.on('exit', () => {
if (this.isRunning)
this.spawn(path, args, callback);
});
},
stop: function() {
if (this.isRunning) {
this.isRunning = false;
this.process.kill();
}
}
};

View file

@ -885,6 +885,18 @@ li.active .ic_flasher {
background-image: url("../images/icons/cf_icon_flasher_white.svg");
}
.ic_sitl {
background-image: url("../images/icons/cf_icon_sitl_grey.svg");
}
.ic_sitl:hover {
background-image: url("../images/icons/cf_icon_sitl_white.svg");
}
li.active .ic_sitl {
background-image: url("../images/icons/cf_icon_sitl_white.svg");
}
.ic_calibration {
background-image: url(../images/icons/cf_icon_cal_grey.svg);
}

View file

@ -178,6 +178,9 @@
<li class="tab_firmware_flasher">
<a href="#" data-i18n="tabFirmwareFlasher" class="tabicon ic_flasher" title="Firmware Flasher"></a>
</li>
<li class="tab_sitl">
<a href="#" data-i18n="tabSitl" class="tabicon ic_sitl" title="SITL"></a>
</li>
</ul>
<ul class="mode-connected">
<li class="tab_setup">

View file

@ -223,6 +223,9 @@ $(document).ready(function () {
case 'firmware_flasher':
TABS.firmware_flasher.initialize(content_ready);
break;
case 'sitl':
TABS.sitl.initialize(content_ready);
break;
case 'auxiliary':
TABS.auxiliary.initialize(content_ready);
break;
@ -412,6 +415,9 @@ $(document).ready(function () {
});
globalSettings.proxyLayer = $(this).val();
});
$('#demoModeReset').on('click', () => {
SITLProcess.deleteEepromFile('demo.bin');
});
function close_and_cleanup(e) {
if (e.type == 'click' && !$.contains($('div#options-window')[0], e.target) || e.type == 'keyup' && e.keyCode == 27) {
$(document).unbind('click keyup', close_and_cleanup);

10971
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@
"jquery-ui-npm": "1.12.0",
"marked": "^0.3.17",
"minimist": "^1.2.0",
"nw": "^0.61.0",
"nw": "^0.61.0-sdk",
"nw-dialog": "^1.0.7",
"openlayers": "^4.6.5",
"plotly": "^1.0.6",
@ -45,12 +45,12 @@
"xml2js": "^0.4.19"
},
"devDependencies": {
"@quanle94/innosetup": "^6.0.2",
"gulp-debian": "^0.1.9",
"gulp-rename": "^2.0.0",
"nw-builder": "^3.5.7",
"rpm-builder": "^1.2.1",
"semver": "6.3.0",
"@quanle94/innosetup": "^6.0.2"
"semver": "6.3.0"
},
"optionalDependencies": {
"appdmg": "^0.6.2"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

86
src/css/tabs/sitl.css Normal file
View file

@ -0,0 +1,86 @@
.tab-sitl input {
width: 111px;
padding-left: 3px;
height: 18px;
line-height: 20px;
text-align: left;
border: 1px solid silver;
border-radius: 3px;
font-size: 11px;
font-weight: normal;
}
.tab-sitl .simIP span {
margin-left: 10px;
}
.map-table td, .map-table th {
padding: 2px;
height: 2.5em;
text-align: center;
}
.tab-sitl .switchery {
margin-right: 68px !important;
}
.map-table thead tr {
border-left: 1px solid #e4e4e4;
border-right: 1px solid #e4e4e4;
background-color: #828885;
color: #FFF;
}
.map-table tr:nth-child(even) td,
.map-table tr:nth-child(even) th {
background-color: #ebe7e7;
}
.sitlNumber {
margin-left: 14px;
}
.map-table {
width: 300px;
}
.tab-sitl .leftWrapper {
float: left;
width: calc(50% - 20px);
}
.tab-sitl .rightWrapper {
float: left;
width: calc(50% - 0px);
margin: 0 0 10px 20px;
}
.sitlProfile_box {
padding: 10px;
margin-bottom: 3px;
float: left;
}
.sitlProfile_select {
float:left;
padding-top: 2px;
padding-right: 10px
}
.default_btn.sitlnarrow {
width: auto;
margin-bottom: 0;
padding-left: 10px;
}
.default_btn.sitlnarrow a {
padding: 5px;
}
#sitlLog {
resize: none;
width: 100%;
height: 100%;
box-sizing: border-box
}

View file

@ -67,5 +67,15 @@
</div>
</div>
</div>
<div class="options-section gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="sitlDemoMode"></div>
</div>
<div class="spacer_box settings">
<div class="default_btn" style="float: none; width: 200px;">
<a id="demoModeReset" href="#" i18n="sitlResetDemoModeData"></a>
</div>
</div>
</div>
</div>
</div>

157
tabs/sitl.html Normal file
View file

@ -0,0 +1,157 @@
<div class="tab-sitl toolbar_fixed_bottom">
<div class="content_wrapper">
<div class="tab_title" data-i18n="tabSitl">SITL</div>
<div class="note" style="margin-bottom: 20px;">
<div class="note_spacer">
<p data-i18n="sitlHelp"></p>
</div>
</div>
<div class="leftWrapper">
<div class="gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="sitlProfiles"></div>
<div class="helpicon cf_tip" data-i18n_title="sitlProfilesHelp"></div>
</div>
<div class="sitlProfile_box">
<div class="sitlProfile_select">
<select id="sitlProfile"></select>
</div>
<div class="default_btn sitlnarrow">
<a id="sitlProfileNew" href="#" i18n="sitlNew"></a>
</div>
<div class="default_btn sitlnarrow">
<a id="sitlProfileSave" href="#" i18n="sitlSave"></a>
</div>
<div class="default_btn sitlnarrow">
<a id="sitlProfileDelete" href="#" i18n="sitlDelete"></a>
</div>
</div>
</div>
<div class="config-section gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="sitlOptions"></div>
</div>
<div class="spacer_box">
<div class="checkbox">
<input type="checkbox" id="sitlEnableSim" class="toggle" data-live="true">
<label for="enableSim"><span data-i18n="sitlEnableSim"></span></label>
<div class="helpicon cf_tip" data-i18n_title="sitlEnableSimulatorHelp"></div>
</div>
<div class="select">
<select id="simulator"></select>
<label for="simulator"> <span data-i18n="sitlSimulator"></span></label>
</div>
<div class="number">
<input type="text" id="simIP" data-setting="simip" data-live="true" />
<label for="simIP" class="sitlNumber"><span data-i18n="sitlSimIP"></span></label>
<div class="helpicon cf_tip" data-i18n_title="sitlIpHelp"></div>
</div>
<div class="number">
<input type="number" id="simPort" data-setting-multiplier="1" step="1" min="1" max="64500" />
<label for="sitlPort" class="sitlNumber"><span data-i18n="sitlPort"></span></label>
<div class="helpicon cf_tip" data-i18n_title="sitlPortHelp"></div>
</div>
<div class="checkbox">
<input type="checkbox" id="sitlUseImu" class="sitlUseImu toggle" data-live="true">
<label for="sitlUseImu"><span data-i18n="sitlUseImu"></span></label>
<div class="helpicon cf_tip" data-i18n_title="sitlUseImuHelp"></div>
</div>
</div>
</div>
<div class="gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="sitlLog"></div>
</div>
<div class="spacer_box">
<textarea readonly id="sitlLog" rows="25"></textarea>
</div>
</div>
</div>
<div class="rightWrapper">
<div class="gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="sitlChannelMap"></div>
</div>
<div class="spacer_box">
<table id="channelMap-table" class="map-table">
<thead>
<tr>
<th style="width: 100px" data-i18n="sitlSimInput"></th>
<th style="width: 100px" data-i18n="sitlInavOutput"></th>
</tr>
</thead>
<tbody class="mapTableBody">
</tbody>
</table>
</div>
</div>
<div class="config-section gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="sitlSerialToTCP"></div>
<div class="helpicon cf_tip" data-i18n_title="sitlSer2TcpHelp"></div>
</div>
<div class="spacer_box">
<div class="checkbox">
<input type="checkbox" id="serialTcpEnable" class="toggle" data-live="true">
<label for="serialTcpEnable"><span data-i18n="sitlSerialTcpEnable"></span></label>
</div>
<span id="serialTcpOptions">
<div class="select">
<select id="sitlSerialPort"></select>
<label for="sitlSerialPort"> <span data-i18n="sitlSerialPort"></span></label>
</div>
<div class="select">
<select id="serialTCPPort">
<option value="5760">5760 (UART1)</option>
<option value="5761">5761 (UART2)</option>
<option value="5762">5762 (UART3)</option>
<option value="5763">5763 (UART4)</option>
<option value="5764">5764 (UART5)</option>
<option value="5765">5765 (UART6)</option>
<option value="5766">5766 (UART7)</option>
<option value="5767">5767 (UART8)</option>
</select>
<label for="serialTCPPort"> <span data-i18n="sitlSerialTCPPort"></span></label>
</div>
<div class="select">
<select id="serialProtocoll"></select>
<label for="serialProtocoll"> <span data-i18n="sitlSerialProtocoll"></span></label>
</div>
<div class="number">
<input type="number" id="sitlBaud" class="sitlBaud" data-setting-multiplier="1" step="1" min="1" max="256000" />
<label for="sitlBaud" class="sitlNumber"><span data-i18n="configurationGPSBaudrate"></span></label>
</div>
<div class="select">
<select id="serialStopbits">
<option value="None">0</option>
<option value="One">1</option>
<option value="OnePointFive">1.5</option>
<option value="Two">2</option>
</select>
<label for="serialStopbits"> <span data-i18n="sitlSerialStopbits"></span></label>
</div>
<div class="select">
<select id="serialParity">
<option value="None">None</option>
<option value="Even">Even</option>
<option value="Mark">Mark</option>
<option value="Odd">Odd</option>
<option value="Space">Space</option>
</select>
<label for="serialParity"> <span data-i18n="sitlSerialParity"></span></label>
</div>
</span>
</div>
</div>
</div>
</div>
<div class="content_toolbar" style="position: unset;">
<div class="btn">
<a class="sitlStop disabled" href="#" i18n="sitlStop"></a>
</div>
<div class="btn">
<a class="sitlStart" href="#" i18n="sitlStart"></a>
</div>
</div>
</div>

521
tabs/sitl.js Normal file
View file

@ -0,0 +1,521 @@
'use strict'
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] X-Plane",
sim: "X-Plane",
eepromFileName: "standard-x-plane.bin",
isStdProfile: true,
simEnabeld: true,
port: 49001,
ip: "127.0.0.1",
useImu: false,
channelMap: [ 1, 15, 13, 16],
useSerialTcp: true,
comPort: "",
tcpPort: 5762,
serialProtocol: "SBus",
baudrate: false,
stopbits: false,
parity: false
},
{
name: "[Standard] RealFlight Flying Wing",
sim: "RealFlight",
eepromFileName: "standard-realflight.bin",
isStdProfile: true,
simEnabeld: true,
port: 49001,
ip: "127.0.0.1",
useImu: false,
channelMap: [ 1, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0],
useSerialTcp: true,
comPort: "",
tcpPort: 5762,
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';
googleAnalytics.sendAppView('SITL');
}
if (GUI.active_tab != 'sitl') {
GUI.active_tab = 'sitl';
googleAnalytics.sendAppView('SITL');
}
GUI.load("./tabs/sitl.html", function () {
localize();
var os = GUI.operating_system;
if (os != 'Windows' && os != 'Linux') {
$('.content_wrapper').find('*').remove();
$('.content_wrapper').append(`<h2>${chrome.i18n.getMessage('sitlOSNotSupported')}</h2>`);
GUI.content_ready(callback);
return;
}
var currentSim, currentProfile, profiles;
var mapping = new Array(28).fill(0);
var serialProtocolls = Ser2TCP.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 serialTcpEnable = $('#serialTcpEnable')
var tcpPort_e = $('#serialTCPPort');
var protocollPreset_e = $('#serialProtocoll');
var baudRate_e = $('#sitlBaud');
var stopBits_e = $('#serialStopbits');
var parity_e = $('#serialParity');
var serialTcpEable_e = $('#serialTcpEnable');
if (SITLProcess.isRunning) {
$('.sitlStart').addClass('disabled');
$('.sitlStop').removeClass('disabled');
} else {
$('.sitlStop').addClass('disabled');
$('.sitlStart').removeClass('disabled');
}
$('#sitlLog').val(SITL_LOG);
$('#sitlLog').animate({scrollTop: $('#sitlLog').scrollHeight}, "fast");
profiles = stdProfiles.slice(0);
chrome.storage.local.get('sitlProfiles', (result) => {
if(result.sitlProfiles)
profiles.push(...result.sitlProfiles);
initElements(true);
});
Ser2TCP.resetPortsList();
Ser2TCP.pollSerialPorts(ports => {
serialPorts_e.find('*').remove();
ports.forEach(port => {
serialPorts_e.append(`<option value="${port}">${port}</option>`)
});
});
enableSim_e.on('change', () => {
currentProfile.simEnabeld = enableSim_e.is(':checked');
});
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;
var selectedProtocoll = protocollPreset_e.find(':selected').val();
if (selectedProtocoll == "manual") {
serialOptions = {
protocollName: "manual",
bitrate: currentProfile.baudrate,
stopBits: currentProfile.stopBits,
parityBit: currentProfile.parity
}
} else {;
serialOptions = {
protocollName: selectedProtocoll
}
}
SITLProcess.start(currentProfile.eepromFileName, sim, useImu_e.is(':checked'), simIp, simPort, channelMap, result => {
appendLog(result);
if (serialTcpEnable.is(':checked') && result == `[SIM] Connection with ${currentProfile.sim} successfully established.\n`) {
Ser2TCP.start(serialPorts_e.val(), serialOptions, currentProfile.ip, currentProfile.tcpPort, result => {
appendLog(`[Serial2TCP] ${result}`);
});
}
});
});
$('.sitlStop').on('click', ()=> {
$('.sitlStop').addClass('disabled');
$('.sitlStart').removeClass('disabled');
Ser2TCP.stop();
SITLProcess.stop();
appendLog(chrome.i18n.getMessage('sitlStopped'));
});
profileSaveBtn_e.on('click', () => {
saveProfiles();
});
profileNewBtn_e.on('click', () => {
var name = prompt(chrome.i18n.getMessage('sitlNewProfile'), chrome.i18n.getMessage('sitlEnterName'));
if (!name)
return;
if (profiles.find(e => { return e.name == name })) {
alert(chrome.i18n.getMessage('sitlProfileExists'))
return;
}
var eerpromName = name.replace(/[^a-z0-9]/gi, '_').toLowerCase() + ".bin";
var profile = {
name: name,
sim: "RealFlight",
isStdProfile: false,
simEnabeld: true,
eepromFileName: eerpromName,
port: 49001,
ip: "127.0.0.1",
useImu: false,
channelMap: [ 1, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0],
useSerialTcp: true,
tcpPort: 5762,
serialProtocol: "SBus",
baudrate: false,
stopbits: false,
parity: false
}
profiles.push(profile);
profiles_e.append(`<option value="${name}">${name}</option>`)
profiles_e.val(name);
updateCurrentProfile();
saveProfiles();
});
profileDeleteBtn_e.on('click', () => {
if (currentProfile.isStdProfile) {
alert(chrome.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();
});
serialTcpEable_e.on('change', () => {
currentProfile.useSerialTcp = serialTcpEable_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);
} else {
baudRate_e.prop('disabled', false);
baudRate_e.val('');
stopBits_e.prop('disabled', false);
stopBits_e.val('');
parity_e.prop('disabled', false);
parity_e.val('');
}
currentProfile.serialProtocol = selectedProtocoll;
});
serialPorts_e.on('change', () => {
currentProfile.comPort = serialPorts_e.val();
});
tcpPort_e.on('change', () => {
currentProfile.tcpPort = parseInt(tcpPort_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(`<option value="${profile.name}">${profile.name}</option>`)
});
if (init) {
simulators.forEach(simulator => {
sim_e.append(`<option value="${simulator.name}">${simulator.name}</option>`)
});
protocollPreset_e.append('<option value="manual">Manual</option>');
serialProtocolls.forEach(protocoll => {
protocollPreset_e.append(`<option value="${protocoll.name}">${protocoll.name}</option>`);
});
chrome.storage.local.get('sitlLastProfile', (result) => {
if (result.sitlLastProfile) {
var element = profiles.find(profile => {
return profile.name == result.sitlLastProfile;
});
if (element)
profiles_e.val(element.name).trigger('change');
}
});
}
updateCurrentProfile();
}
function saveProfiles() {
var profilesToSave = [];
profiles.forEach(profile => {
if (!profile.isStdProfile)
profilesToSave.push(profile);
});
chrome.storage.local.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);
}
port_e.prop('disabled', simulator.isPortFixed);
renderChanMapTable();
return;
}
});
}
function updateCurrentProfile() {
var selected = profiles_e.find(':selected').val();
var selectedIndex = profiles.findIndex(element => {
return element.name == selected;
});
currentProfile = profiles[selectedIndex];
if (currentProfile.isStdProfile) {
currentProfile = structuredClone(stdProfiles.find((profile) => {
return profile.sim == currentProfile.sim;
}));
}
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);
} 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);
}
if (currentProfile.comPort != "")
serialPorts_e.val(currentProfile.comPort);
enableSim_e.prop('checked', currentProfile.simEnabeld).trigger('change');
serialTcpEable_e.prop('checked', currentProfile.useSerialTcp).trigger('change');
tcpPort_e.val(currentProfile.tcpPort);
mapping = currentProfile.channelMap;
sim_e.val(currentProfile.sim);
updateSim();
simIp_e.val(currentProfile.ip).trigger('change');
useImu_e.prop('checked', currentProfile.useImu).trigger('change');
chrome.storage.local.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("<tr><td>" + output + "</td><td><select data-out=\"" + i + "\" class=\"inavChannel\"\"></select></td></td>");
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());
chrome.storage.local.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;
$('#sitlLog').val(SITL_LOG);
$('#sitlLog').animate({scrollTop: $('#sitlLog')[0].scrollHeight}, "fast");
}
GUI.content_ready(callback);
});
};
TABS.sitl.cleanup = (callback) => {
Ser2TCP.stopPollSerialPorts();
if (callback)
callback();
};