1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-23 16:25:19 +03:00
inav-configurator/tabs/receiver.js
Alberto García Hierro 0e01133fc9 Make tab loading async safe and add a small animation
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.
2020-01-21 18:19:06 +00:00

384 lines
14 KiB
JavaScript

/*global chrome*/
'use strict';
TABS.receiver = {
rateChartHeight: 117
};
TABS.receiver.initialize = function (callback) {
var self = this;
if (GUI.active_tab != 'receiver') {
GUI.active_tab = 'receiver';
googleAnalytics.sendAppView('Receiver');
}
var loadChainer = new MSPChainerClass();
var loadChain = [
mspHelper.loadMisc,
mspHelper.loadRcData,
mspHelper.loadRcMap,
mspHelper.loadRxConfig,
mspHelper.loadRcDeadband
];
loadChain.push(mspHelper.loadRateProfileData);
loadChainer.setChain(loadChain);
loadChainer.setExitPoint(load_html);
loadChainer.execute();
function load_html() {
GUI.load("./tabs/receiver.html", process_html);
}
function drawRollPitchExpo() {
var pitch_roll_curve = $('.pitch_roll_curve canvas').get(0);
var context = pitch_roll_curve.getContext("2d");
var expoAVal = $('.tunings .rate input[name="expo"]');
var expoA = parseFloat(expoAVal.val());
var expoMVal = $('.tunings .rate input[name="manual_expo"]');
var expoM = parseFloat(expoMVal.val());
if (expoA <= parseFloat(expoAVal.prop('min')) || expoA >= parseFloat(expoAVal.prop('max')) ||
expoM <= parseFloat(expoMVal.prop('min')) || expoM >= parseFloat(expoMVal.prop('max'))) {
return;
}
var rateHeight = TABS.receiver.rateChartHeight;
// draw
context.clearRect(0, 0, 200, rateHeight);
context.beginPath();
context.moveTo(0, rateHeight);
context.quadraticCurveTo(110, rateHeight - ((rateHeight / 2) * (1 - expoA)), 200, 0);
context.lineWidth = 2;
context.strokeStyle = '#37a8db';
context.stroke();
context.beginPath();
context.moveTo(0, rateHeight);
context.quadraticCurveTo(110, rateHeight - ((rateHeight / 2) * (1 - expoM)), 200, 0);
context.lineWidth = 2;
context.strokeStyle = '#a837db';
context.stroke();
}
function process_html() {
// translate to user-selected language
localize();
// fill in data from RC_tuning
$('.tunings .throttle input[name="mid"]').val(RC_tuning.throttle_MID.toFixed(2));
$('.tunings .throttle input[name="expo"]').val(RC_tuning.throttle_EXPO.toFixed(2));
$('.tunings .rate input[name="expo"]').val(RC_tuning.RC_EXPO.toFixed(2));
$('.tunings .yaw_rate input[name="yaw_expo"]').val(RC_tuning.RC_YAW_EXPO.toFixed(2));
$('.tunings .rate input[name="manual_expo"]').val(RC_tuning.manual_RC_EXPO.toFixed(2));
$('.tunings .yaw_rate input[name="manual_yaw_expo"]').val(RC_tuning.manual_RC_YAW_EXPO.toFixed(2));
$('.deadband input[name="yaw_deadband"]').val(RC_deadband.yaw_deadband);
$('.deadband input[name="deadband"]').val(RC_deadband.deadband);
// generate bars
var bar_names = [
chrome.i18n.getMessage('controlAxisRoll'),
chrome.i18n.getMessage('controlAxisPitch'),
chrome.i18n.getMessage('controlAxisYaw'),
chrome.i18n.getMessage('controlAxisThrottle')
],
bar_container = $('.tab-receiver .bars');
for (var i = 0; i < RC.active_channels; i++) {
var name;
if (i < bar_names.length) {
name = bar_names[i];
} else {
name = chrome.i18n.getMessage("radioChannelShort") + (i + 1);
}
bar_container.append('\
<ul>\
<li class="name">' + name + '</li>\
<li class="meter">\
<div class="meter-bar">\
<div class="label"></div>\
<div class="fill">\
<div class="label"></div>\
</div>\
</div>\
</li>\
</ul>\
');
}
// we could probably use min and max throttle for the range, will see
var meter_scale = {
'min': 800,
'max': 2200
};
var meter_fill_array = [];
$('.meter .fill', bar_container).each(function () {
meter_fill_array.push($(this));
});
var meter_label_array = [];
$('.meter', bar_container).each(function () {
meter_label_array.push($('.label', this));
});
// correct inner label margin on window resize (i don't know how we could do this in css)
self.resize = function () {
var containerWidth = $('.meter:first', bar_container).width(),
labelWidth = $('.meter .label:first', bar_container).width(),
margin = (containerWidth / 2) - (labelWidth / 2);
for (var i = 0; i < meter_label_array.length; i++) {
meter_label_array[i].css('margin-left', margin);
}
};
$(window).on('resize', self.resize).resize(); // trigger so labels get correctly aligned on creation
// handle rcmap & rssi aux channel
var strBuffer = [], rcMapLetters = FC.getRcMapLetters();
for (var i = 0; i < RC_MAP.length; i++) {
strBuffer[RC_MAP[i]] = rcMapLetters[i];
}
// reconstruct
var str = strBuffer.join(''),
$rcMap = $('input[name="rcmap"]');
// set current value
$rcMap.val(str);
/*
* Send tracking event so we can know if users are using different mappings than EATR
*/
googleAnalytics.sendEvent('Setting', 'RcMappingRead', str);
// validation / filter
var last_valid = str;
$rcMap.on('input', function () {
var val = $(this).val();
// limit length to max 8
if (val.length > 8) {
val = val.substr(0, 8);
$(this).val(val);
}
});
$rcMap.focusout(function () {
if (!FC.isRcMapValid($(this).val()))
$(this).val(last_valid);
});
$rcMap.on('input change', function() {
$(this).css("color", FC.isRcMapValid($(this).val()) ? "" : "#FF0000");
});
// handle helper
$('select[name="rcmap_helper"]').val(0); // go out of bounds
$('select[name="rcmap_helper"]').change(function () {
$rcMap.val($(this).val());
});
// rssi
var rssi_channel_e = $('select[name="rssi_channel"]');
rssi_channel_e.append('<option value="0">Disabled</option>');
for (var i = 5; i < RC.active_channels + 1; i++) {
rssi_channel_e.append('<option value="' + i + '">CH' + i + '</option>');
}
$('select[name="rssi_channel"]').val(MISC.rssi_channel);
var rateHeight = TABS.receiver.rateChartHeight;
// UI Hooks
// curves
$('.tunings .throttle input').on('input change', function () {
setTimeout(function () { // let global validation trigger and adjust the values first
var throttleMidE = $('.tunings .throttle input[name="mid"]'),
throttleExpoE = $('.tunings .throttle input[name="expo"]'),
mid = parseFloat(throttleMidE.val()),
expo = parseFloat(throttleExpoE.val()),
throttle_curve = $('.throttle_curve canvas').get(0),
context = throttle_curve.getContext("2d");
// local validation to deal with input event
if (mid >= parseFloat(throttleMidE.prop('min')) &&
mid <= parseFloat(throttleMidE.prop('max')) &&
expo >= parseFloat(throttleExpoE.prop('min')) &&
expo <= parseFloat(throttleExpoE.prop('max'))) {
// continue
} else {
return;
}
// math magic by englishman
var midx = 200 * mid,
midxl = midx * 0.5,
midxr = (((200 - midx) * 0.5) + midx),
midy = rateHeight - (midx * (rateHeight / 200)),
midyl = rateHeight - ((rateHeight - midy) * 0.5 * (expo + 1)),
midyr = (midy / 2) * (expo + 1);
// draw
context.clearRect(0, 0, 200, rateHeight);
context.beginPath();
context.moveTo(0, rateHeight);
context.quadraticCurveTo(midxl, midyl, midx, midy);
context.moveTo(midx, midy);
context.quadraticCurveTo(midxr, midyr, 200, 0);
context.lineWidth = 2;
context.strokeStyle = '#37a8db';
context.stroke();
}, 0);
}).trigger('input');
$('.tunings .rate input').on('input change', function () {
setTimeout(function () { // let global validation trigger and adjust the values first
drawRollPitchExpo();
}, 0);
}).trigger('input');
$('a.refresh').click(function () {
MSP.send_message(MSPCodes.MSP_RC_TUNING, false, false, function () {
GUI.log(chrome.i18n.getMessage('receiverDataRefreshed'));
// fill in data from RC_tuning
$('.tunings .throttle input[name="mid"]').val(RC_tuning.throttle_MID.toFixed(2));
$('.tunings .throttle input[name="expo"]').val(RC_tuning.throttle_EXPO.toFixed(2));
$('.tunings .rate input[name="expo"]').val(RC_tuning.RC_EXPO.toFixed(2));
// update visual representation
$('.tunings .throttle input').change();
$('.tunings .rate input').change();
});
});
$('a.update').click(function () {
// catch RC_tuning changes
RC_tuning.throttle_MID = parseFloat($('.tunings .throttle input[name="mid"]').val());
RC_tuning.throttle_EXPO = parseFloat($('.tunings .throttle input[name="expo"]').val());
RC_tuning.RC_EXPO = parseFloat($('.tunings .rate input[name="expo"]').val());
RC_tuning.RC_YAW_EXPO = parseFloat($('.tunings .yaw_rate input[name="yaw_expo"]').val());
RC_tuning.manual_RC_EXPO = parseFloat($('.tunings .rate input[name="manual_expo"]').val());
RC_tuning.manual_RC_YAW_EXPO = parseFloat($('.tunings .yaw_rate input[name="manual_yaw_expo"]').val());
RC_deadband.yaw_deadband = parseInt($('.deadband input[name="yaw_deadband"]').val());
RC_deadband.deadband = parseInt($('.deadband input[name="deadband"]').val());
// catch rc map
var rcMapValue = $('input[name="rcmap"]').val();
var strBuffer = rcMapValue.split('');
/*
* Send tracking event so we can know if users are using different mappings than EATR
*/
googleAnalytics.sendEvent('Setting', 'RcMappingSave', rcMapValue);
for (var i = 0; i < RC_MAP.length; i++) {
RC_MAP[i] = strBuffer.indexOf(FC.getRcMapLetters()[i]);
}
// catch rssi aux
MISC.rssi_channel = parseInt($('select[name="rssi_channel"]').val());
function save_rc_map() {
MSP.send_message(MSPCodes.MSP_SET_RX_MAP, mspHelper.crunch(MSPCodes.MSP_SET_RX_MAP), false, save_misc);
}
function save_misc() {
MSP.send_message(MSPCodes.MSP_SET_MISC, mspHelper.crunch(MSPCodes.MSP_SET_MISC), false, save_rc_configs);
}
function save_rc_configs() {
MSP.send_message(MSPCodes.MSP_SET_RC_DEADBAND, mspHelper.crunch(MSPCodes.MSP_SET_RC_DEADBAND), false, save_to_eeprom);
}
function save_to_eeprom() {
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('receiverEepromSaved'));
});
}
MSP.send_message(MSPCodes.MSPV2_INAV_SET_RATE_PROFILE, mspHelper.crunch(MSPCodes.MSPV2_INAV_SET_RATE_PROFILE), false, save_rc_map);
});
$("a.sticks").click(function () {
var
windowWidth = 420,
windowHeight = Math.min(window.innerHeight, 720);
chrome.app.window.create("/tabs/receiver_msp.html", {
id: "receiver_msp",
innerBounds: {
minWidth: windowWidth, minHeight: windowHeight,
width: windowWidth, height: windowHeight,
maxWidth: windowWidth, maxHeight: windowHeight
},
alwaysOnTop: true
}, function (createdWindow) {
// Give the window a callback it can use to send the channels (otherwise it can't see those objects)
createdWindow.contentWindow.setRawRx = function (channels) {
if (CONFIGURATOR.connectionValid && GUI.active_tab != 'cli') {
mspHelper.setRawRx(channels);
return true;
} else {
return false;
}
}
});
});
// Only show the MSP control sticks if the MSP Rx feature is enabled
$(".sticks_btn").toggle(FC.isRxTypeEnabled('RX_MSP'));
function get_rc_data() {
/*
* Throttling
*/
if (helper.mspQueue.shouldDrop()) {
update_ui();
return;
}
MSP.send_message(MSPCodes.MSP_RC, false, false, update_ui);
}
function update_ui() {
var i;
// update bars with latest data
for (i = 0; i < RC.active_channels; i++) {
meter_fill_array[i].css('width', ((RC.channels[i] - meter_scale.min) / (meter_scale.max - meter_scale.min) * 100).clamp(0, 100) + '%');
meter_label_array[i].text(RC.channels[i]);
}
}
helper.mspBalancedInterval.add('receiver_pull', 35, 1, get_rc_data);
GUI.content_ready(callback);
}
};
TABS.receiver.cleanup = function (callback) {
$(window).off('resize', this.resize);
if (callback) callback();
};