Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions js/outputMapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
const TIM_USE_BEEPER = 25;
const TIM_USE_PINIO = 26;

const OUTPUT_TYPE_MOTOR = 0;
const OUTPUT_TYPE_SERVO = 1;
const OUTPUT_TYPE_LED = 2;
const OUTPUT_TYPE_PINIO = 3;
const OUTPUT_TYPE_MOTOR = 0;
const OUTPUT_TYPE_SERVO = 1;
const OUTPUT_TYPE_LED = 2;
const OUTPUT_TYPE_PINIO = 3;
const OUTPUT_TYPE_BEEPER = 4;

const SPECIAL_LABEL_LED = 1;
const SPECIAL_LABEL_PINIO_BASE = 2; // values 2..5 = USER1..USER4 (add channel index 0-3)
Expand All @@ -43,6 +44,7 @@
self.TIMER_OUTPUT_MODE_SERVOS = 2;
self.TIMER_OUTPUT_MODE_LED = 3;
self.TIMER_OUTPUT_MODE_PINIO = 4;
self.TIMER_OUTPUT_MODE_BEEPER = 5;

self.flushTimerOverrides = function() {
timerOverrides = {};
Expand Down Expand Up @@ -76,13 +78,13 @@
for (let entry of directAssignments) {
let displayIndex = entry.outputIndex - offset;
if (displayIndex < 0 || displayIndex >= outputCount) continue;
if (entry.type === 1) {
if (entry.type === TIM_USE_MOTOR) {
outputMap[displayIndex] = 'Motor ' + entry.number;
} else if (entry.type === 2) {
} else if (entry.type === TIM_USE_SERVO) {
outputMap[displayIndex] = 'Servo ' + entry.number;
} else if (entry.type === 3) {
outputMap[displayIndex] = 'Led';
} else if (entry.type === 4) {
} else if (entry.type === TIM_USE_BEEPER) {
outputMap[displayIndex] = 'Buzzer';
} else if (entry.type === TIM_USE_PINIO) {
outputMap[displayIndex] = 'USER' + entry.number;
}
}
Expand All @@ -108,6 +110,39 @@
return data[timer].specialLabels == SPECIAL_LABEL_LED;
}

self.isBeeperPin = function(outputIndex) {
return BitHelper.bit_check(data[outputIndex]['usageFlags'], TIM_USE_BEEPER);
}

function isTimerDefault(timerId, flag) {
let found = false;
for (const entry of data) {
if (entry['timerId'] !== timerId) continue;
const flags = entry['usageFlags'];
if (BitHelper.bit_check(flags, TIM_USE_MOTOR) || BitHelper.bit_check(flags, TIM_USE_SERVO)) return false;
if (BitHelper.bit_check(flags, flag)) found = true;
}
return found;
}

// Returns true when the timer's compile-time default assignment is LED.
self.isTimerDefaultLed = function(timerId) {
// LED can also be identified by specialLabels, so check both.
let hasLed = false;
for (const entry of data) {
if (entry['timerId'] !== timerId) continue;
const flags = entry['usageFlags'];
if (BitHelper.bit_check(flags, TIM_USE_MOTOR) || BitHelper.bit_check(flags, TIM_USE_SERVO)) return false;
if (BitHelper.bit_check(flags, TIM_USE_LED) || entry['specialLabels'] === SPECIAL_LABEL_LED) hasLed = true;
}
return hasLed;
}

// Returns true when the timer's compile-time default assignment is BEEPER.
self.isTimerDefaultBeeper = function(timerId) {
return isTimerDefault(timerId, TIM_USE_BEEPER);
}

self.getOutputTimerColor = function (output) {
let timerId = self.getTimerId(output);

Expand All @@ -126,7 +161,7 @@
return Object.keys(used).sort((a, b) => a - b);
}

function getTimerMap(isMR, motors, servos) {

Check failure on line 164 in js/outputMapping.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 33 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=iNavFlight_inav-configurator&issues=AZ6loOj8V2gNjGak2LlC&open=AZ6loOj8V2gNjGak2LlC&pullRequest=2654
let timerMap = [],
motorsToGo = motors,
servosToGo = servos;
Expand All @@ -142,6 +177,15 @@
}
}

