mirror of
https://github.com/iNavFlight/inav-configurator.git
synced 2025-07-23 16:25:19 +03:00
Tab loading was relying on replacing the contents of '#content' with the loading indicator, then replacing it with the loading tab content and blocking rendering until the tab was ready by not yielding. This is problematic for tabs that load some data asynchronously, like PID and OSD. Instead, put the loading indicator in front of everything else and load new content inside '#content' next to the loading indicator (but without showing it). Once the content and data are fully loaded we fade out the loading indicator with a 0.4s long animation and then we remove. This works for both synchronous and asynchonous loading of tabs.
428 lines
15 KiB
JavaScript
428 lines
15 KiB
JavaScript
/*global $,helper,mspHelper,MSP,GUI,SERVO_RULES,MOTOR_RULES,MIXER_CONFIG,googleAnalytics,LOGIC_CONDITIONS,TABS,ServoMixRule*/
|
|
'use strict';
|
|
|
|
TABS.mixer = {};
|
|
|
|
TABS.mixer.initialize = function (callback, scrollPosition) {
|
|
|
|
let loadChainer = new MSPChainerClass(),
|
|
saveChainer = new MSPChainerClass(),
|
|
currentPlatform,
|
|
currentMixerPreset,
|
|
$servoMixTable,
|
|
$servoMixTableBody,
|
|
$motorMixTable,
|
|
$motorMixTableBody;
|
|
|
|
if (GUI.active_tab != 'mixer') {
|
|
GUI.active_tab = 'mixer';
|
|
googleAnalytics.sendAppView('Mixer');
|
|
}
|
|
|
|
loadChainer.setChain([
|
|
mspHelper.loadMixerConfig,
|
|
mspHelper.loadMotors,
|
|
mspHelper.loadServoMixRules,
|
|
mspHelper.loadMotorMixRules,
|
|
mspHelper.loadOutputMapping,
|
|
mspHelper.loadLogicConditions
|
|
]);
|
|
loadChainer.setExitPoint(loadHtml);
|
|
loadChainer.execute();
|
|
|
|
saveChainer.setChain([
|
|
mspHelper.saveMixerConfig,
|
|
mspHelper.sendServoMixer,
|
|
mspHelper.sendMotorMixer,
|
|
mspHelper.saveToEeprom
|
|
]);
|
|
saveChainer.setExitPoint(reboot);
|
|
|
|
function reboot() {
|
|
//noinspection JSUnresolvedVariable
|
|
GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
|
|
|
|
GUI.tab_switch_cleanup(function() {
|
|
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitialize);
|
|
});
|
|
}
|
|
|
|
function reinitialize() {
|
|
//noinspection JSUnresolvedVariable
|
|
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
|
|
GUI.handleReconnect($('.tab_mixer a'));
|
|
}
|
|
|
|
function loadHtml() {
|
|
GUI.load("./tabs/mixer.html", processHtml);
|
|
}
|
|
|
|
function renderOutputTable() {
|
|
let outputCount = OUTPUT_MAPPING.getOutputCount(),
|
|
$outputRow = $('#output-row'),
|
|
$functionRow = $('#function-row');
|
|
|
|
$outputRow.append('<th data-i18n="mappingTableOutput"></th>');
|
|
$functionRow.append('<th data-i18n="mappingTableFunction"></th>');
|
|
|
|
for (let i = 1; i <= outputCount; i++) {
|
|
$outputRow.append('<td>S' + i + '</td>');
|
|
$functionRow.append('<td id="function-' + i +'">-</td>');
|
|
}
|
|
|
|
$outputRow.find('td').css('width', 100 / (outputCount + 1) + '%');
|
|
|
|
}
|
|
|
|
function renderOutputMapping() {
|
|
let outputMap = OUTPUT_MAPPING.getOutputTable(
|
|
MIXER_CONFIG.platformType == PLATFORM_MULTIROTOR || MIXER_CONFIG.platformType == PLATFORM_TRICOPTER,
|
|
MOTOR_RULES.getNumberOfConfiguredMotors(),
|
|
SERVO_RULES.getUsedServoIndexes()
|
|
);
|
|
|
|
for (let i = 1; i <= OUTPUT_MAPPING.getOutputCount(); i++) {
|
|
$('#function-' + i).html(outputMap[i - 1]);
|
|
}
|
|
}
|
|
|
|
function renderServoMixRules() {
|
|
/*
|
|
* Process servo mix table UI
|
|
*/
|
|
let rules = SERVO_RULES.get();
|
|
$servoMixTableBody.find("*").remove();
|
|
for (let servoRuleIndex in rules) {
|
|
if (rules.hasOwnProperty(servoRuleIndex)) {
|
|
const servoRule = rules[servoRuleIndex];
|
|
|
|
$servoMixTableBody.append('\
|
|
<tr>\
|
|
<td><input type="number" class="mix-rule-servo" step="1" min="0" max="15" /></td>\
|
|
<td><select class="mix-rule-input"></select></td>\
|
|
<td class="mixer-fixed-value-col"><input type="number" class="mix-rule-fixed-value" min="875" max="2125" disabled /></td> \
|
|
<td><input type="number" class="mix-rule-rate" step="1" min="-125" max="125" /></td>\
|
|
<td><input type="number" class="mix-rule-speed" step="1" min="0" max="255" /></td>\
|
|
<td><select class="mix-rule-condition"></td>\
|
|
<td><span class="btn default_btn narrow red"><a href="#" data-role="role-servo-delete" data-i18n="servoMixerDelete"></a></span></td>\
|
|
</tr>\
|
|
');
|
|
|
|
const $row = $servoMixTableBody.find('tr:last');
|
|
const $conditions = $row.find('.mix-rule-condition');
|
|
|
|
$conditions.append('<option value="-1">Always</option>')
|
|
for (let i = 0; i < 16 ; i++) {
|
|
$conditions.append('<option value="' + i + '">Logic Condition ' + i + ' </option>');
|
|
}
|
|
$conditions.val(servoRule.getConditionId()).change(function () {
|
|
servoRule.setConditionId($(this).val());
|
|
});
|
|
|
|
GUI.fillSelect($row.find(".mix-rule-input"), FC.getServoMixInputNames(), servoRule.getInput());
|
|
|
|
$row.find(".mix-rule-input").val(servoRule.getInput()).change(function () {
|
|
servoRule.setInput($(this).val());
|
|
updateFixedValueVisibility($row, $(this));
|
|
});
|
|
|
|
$row.find(".mix-rule-servo").val(servoRule.getTarget()).change(function () {
|
|
servoRule.setTarget($(this).val());
|
|
});
|
|
|
|
$row.find(".mix-rule-rate").val(servoRule.getRate()).change(function () {
|
|
servoRule.setRate($(this).val());
|
|
$row.find(".mix-rule-fixed-value").val(mapServoWeightToFixedValue($(this).val()));
|
|
});
|
|
|
|
$row.find(".mix-rule-fixed-value").val(mapServoWeightToFixedValue($row.find(".mix-rule-rate").val()));
|
|
|
|
$row.find(".mix-rule-speed").val(servoRule.getSpeed()).change(function () {
|
|
servoRule.setSpeed($(this).val());
|
|
});
|
|
|
|
$row.find("[data-role='role-servo-delete']").attr("data-index", servoRuleIndex);
|
|
|
|
updateFixedValueVisibility($row, $row.find(".mix-rule-input"));
|
|
}
|
|
}
|
|
|
|
let rate_inputs = $('.mix-rule-rate');
|
|
rate_inputs.attr("min", -1000);
|
|
rate_inputs.attr("max", 1000);
|
|
|
|
localize();
|
|
}
|
|
|
|
function updateFixedValueVisibility(row, $mixRuleInput) {
|
|
|
|
// Show the fixed value input box if "ONE" input was selected for this servo
|
|
const $fixedValueCalcInput = row.find(".mix-rule-fixed-value");
|
|
if (FC.getServoMixInputNames()[$mixRuleInput.val()] === 'ONE') {
|
|
$fixedValueCalcInput.show();
|
|
row.find(".mix-rule-speed").prop('disabled', true);
|
|
} else {
|
|
$fixedValueCalcInput.hide();
|
|
row.find(".mix-rule-speed").prop('disabled', false);
|
|
}
|
|
|
|
// Show the Fixed Value column if at least one servo has the "ONE" input assigned
|
|
const $fixedValueCol = $("#servo-mix-table").find(".mixer-fixed-value-col");
|
|
const rules = SERVO_RULES.get();
|
|
for (let servoRuleIndex in rules) {
|
|
if (rules.hasOwnProperty(servoRuleIndex)) {
|
|
if (FC.getServoMixInputNames()[rules[servoRuleIndex].getInput()] === 'ONE') {
|
|
$fixedValueCol.show();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
$fixedValueCol.hide();
|
|
}
|
|
|
|
function mapServoWeightToFixedValue(weight) {
|
|
return (parseInt(weight) + 100) * 1000 / 200 + 1000;
|
|
}
|
|
|
|
function renderMotorMixRules() {
|
|
|
|
/*
|
|
* Process motor mix table UI
|
|
*/
|
|
var rules = MOTOR_RULES.get();
|
|
$motorMixTableBody.find("*").remove();
|
|
let index = 0;
|
|
for (const i in rules) {
|
|
if (rules.hasOwnProperty(i)) {
|
|
const rule = rules[i];
|
|
index++;
|
|
|
|
$motorMixTableBody.append('\
|
|
<tr>\
|
|
<td><span class="mix-rule-motor"></span></td>\
|
|
<td><input type="number" class="mix-rule-throttle" step="0.001" min="0" max="1" /></td>\
|
|
<td><input type="number" class="mix-rule-roll" step="0.001" min="-2" max="2" /></td>\
|
|
<td><input type="number" class="mix-rule-pitch" step="0.001" min="-2" max="2" /></td>\
|
|
<td><input type="number" class="mix-rule-yaw" step="0.001" min="-2" max="2" /></td>\
|
|
<td><span class="btn default_btn narrow red"><a href="#" data-role="role-motor-delete" data-i18n="servoMixerDelete"></a></span></td>\
|
|
</tr>\
|
|
');
|
|
|
|
const $row = $motorMixTableBody.find('tr:last');
|
|
|
|
$row.find('.mix-rule-motor').html(index);
|
|
$row.find('.mix-rule-throttle').val(rule.getThrottle()).change(function () {
|
|
rule.setThrottle($(this).val());
|
|
});
|
|
$row.find('.mix-rule-roll').val(rule.getRoll()).change(function () {
|
|
rule.setRoll($(this).val());
|
|
});
|
|
$row.find('.mix-rule-pitch').val(rule.getPitch()).change(function () {
|
|
rule.setPitch($(this).val());
|
|
});
|
|
$row.find('.mix-rule-yaw').val(rule.getYaw()).change(function () {
|
|
rule.setYaw($(this).val());
|
|
});
|
|
$row.find("[data-role='role-motor-delete']").attr("data-index", i);
|
|
}
|
|
|
|
}
|
|
localize();
|
|
}
|
|
|
|
function saveAndReboot() {
|
|
|
|
/*
|
|
* Send tracking
|
|
*/
|
|
googleAnalytics.sendEvent('Mixer', 'Platform type', helper.platform.getById(MIXER_CONFIG.platformType).name);
|
|
googleAnalytics.sendEvent('Mixer', 'Mixer preset', helper.mixer.getById(MIXER_CONFIG.appliedMixerPreset).name);
|
|
|
|
/*
|
|
* Send mixer rules
|
|
*/
|
|
SERVO_RULES.cleanup();
|
|
SERVO_RULES.inflate();
|
|
MOTOR_RULES.cleanup();
|
|
MOTOR_RULES.inflate();
|
|
saveChainer.execute();
|
|
}
|
|
|
|
function processHtml() {
|
|
|
|
$servoMixTable = $('#servo-mix-table');
|
|
$servoMixTableBody = $servoMixTable.find('tbody');
|
|
$motorMixTable = $('#motor-mix-table');
|
|
$motorMixTableBody = $motorMixTable.find('tbody');
|
|
|
|
function fillMixerPreset() {
|
|
let mixers = helper.mixer.getByPlatform(MIXER_CONFIG.platformType);
|
|
|
|
$mixerPreset.find("*").remove();
|
|
for (i in mixers) {
|
|
if (mixers.hasOwnProperty(i)) {
|
|
let m = mixers[i];
|
|
$mixerPreset.append('<option value="' + m.id + '">' + m.name + '</option>');
|
|
}
|
|
}
|
|
}
|
|
|
|
let $platformSelect = $('#platform-type'),
|
|
platforms = helper.platform.getList(),
|
|
$hasFlapsWrapper = $('#has-flaps-wrapper'),
|
|
$hasFlaps = $('#has-flaps'),
|
|
$mixerPreset = $('#mixer-preset'),
|
|
modal;
|
|
|
|
$platformSelect.find("*").remove();
|
|
|
|
for (let i in platforms) {
|
|
if (platforms.hasOwnProperty(i)) {
|
|
let p = platforms[i];
|
|
$platformSelect.append('<option value="' + p.id + '">' + p.name + '</option>');
|
|
}
|
|
}
|
|
|
|
$hasFlaps.prop("checked", MIXER_CONFIG.hasFlaps);
|
|
$hasFlaps.change(function () {
|
|
if ($(this).is(":checked")) {
|
|
MIXER_CONFIG.hasFlaps = 1;
|
|
} else {
|
|
MIXER_CONFIG.hasFlaps = 0;
|
|
}
|
|
});
|
|
$hasFlaps.change();
|
|
|
|
$platformSelect.change(function () {
|
|
MIXER_CONFIG.platformType = parseInt($platformSelect.val(), 10);
|
|
currentPlatform = helper.platform.getById(MIXER_CONFIG.platformType);
|
|
|
|
var $platformSelectParent = $platformSelect.parent('.select');
|
|
|
|
if (currentPlatform.flapsPossible) {
|
|
$hasFlapsWrapper.removeClass('is-hidden');
|
|
$platformSelectParent.removeClass('no-bottom-border');
|
|
} else {
|
|
$hasFlapsWrapper.addClass('is-hidden');
|
|
$platformSelectParent.addClass('no-bottom-border');
|
|
}
|
|
|
|
fillMixerPreset();
|
|
$mixerPreset.change();
|
|
});
|
|
|
|
currentPlatform = helper.platform.getById(MIXER_CONFIG.platformType);
|
|
$platformSelect.val(MIXER_CONFIG.platformType).change();
|
|
|
|
$mixerPreset.change(function () {
|
|
const presetId = parseInt($mixerPreset.val(), 10);
|
|
currentMixerPreset = helper.mixer.getById(presetId);
|
|
|
|
MIXER_CONFIG.appliedMixerPreset = presetId;
|
|
|
|
$('.mixerPreview img').attr('src', './resources/motor_order/'
|
|
+ currentMixerPreset.image + '.svg');
|
|
});
|
|
|
|
if (MIXER_CONFIG.appliedMixerPreset > -1) {
|
|
$mixerPreset.val(MIXER_CONFIG.appliedMixerPreset).change();
|
|
} else {
|
|
$mixerPreset.change();
|
|
}
|
|
|
|
modal = new jBox('Modal', {
|
|
width: 480,
|
|
height: 240,
|
|
closeButton: 'title',
|
|
animation: false,
|
|
attach: $('#load-and-apply-mixer-button'),
|
|
title: chrome.i18n.getMessage("mixerApplyModalTitle"),
|
|
content: $('#mixerApplyContent')
|
|
});
|
|
|
|
$('#execute-button').click(function () {
|
|
helper.mixer.loadServoRules(currentMixerPreset);
|
|
helper.mixer.loadMotorRules(currentMixerPreset);
|
|
renderServoMixRules();
|
|
renderMotorMixRules();
|
|
renderOutputMapping();
|
|
modal.close();
|
|
saveAndReboot();
|
|
});
|
|
|
|
$('#load-mixer-button').click(function () {
|
|
helper.mixer.loadServoRules(currentMixerPreset);
|
|
helper.mixer.loadMotorRules(currentMixerPreset);
|
|
renderServoMixRules();
|
|
renderMotorMixRules();
|
|
renderOutputMapping();
|
|
});
|
|
|
|
$servoMixTableBody.on('click', "[data-role='role-servo-delete']", function (event) {
|
|
SERVO_RULES.drop($(event.currentTarget).attr("data-index"));
|
|
renderServoMixRules();
|
|
renderOutputMapping();
|
|
});
|
|
|
|
$motorMixTableBody.on('click', "[data-role='role-motor-delete']", function (event) {
|
|
MOTOR_RULES.drop($(event.currentTarget).attr("data-index"));
|
|
renderMotorMixRules();
|
|
renderOutputMapping();
|
|
});
|
|
|
|
$servoMixTableBody.on('change', "input", function (event) {
|
|
renderOutputMapping();
|
|
});
|
|
|
|
$("[data-role='role-servo-add']").click(function () {
|
|
if (SERVO_RULES.hasFreeSlots()) {
|
|
SERVO_RULES.put(new ServoMixRule(0, 0, 100, 0));
|
|
renderServoMixRules();
|
|
renderOutputMapping();
|
|
}
|
|
});
|
|
|
|
$("[data-role='role-motor-add']").click(function () {
|
|
if (MOTOR_RULES.hasFreeSlots()) {
|
|
MOTOR_RULES.put(new MotorMixRule(1, 0, 0, 0));
|
|
renderMotorMixRules();
|
|
renderOutputMapping();
|
|
}
|
|
});
|
|
|
|
$("[data-role='role-logic-conditions-open']").click(function () {
|
|
LOGIC_CONDITIONS.open();
|
|
});
|
|
|
|
$('#save-button').click(saveAndReboot);
|
|
|
|
renderServoMixRules();
|
|
renderMotorMixRules();
|
|
|
|
renderOutputTable();
|
|
renderOutputMapping();
|
|
|
|
LOGIC_CONDITIONS.init($('#logic-wrapper'));
|
|
|
|
localize();
|
|
|
|
if (semver.gte(CONFIG.flightControllerVersion, "2.3.0")) {
|
|
helper.mspBalancedInterval.add('logic_conditions_pull', 350, 1, getLogicConditionsStatus);
|
|
}
|
|
|
|
GUI.content_ready(callback);
|
|
}
|
|
|
|
function getLogicConditionsStatus() {
|
|
mspHelper.loadSensorStatus(onStatusPullDone);
|
|
}
|
|
|
|
function onStatusPullDone() {
|
|
LOGIC_CONDITIONS.update(LOGIC_CONDITIONS_STATUS);
|
|
}
|
|
|
|
};
|
|
|
|
TABS.mixer.cleanup = function (callback) {
|
|
if (callback) callback();
|
|
};
|