Skip to content

Commit 3d5e4b2

Browse files
committed
fix: prevent Mixer tab hang and stale preview in direct assignment mode
Move loadOutputAssignment out of the mandatory load chain to avoid a permanent hang when a malformed packet drops the MSP callback. The tab now renders immediately with the JS fallback and updates once firmware responds (2 s timeout guard for safety). Add invalidateDirectAssignment() so that local motor/servo rule changes (preset load, add/remove rules, motor wizard) fall back to the JS algorithm rather than showing stale cached firmware data. Add LED and PINIO output type rendering in getOutputTableDirect() and clear directAssignments in flush() for clean reconnect state.
1 parent 8d51ce1 commit 3d5e4b2

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

js/outputMapping.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ var OutputMappingCollection = function () {
5252
directAssignments = [];
5353
}
5454

55+
// Discard cached data to force JS fallback on the next render — used when local
56+
// motor/servo rules change before being saved, so firmware data is stale.
57+
self.invalidateDirectAssignment = function() {
58+
directAssignments = [];
59+
}
60+
5561
self.setDirectAssignment = function(outputIndex, type, number) {
5662
directAssignments.push({ outputIndex, type, number });
5763
}
@@ -74,6 +80,10 @@ var OutputMappingCollection = function () {
7480
outputMap[displayIndex] = 'Motor ' + entry.number;
7581
} else if (entry.type === 2) {
7682
outputMap[displayIndex] = 'Servo ' + entry.number;
83+
} else if (entry.type === 3) {
84+
outputMap[displayIndex] = 'Led';
85+
} else if (entry.type === 4) {
86+
outputMap[displayIndex] = 'USER' + entry.number;
7787
}
7888
}
7989

@@ -207,6 +217,7 @@ var OutputMappingCollection = function () {
207217

208218
self.flush = function () {
209219
data = [];
220+
directAssignments = [];
210221
};
211222

212223
self.put = function (element) {

tabs/mixer.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ mixerTab.initialize = function (callback, scrollPosition) {
4141
mspHelper.loadMotorMixRules,
4242
mspHelper.loadOutputMappingExt,
4343
mspHelper.loadTimerOutputModes,
44-
mspHelper.loadOutputAssignment,
4544
mspHelper.loadLogicConditions,
4645
mspHelper.loadEzTune,
4746
]);
@@ -670,6 +669,7 @@ mixerTab.initialize = function (callback, scrollPosition) {
670669
}
671670

672671
renderMotorMixRules();
672+
FC.OUTPUT_MAPPING.invalidateDirectAssignment();
673673
renderOutputMapping();
674674

675675
motorWizardModal.close();
@@ -795,6 +795,7 @@ mixerTab.initialize = function (callback, scrollPosition) {
795795
FC.MIXER_CONFIG.hasFlaps = (currentMixerPreset.hasFlaps === true) ? true : false;
796796
renderServoMixRules();
797797
renderMotorMixRules();
798+
FC.OUTPUT_MAPPING.invalidateDirectAssignment();
798799
renderOutputMapping();
799800
updateRefreshButtonStatus();
800801
});
@@ -813,12 +814,14 @@ mixerTab.initialize = function (callback, scrollPosition) {
813814
$servoMixTableBody.on('click', "[data-role='role-servo-delete']", function (event) {
814815
FC.SERVO_RULES.drop($(event.currentTarget).attr("data-index"));
815816
renderServoMixRules();
817+
FC.OUTPUT_MAPPING.invalidateDirectAssignment();
816818
renderOutputMapping();
817819
});
818820

819821
$motorMixTableBody.on('click', "[data-role='role-motor-delete']", function (event) {
820822
FC.MOTOR_RULES.drop($(event.currentTarget).attr("data-index"));
821823
renderMotorMixRules();
824+
FC.OUTPUT_MAPPING.invalidateDirectAssignment();
822825
renderOutputMapping();
823826
});
824827

@@ -830,6 +833,7 @@ mixerTab.initialize = function (callback, scrollPosition) {
830833
if (FC.SERVO_RULES.hasFreeSlots()) {
831834
FC.SERVO_RULES.put(new ServoMixRule(FC.SERVO_RULES.getNextUnusedIndex(), 0, 100, 0));
832835
renderServoMixRules();
836+
FC.OUTPUT_MAPPING.invalidateDirectAssignment();
833837
renderOutputMapping();
834838
}
835839
});
@@ -838,6 +842,7 @@ mixerTab.initialize = function (callback, scrollPosition) {
838842
if (FC.MOTOR_RULES.hasFreeSlots()) {
839843
FC.MOTOR_RULES.put(new MotorMixRule(1, 0, 0, 0));
840844
renderMotorMixRules();
845+
FC.OUTPUT_MAPPING.invalidateDirectAssignment();
841846
renderOutputMapping();
842847
}
843848
});
@@ -855,6 +860,20 @@ mixerTab.initialize = function (callback, scrollPosition) {
855860
renderOutputMapping();
856861
renderTimerOverride();
857862

863+
// Attempt to enhance output preview with firmware-authoritative assignments.
864+
// Tab is already functional above; this re-renders if/when firmware responds.
865+
// The settled guard handles the edge case of a malformed packet causing no callback.
866+
{
867+
let settled = false;
868+
const onOutputAssignmentLoaded = function() {
869+
if (settled) return;
870+
settled = true;
871+
renderOutputMapping();
872+
};
873+
mspHelper.loadOutputAssignment(onOutputAssignmentLoaded);
874+
setTimeout(onOutputAssignmentLoaded, 2000);
875+
}
876+
858877
FC.LOGIC_CONDITIONS.init($('#logic-wrapper'));
859878

860879
i18n.localize();;

0 commit comments

Comments
 (0)