// Pre-assign BEEPER outputs — explicitly overridden by user, takes precedence over flag-based assignment
for (let i = 0; i < data.length; i++) {
let timerId = data[i]['timerId'];
let mode = timerOverrides[timerId] || self.TIMER_OUTPUT_MODE_AUTO;
if (mode === self.TIMER_OUTPUT_MODE_BEEPER) {
timerMap[i] = OUTPUT_TYPE_BEEPER;
}
}

// Two priority passes: dedicated outputs first, then auto.
// Matches firmware pwmBuildTimerOutputList() behavior.
for (let priority = 0; priority < 2; priority++) {
Expand Down Expand Up @@ -169,6 +213,8 @@
servosToGo--;
} else if (!isDedicated && BitHelper.bit_check(flags, TIM_USE_LED)) {
timerMap[i] = OUTPUT_TYPE_LED;
} else if (!isDedicated && BitHelper.bit_check(flags, TIM_USE_BEEPER)) {
timerMap[i] = OUTPUT_TYPE_BEEPER;
}
}
}
Expand All @@ -176,7 +222,7 @@
return timerMap;
};

self.getOutputTable = function (isMR, motors, servos) {

Check failure on line 225 in js/outputMapping.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=iNavFlight_inav-configurator&issues=AZ6loOj8V2gNjGak2LlD&open=AZ6loOj8V2gNjGak2LlD&pullRequest=2654
let currentServoIndex = 0,
timerMap = getTimerMap(isMR, motors, servos.length),
motorNumbers = {},
Expand Down Expand Up @@ -209,6 +255,8 @@
outputMap[i] = "Led";
} else if (assignment == OUTPUT_TYPE_PINIO) {
outputMap[i] = "USER" + (data[i + offset]['specialLabels'] - SPECIAL_LABEL_PINIO_BASE + 1);
} else if (assignment == OUTPUT_TYPE_BEEPER) {
outputMap[i] = "Buzzer";
}
}

Expand All @@ -233,6 +281,7 @@
BitHelper.bit_check(flags, TIM_USE_MOTOR) ||
BitHelper.bit_check(flags, TIM_USE_SERVO) ||
BitHelper.bit_check(flags, TIM_USE_LED) ||
BitHelper.bit_check(flags, TIM_USE_BEEPER) ||
BitHelper.bit_check(flags, TIM_USE_PINIO) ||
data[i]['specialLabels'] >= SPECIAL_LABEL_PINIO_BASE
) {
Expand All @@ -249,6 +298,7 @@
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_MOTOR) ||
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_SERVO) ||
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_LED) ||
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_BEEPER) ||
BitHelper.bit_check(data[i]['usageFlags'], TIM_USE_PINIO) ||
data[i]['specialLabels'] >= SPECIAL_LABEL_PINIO_BASE
) {
Expand Down
10 changes: 3 additions & 7 deletions tabs/mixer.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@
</div>
</div>

<div class="platform-type gui_box grey">
<div class="gui_box_titlebar">
<div class="spacer_box_title" data-i18n="timerOutputs"></div>
</div>
<div class="spacer_box" id="timerOutputsList" style="padding: 0; width: calc(100% - 12px)"></div>
</div>

</div>
<div class="rightWrapper">
<div class="platform-type gui_box grey">
Expand Down Expand Up @@ -104,6 +97,9 @@
<div class="spacer_box_title" data-i18n="mappingTableTitle"></div>
</div>
<table class="output-table">
<tr id="timer-row">

</tr>
<tr id="output-row">

</tr>
Expand Down
122 changes: 70 additions & 52 deletions tabs/mixer.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,32 +80,85 @@
import('./mixer.html?raw').then(({default: html}) => GUI.load(html, Settings.processHtml(processHtml)));
}

function buildTimerGroups(outputCount) {

Check warning on line 83 in tabs/mixer.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function 'buildTimerGroups' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=iNavFlight_inav-configurator&issues=AZ6qO9bSze_0heoHBc0H&open=AZ6qO9bSze_0heoHBc0H&pullRequest=2654
const groups = [];
for (let i = 0; i < outputCount; i++) {
const timerId = FC.OUTPUT_MAPPING.getTimerId(i);
const color = FC.OUTPUT_MAPPING.getOutputTimerColor(i);
if (groups.length > 0 && groups.at(-1).timerId === timerId) {
groups.at(-1).count++;
} else {
groups.push({ timerId, count: 1, color });
}
}
return groups;
}

