1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-22 15:55:28 +03:00

UI for smix rules

This commit is contained in:
Pawel Spychalski (DzikuVx) 2018-01-24 16:25:39 +01:00
parent 912864bc19
commit cf3a5b2a64
11 changed files with 257 additions and 76 deletions

View file

@ -2315,5 +2315,14 @@
},
"downloadUpdatesBtn": {
"message": "Download new app"
},
"servoMixer": {
"message": "Servo mixer"
},
"servoMixerDelete": {
"message": "Delete"
},
"servoMixerAdd": {
"message": "Add new mixer rule"
}
}

View file

@ -91,6 +91,7 @@ sources.js = [
'./js/boards.js',
'./js/tasks.js',
'./js/servoMixRule.js',
'./js/servoMixRuleCollection.js',
'./main.js',
'./tabs/*.js',
'./js/eventFrequencyAnalyzer.js',

View file

@ -150,7 +150,7 @@ var FC = {
ADJUSTMENT_RANGES = [];
SERVO_CONFIG = [];
SERVO_RULES = [];
SERVO_RULES = new ServoMixRuleCollection();
SERIAL_CONFIG = {
ports: [],
@ -935,5 +935,27 @@ var FC = {
},
getRcMapLetters: function () {
return ['A', 'E', 'R', 'T', '5', '6', '7', '8'];
},
getServoMixInputNames: function () {
return [
'Stabilised Roll',
'Stabilised Pitch',
'Stabilised Yaw',
'Stabilised Throttle',
'RC Roll',
'RC Pitch',
'RC Yaw',
'RC Throttle',
'RC Channel 5',
'RC Channel 6',
'RC Channel 7',
'RC Channel 8',
'Gimbal Pitch',
'Gimbal Roll',
'Flaps'
];
},
getServoMixInputName: function (input) {
return getServoMixInputNames()[input];
}
};

View file

@ -2,29 +2,29 @@
// generate mixer
var mixerList = [
{name: 'Tricopter', model: 'tricopter', image: 'tri'}, // 1
{name: 'Quad +', model: 'quad_x', image: 'quad_p'}, // 2
{name: 'Quad X', model: 'quad_x', image: 'quad_x'}, // 3
{name: 'Bicopter', model: 'custom', image: 'bicopter'}, // 4
{name: 'Gimbal', model: 'custom', image: 'custom'}, // 5
{name: 'Y6', model: 'y6', image: 'y6'}, // 6
{name: 'Hex +', model: 'hex_plus', image: 'hex_p'}, // 7
{name: 'Flying Wing', model: 'custom', image: 'flying_wing'}, // 8
{name: 'Y4', model: 'y4', image: 'y4'}, // 9
{name: 'Hex X', model: 'hex_x', image: 'hex_x'}, // 10
{name: 'Octo X8', model: 'custom', image: 'octo_x8'}, // 11
{name: 'Octo Flat +', model: 'custom', image: 'octo_flat_p'}, // 12
{name: 'Octo Flat X', model: 'custom', image: 'octo_flat_x'}, // 13
{name: 'Airplane', model: 'custom', image: 'airplane'}, // 14
{name: 'Heli 120', model: 'custom', image: 'custom'}, // 15
{name: 'Heli 90', model: 'custom', image: 'custom'}, // 16
{name: 'V-tail Quad', model: 'quad_vtail', image: 'vtail_quad'}, // 17
{name: 'Hex H', model: 'custom', image: 'custom'}, // 18
{name: 'PPM to SERVO', model: 'custom', image: 'custom'}, // 19
{name: 'Dualcopter', model: 'custom', image: 'custom'}, // 20
{name: 'Singlecopter', model: 'custom', image: 'custom'}, // 21
{name: 'A-tail Quad', model: 'quad_atail', image: 'atail_quad'}, // 22
{name: 'Custom', model: 'custom', image: 'custom'}, // 23
{name: 'Custom Airplane', model: 'custom', image: 'custom'}, // 24
{name: 'Custom Tricopter', model: 'custom', image: 'custom'} // 25
{name: 'Tricopter', model: 'tricopter', image: 'tri', hasCustomServoMixer: false}, // 1
{name: 'Quad +', model: 'quad_x', image: 'quad_p', hasCustomServoMixer: false}, // 2
{name: 'Quad X', model: 'quad_x', image: 'quad_x', hasCustomServoMixer: false}, // 3
{name: 'Bicopter', model: 'custom', image: 'bicopter', hasCustomServoMixer: false}, // 4
{name: 'Gimbal', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 5
{name: 'Y6', model: 'y6', image: 'y6', hasCustomServoMixer: false}, // 6
{name: 'Hex +', model: 'hex_plus', image: 'hex_p', hasCustomServoMixer: false}, // 7
{name: 'Flying Wing', model: 'custom', image: 'flying_wing', hasCustomServoMixer: false}, // 8
{name: 'Y4', model: 'y4', image: 'y4', hasCustomServoMixer: false}, // 9
{name: 'Hex X', model: 'hex_x', image: 'hex_x', hasCustomServoMixer: false}, // 10
{name: 'Octo X8', model: 'custom', image: 'octo_x8', hasCustomServoMixer: false}, // 11
{name: 'Octo Flat +', model: 'custom', image: 'octo_flat_p', hasCustomServoMixer: false}, // 12
{name: 'Octo Flat X', model: 'custom', image: 'octo_flat_x', hasCustomServoMixer: false}, // 13
{name: 'Airplane', model: 'custom', image: 'airplane', hasCustomServoMixer: false}, // 14
{name: 'Heli 120', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 15
{name: 'Heli 90', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 16
{name: 'V-tail Quad', model: 'quad_vtail', image: 'vtail_quad', hasCustomServoMixer: false}, // 17
{name: 'Hex H', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 18
{name: 'PPM to SERVO', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 19
{name: 'Dualcopter', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 20
{name: 'Singlecopter', model: 'custom', image: 'custom', hasCustomServoMixer: false}, // 21
{name: 'A-tail Quad', model: 'quad_atail', image: 'atail_quad', hasCustomServoMixer: false}, // 22
{name: 'Custom', model: 'custom', image: 'custom', hasCustomServoMixer: true}, // 23
{name: 'Custom Airplane', model: 'custom', image: 'custom', hasCustomServoMixer: true}, // 24
{name: 'Custom Tricopter', model: 'custom', image: 'custom', hasCustomServoMixer: true} // 25
];

View file

@ -350,11 +350,11 @@ var mspHelper = (function (gui) {
}
break;
case MSPCodes.MSP_SERVO_MIX_RULES:
SERVO_RULES = [];
SERVO_RULES.flush();
if (data.byteLength % 7 === 0) {
for (i = 0; i < data.byteLength; i += 7) {
SERVO_RULES.push(new ServoMixRule(
SERVO_RULES.put(new ServoMixRule(
data.getInt8(i + 0, true),
data.getInt8(i + 1, true),
data.getInt8(i + 2, true),
@ -362,6 +362,7 @@ var mspHelper = (function (gui) {
));
}
}
SERVO_RULES.cleanup();
break;

View file

@ -5,27 +5,41 @@ var ServoMixRule = function (target, input, rate, speed) {
var self = {};
// self.target = target;
// self.input = input;
// self.rate = rate;
// self.speed = speed;
self.getTarget = function () {
return target;
};
self.setTarget = function (data) {
target = data;
};
self.getInput = function () {
return input;
};
self.setInput = function (data) {
input = data;
};
self.getRate = function () {
return rate;
};
self.setRate = function (data) {
rate = data;
};
self.getSpeed = function () {
return speed;
};
self.setSpeed = function (data) {
speed = data;
};
self.isUsed = function () {
return rate !== 0;
};
return self;
};

View file

@ -0,0 +1,49 @@
/*global $, ServoMixRule*/
'use strict';
var ServoMixRuleCollection = function () {
var self = {};
var data = [];
self.put = function (element) {
data.push(element);
};
self.get = function () {
return data;
};
self.drop = function (index) {
data[index].setRate(0);
self.cleanup();
};
self.flush = function () {
data = [];
};
self.cleanup = function () {
var tmpData = [];
data.forEach(function (element) {
if (element.isUsed()) {
tmpData.push(element);
}
});
data = tmpData;
};
self.inflate = function () {
while (self.hasFreeSlots()) {
self.put(new ServoMixRule(0, 0, 0, 0));
}
}
self.hasFreeSlots = function () {
return data.length < 16;
};
return self;
};

View file

@ -1315,6 +1315,15 @@ dialog {
float: left;
}
.default_btn.narrow {
width: auto;
margin-bottom: 0;
}
.default_btn.narrow a {
padding: 5px;
}
.default_btn a {
padding: 5px 0 5px 0;
text-align: center;

View file

@ -50,7 +50,16 @@
background-color: #f9f9f9;
}
.tab-servos table tr:first-child {
#servo-mix-table input {
width: 75px;
}
#servo-mix-table .btn {
float: none;
}
#servo-config-table tr:first-child,
#servo-mix-table thead tr {
border-left: 1px solid #e4e4e4;
border-right: 1px solid #e4e4e4;
background-color: #828885;
@ -154,22 +163,6 @@
bottom: 10px;
}
.tab-servos .require-support {
display: none;
}
.tab-servos.supported .require-support {
display: block;
}
.tab-servos .require-upgrade {
display: block;
}
.tab-servos.supported .require-upgrade {
display: none;
}
.tab-servos .live span {
margin-right: 10px;
}

View file

@ -1,13 +1,12 @@
<!--suppress HtmlFormInputWithoutLabel -->
<div class="tab-servos toolbar_fixed_bottom">
<div class="content_wrapper">
<div class="tab_title" data-i18n="tabServos">Servos</div>
<div class="cf_doc_version_bt">
<a id="button-documentation" href="" target="_blank"></a>
</div>
<div class="require-support">
<div>
<div class="title" data-i18n="servosChangeDirection"></div>
<table class="fields">
<table id="servo-config-table" class="fields">
<tr class="main">
<th width="110px" data-i18n="servosName"></th>
<th data-i18n="servosMid"></th>
@ -23,6 +22,30 @@
<input type="checkbox" class="togglemedium" /> <span data-i18n="servosLiveMode"></span>
</div>
</div>
<div class="clear-both"></div>
<div id="servo-mix-table-wrapper" class="margin-top">
<div class="tab_title" data-i18n="servoMixer">Servo mixer</div>
<div class="btn default_btn narrow pull-right" style="margin-bottom: 15px">
<a href="#" data-role="role-add" data-i18n="servoMixerAdd"></a>
</div>
<table id="servo-mix-table">
<thead>
<tr>
<th style="width: 75px">Servo</th>
<th>Input</th>
<th>Weight</th>
<th>Speed</th>
<th style="width: 75px"></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="btn default_btn narrow pull-right" style="margin-bottom: 15px">
<a href="#" data-role="role-add" data-i18n="servoMixerAdd"></a>
</div>
</div>
</div>
<div class="content_toolbar">
<div class="btn save_btn">

View file

@ -14,6 +14,7 @@ TABS.servos.initialize = function (callback) {
loadChainer.setChain([
mspHelper.loadServoConfiguration,
mspHelper.loadRcData,
mspHelper.loadBfConfig,
mspHelper.loadServoMixRules
]);
loadChainer.setExitPoint(load_html);
@ -36,19 +37,20 @@ TABS.servos.initialize = function (callback) {
function update_ui() {
var i,
$tabServos = $(".tab-servos");
$tabServos = $(".tab-servos"),
$servoConfigTable = $('#servo-config-table'),
$servoMixTable = $('#servo-mix-table'),
$servoMixTableBody = $servoMixTable.find('tbody');
if (SERVO_CONFIG.length == 0) {
$tabServos.removeClass("supported");
$tabServos.addClass("is-hidden");
return;
}
$tabServos.addClass("supported");
var servoCheckbox = '';
var servoHeader = '';
for (i = 0; i < RC.active_channels-4; i++) {
servoHeader = servoHeader + '<th class="short">CH' + (i+5) + '</th>';
for (i = 0; i < RC.active_channels - 4; i++) {
servoHeader = servoHeader + '<th class="short">CH' + (i + 5) + '</th>';
}
servoHeader = servoHeader + '<th data-i18n="servosDirectionAndRate"></th>';
@ -56,18 +58,16 @@ TABS.servos.initialize = function (callback) {
servoCheckbox = servoCheckbox + '<td class="channel"><input type="checkbox"/></td>';
}
$('div.tab-servos table.fields tr.main').append(servoHeader);
$servoConfigTable.find('tr.main').append(servoHeader);
function process_servos(name, alternate, obj) {
$('div.supported_wrapper').show();
$('div.tab-servos table.fields').append('\
$servoConfigTable.append('\
<tr> \
<td style="text-align: center">' + name + '</td>\
<td class="middle"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].middle + '" /></td>\
<td class="min"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].min +'" /></td>\
<td class="max"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].max +'" /></td>\
<td class="min"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].min + '" /></td>\
<td class="max"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].max + '" /></td>\
' + servoCheckbox + '\
<td class="direction">\
</td>\
@ -75,13 +75,13 @@ TABS.servos.initialize = function (callback) {
');
if (SERVO_CONFIG[obj].indexOfChannelToForward >= 0) {
$('div.tab-servos table.fields tr:last td.channel input').eq(SERVO_CONFIG[obj].indexOfChannelToForward).prop('checked', true);
$servoConfigTable.find('tr:last td.channel input').eq(SERVO_CONFIG[obj].indexOfChannelToForward).prop('checked', true);
}
// adding select box and generating options
$('div.tab-servos table.fields tr:last td.direction').append('<select class="rate" name="rate"></select>');
$servoConfigTable.find('tr:last td.direction').append('<select class="rate" name="rate"></select>');
var select = $('div.tab-servos table.fields tr:last td.direction select');
var select = $servoConfigTable.find('tr:last td.direction select');
for (var i = FC.MAX_SERVO_RATE; i >= FC.MIN_SERVO_RATE; i--) {
select.append('<option value="' + i + '">Rate: ' + i + '%</option>');
@ -90,20 +90,20 @@ TABS.servos.initialize = function (callback) {
// select current rate
select.val(SERVO_CONFIG[obj].rate);
$('div.tab-servos table.fields tr:last').data('info', {'obj': obj});
$servoConfigTable.find('tr:last').data('info', { 'obj': obj });
// UI hooks
// only one checkbox for indicating a channel to forward can be selected at a time, perhaps a radio group would be best here.
$('div.tab-servos table.fields tr:last td.channel input').click(function () {
if($(this).is(':checked')) {
$servoConfigTable.find('tr:last td.channel input').click(function () {
if ($(this).is(':checked')) {
$(this).parent().parent().find('.channel input').not($(this)).prop('checked', false);
}
});
}
function servos_update(save_configuration_to_eeprom) {
$('div.tab-servos table.fields tr:not(".main")').each(function () {
$servoConfigTable.find('tr:not(".main")').each(function () {
var info = $(this).data('info');
@ -115,7 +115,6 @@ TABS.servos.initialize = function (callback) {
SERVO_CONFIG[info.obj].indexOfChannelToForward = channelIndex;
SERVO_CONFIG[info.obj].middle = parseInt($('.middle input', this).val());
SERVO_CONFIG[info.obj].min = parseInt($('.min input', this).val());
SERVO_CONFIG[info.obj].max = parseInt($('.max input', this).val());
@ -128,14 +127,14 @@ TABS.servos.initialize = function (callback) {
}
// drop previous table
$('div.tab-servos table.fields tr:not(:first)').remove();
$servoConfigTable.find('tr:not(:first)').remove();
for (var servoIndex = 0; servoIndex < 8; servoIndex++) {
process_servos('Servo ' + servoIndex, '', servoIndex, false);
}
// UI hooks for dynamically generated elements
$('table.directions select, table.directions input, table.fields select, table.fields input').change(function () {
$('table.directions select, table.directions input, #servo-config-table select, #servo-config-table input').change(function () {
if ($('div.live input').is(':checked')) {
// apply small delay as there seems to be some funky update business going wrong
helper.timeout.add('servos_update', servos_update, 10);
@ -146,6 +145,67 @@ TABS.servos.initialize = function (callback) {
servos_update(true);
});
$servoMixTableBody.on('click', "[data-role='role-delete']", function (event) {
SERVO_RULES.drop($(event.currentTarget).attr("data-index"));
renderServoMixRules();
});
$("[data-role='role-add']").click(function () {
if (SERVO_RULES.hasFreeSlots()) {
SERVO_RULES.put(new ServoMixRule(0, 0, 0, 0));
renderServoMixRules();
}
});
function renderServoMixRules() {
/*
* Process servo mix table UI
*/
var rules = SERVO_RULES.get();
$servoMixTableBody.find("*").remove();
for (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="7" /></td>\
<td><select class="mix-rule-input"></select></td>\
<td><input type="number" class="mix-rule-rate" step="1" min="-100" max="100" /></td>\
<td><input type="number" class="mix-rule-speed" step="1" min="0" max="255" /></td>\
<td><span class="btn default_btn narrow"><a href="#" data-role="role-delete" data-i18n="servoMixerDelete"></a></span></td>\
</tr>\
');
const $row = $servoMixTableBody.find('tr:last');
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());
});
$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-speed").val(servoRule.getSpeed()).change(function () {
servoRule.setSpeed($(this).val());
});
$row.find("[data-role='role-delete']").attr("data-index", servoRuleIndex);
}
}
localize();
}
renderServoMixRules();
}
function process_html() {