function buildTimerSelectHtml(group) {

Check warning on line 97 in tabs/mixer.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move function 'buildTimerSelectHtml' to the outer scope.

See more on https://sonarcloud.io/project/issues?id=iNavFlight_inav-configurator&issues=AZ6qO9bSze_0heoHBc0I&open=AZ6qO9bSze_0heoHBc0I&pullRequest=2654
const O = FC.OUTPUT_MAPPING;
const usageMode = O.getTimerOverride(group.timerId) ?? O.TIMER_OUTPUT_MODE_AUTO;
let displayMode = usageMode;
if (usageMode === O.TIMER_OUTPUT_MODE_AUTO) {
if (O.isTimerDefaultLed(group.timerId)) displayMode = O.TIMER_OUTPUT_MODE_LED;
else if (O.isTimerDefaultBeeper(group.timerId)) displayMode = O.TIMER_OUTPUT_MODE_BEEPER;
}
const optionsHtml = [
[O.TIMER_OUTPUT_MODE_AUTO, 'AUTO'],
[O.TIMER_OUTPUT_MODE_MOTORS, 'MOTORS'],
[O.TIMER_OUTPUT_MODE_SERVOS, 'SERVOS'],
[O.TIMER_OUTPUT_MODE_LED, 'LED'],
[O.TIMER_OUTPUT_MODE_PINIO, 'PINIO / PWM'],
[O.TIMER_OUTPUT_MODE_BEEPER, 'BEEPER'],
].map(([value, label]) =>
'<option value=' + value + (displayMode === value ? ' selected' : '') + '>' + label + '</option>'
).join('');
return 'Timer&nbsp;' + (group.timerId + 1) + ' <select id="timer-output-' + group.timerId + '">' + optionsHtml + '</select>';
}

function renderOutputTable() {
let outputCount = FC.OUTPUT_MAPPING.getOutputCount(),
$outputRow = $('#output-row'),
$functionRow = $('#function-row');
const outputCount = FC.OUTPUT_MAPPING.getOutputCount();
const $timerRow = $('#timer-row');
const $outputRow = $('#output-row');
const $functionRow = $('#function-row');

$timerRow.append('<th></th>');
$outputRow.append('<th data-i18n="mappingTableOutput"></th>');
$functionRow.append('<th data-i18n="mappingTableFunction"></th>');

for (let i = 1; i <= outputCount; i++) {

let timerId = FC.OUTPUT_MAPPING.getTimerId(i - 1);
let color = FC.OUTPUT_MAPPING.getOutputTimerColor(i - 1);
let isLed = FC.OUTPUT_MAPPING.isLedPin(i - 1);
const groups = buildTimerGroups(outputCount);

for (const group of groups) {
const selectHtml = buildTimerSelectHtml(group);
$timerRow.append('<td colspan="' + group.count + '" style="background-color: ' + group.color + '; padding-right: 4px">' + selectHtml + '</td>');
}

$outputRow.append('<td style="background-color: ' + color + '">S' + i + (isLed ? '/LED' : '') + ' (Timer&nbsp;' + (timerId + 1) + ')</td>');
$functionRow.append('<td id="function-' + i +'">-</td>');
for (let i = 1; i <= outputCount; i++) {
const timerId = FC.OUTPUT_MAPPING.getTimerId(i - 1);

Check warning on line 136 in tabs/mixer.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of the unused 'timerId' variable.

See more on https://sonarcloud.io/project/issues?id=iNavFlight_inav-configurator&issues=AZ6qlhVLBlW5IpazGRgT&open=AZ6qlhVLBlW5IpazGRgT&pullRequest=2654

Check warning on line 136 in tabs/mixer.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to variable "timerId".

See more on https://sonarcloud.io/project/issues?id=iNavFlight_inav-configurator&issues=AZ6qlhVLBlW5IpazGRgU&open=AZ6qlhVLBlW5IpazGRgU&pullRequest=2654
const color = FC.OUTPUT_MAPPING.getOutputTimerColor(i - 1);
const isLed = FC.OUTPUT_MAPPING.isLedPin(i - 1);
const isBeeper = FC.OUTPUT_MAPPING.isBeeperPin(i - 1);
$outputRow.append('<td style="background-color: ' + color + '">S' + i + (isLed ? '/LED' : '') + (isBeeper ? '/Buzzer' : '') + '</td>');
$functionRow.append('<td id="function-' + i + '">-</td>');
}

$outputRow.find('td').css('width', 100 / (outputCount + 1) + '%');

for (const group of groups) {
$('#timer-output-' + group.timerId).on('change', function() {
updateTimerOverride();
if (FC.OUTPUT_MAPPING.hasDirectAssignment()) {
mspHelper.queryOutputAssignment(renderOutputMapping);
} else {
renderOutputMapping();
}
});
}
}

function updateTimerOverride() {
let timers = FC.OUTPUT_MAPPING.getUsedTimerIds();

for(let i =0; i < timers.length;++i) {
for(let i = 0; i < timers.length; ++i) {
let timerId = timers[i];
let $select = $('#timer-output-' + timerId);
if(!$select) {
Expand All @@ -115,51 +168,17 @@
}
}

function renderTimerOverride() {
let outputCount = FC.OUTPUT_MAPPING.getOutputCount(),
$container = $('#timerOutputsList'), timers = {};


let usedTimers = FC.OUTPUT_MAPPING.getUsedTimerIds();

for (let t of usedTimers) {
var usageMode = FC.OUTPUT_MAPPING.getTimerOverride(t);
$container.append(
'<div class="select" style="padding: 5px; margin: 1px; background-color: ' + FC.OUTPUT_MAPPING.getTimerColor(t) + '">' +
'<select id="timer-output-' + t + '">' +
'<option value=' + FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_AUTO + '' + (usageMode == FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_AUTO ? ' selected' : '')+ '>AUTO</option>'+
'<option value=' + FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_MOTORS + '' + (usageMode == FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_MOTORS ? ' selected' : '')+ '>MOTORS</option>'+
'<option value=' + FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_SERVOS + '' + (usageMode == FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_SERVOS ? ' selected' : '')+ '>SERVOS</option>'+
'<option value=' + FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_LED + '' + (usageMode == FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_LED ? ' selected' : '')+ '>LED</option>'+
'<option value=' + FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_PINIO + '' + (usageMode == FC.OUTPUT_MAPPING.TIMER_OUTPUT_MODE_PINIO ? ' selected' : '')+ '>PINIO / DUTY CYCLE</option>'+
'</select>' +
'<label for="timer-output-' + t + '">' +
'<span> Timer ' + (parseInt(t) + 1) + '</span>' +
'</label>' +
'</div>'
);
$('#timer-output-' + t).on('change', function() {
updateTimerOverride();
if (FC.OUTPUT_MAPPING.hasDirectAssignment()) {
mspHelper.queryOutputAssignment(renderOutputMapping);
} else {
renderOutputMapping();
}
});
}

}

function renderOutputMapping() {
let isMR = FC.MIXER_CONFIG.platformType == PLATFORM.MULTIROTOR || FC.MIXER_CONFIG.platformType == PLATFORM.TRICOPTER;
let jsMap = FC.OUTPUT_MAPPING.getOutputTable(isMR, FC.MOTOR_RULES.getNumberOfConfiguredMotors(), FC.SERVO_RULES.getUsedServoIndexes());
let outputMap;
if (FC.OUTPUT_MAPPING.hasDirectAssignment()) {
outputMap = FC.OUTPUT_MAPPING.getOutputTableDirect();
for (let i = 0; i < Math.min(outputMap.length, jsMap.length); i++) {
if (outputMap[i] === '-' && jsMap[i] && jsMap[i] !== '-') outputMap[i] = jsMap[i]; // fill in default LED and beeper
}
} else {
outputMap = FC.OUTPUT_MAPPING.getOutputTable(
FC.MIXER_CONFIG.platformType == PLATFORM.MULTIROTOR || FC.MIXER_CONFIG.platformType == PLATFORM.TRICOPTER,
FC.MOTOR_RULES.getNumberOfConfiguredMotors(),
FC.SERVO_RULES.getUsedServoIndexes()
);
outputMap = jsMap;
}

for (let i = 1; i <= FC.OUTPUT_MAPPING.getOutputCount(); i++) {
Expand Down Expand Up @@ -861,7 +880,6 @@

renderOutputTable();
renderOutputMapping();
renderTimerOverride();

// Attempt to enhance output preview with firmware-authoritative assignments.
// Tab is already functional above; this re-renders if/when firmware responds.
Expand Down
Loading