1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-24 16:55:24 +03:00

Motor output reordering feature

Fixed Sonar warnings

renaming MOTOR_REMAP to MOTOR_OUTPUT_REORDER<ING>

Sonar warning fix

Code style fixes after the code review

moving styles to css from the motors tab dialog

Dialog size of Androind devices

Raneming MSP_<SET>_MOTOR_OUTPUT_REORDERING to MSP2

removing old styles and js files reference to motor_remap folder

adding FC.* where needed to accomodate new master changes

fixed alphabetical order for FC settings MOTOR_OUTPUT_REORDER

css fix for Android for motor reordering dialog
This commit is contained in:
Ivan Efimov 2020-06-28 03:54:39 -05:00 committed by Ivan Efimov
parent b596c5fc76
commit e4a85ccc2f
13 changed files with 930 additions and 3 deletions

View file

@ -0,0 +1,30 @@
<div class="motorOutputReorderComponent">
<h3 i18n="motorsRemapDialogTitle" class="motorOutputReorderComponentHeader"></h3>
<div class="componentContent" id="dialogMotorOutputReorderMainContent">
<canvas id="motorOutputReorderCanvas"></canvas>
<div id="motorOutputReorderActionPanel">
<h4 id="motorOutputReorderActionHint"></h4>
</div>
<div id="motorOutputReorderSaveStartOverButtonsPanel">
<a href="#" id="motorsRemapDialogSave" class="regular-button left" i18n="motorsRemapDialogSave"></a>
<a href="#" id="motorsRemapDialogStartOver" class="regular-button left" i18n="motorsRemapDialogStartOver"></a>
</div>
</div>
<div class="componentContent" id="dialogMotorOutputReorderWarning">
<div class="notice">
<p class="motorsRemapDialogRiskNoticeText" i18n="motorsRemapDialogRiskNotice"></p>
<div class="motorsRemapToggleParentContainer">
<div class="motorsRemapToggleNarrow">
<input id="motorsEnableTestMode-dialogMotorOutputReorder" type="checkbox" class="toggle"/>
</div>
<div class="motorsRemapToggleWide">
<span class="motorsEnableTestMode motorsRemapDialogRiskNoticeText" i18n="motorsRemapDialogUnderstandRisks"></span>
</div>
</div>
<div class="motorsRemapDialogRExplanationText" i18n="motorsRemapDialogExplanations"></div>
</div>
<div class="buttons">
<a href="#" id="dialogMotorOutputReorderAgreeButton" class="regular-button" i18n="motorOutputReorderDialogAgree"></a>
</div>
</div>
</div>

View file

@ -0,0 +1,269 @@
'use strict';
class MotorOutputReorderCanvas
{
constructor(canvas, droneConfiguration, motorClickCallback, spinMotorCallback)
{
this._spinMotorCallback = spinMotorCallback;
this._canvas = canvas;
this._motorClickCallback = motorClickCallback;
this._width = this._canvas.width();
this._height = this._canvas.height();
this._screenSize = Math.min(this._width, this._height);
this._config = new MotorOutputReorderConfig(this._screenSize);
// no component resize allowing yet
this._canvas.prop({
width: this._width,
height: this._height,
});
this._droneConfiguration = droneConfiguration;
this._ctx = this._canvas[0].getContext("2d");
this._ctx.translate(this._width / 2, this._height / 2);
this._canvas.mousemove((event) =>
{
this._onMouseMove(event);
});
this._canvas.mouseleave(() =>
{
this._onMouseLeave();
});
this._canvas.mousedown(() =>
{
this._onMouseDown();
});
this._canvas.mouseup(() =>
{
this._onMouseUp(event);
});
this._canvas.click(() =>
{
this._onMouseClick();
});
this.startOver();
}
pause()
{
this._keepDrawing = false;
}
startOver()
{
this.readyMotors = []; //motors that already being selected for remapping by user
this.remappingReady = false;
this._motorIndexToSpinOnMouseDown = -1;
this._keepDrawing = true;
this._mouse = {x : 0, y: 0};
window.requestAnimationFrame(() =>
{
this._drawOnce();
});
}
_drawOnce()
{
this._ctx.clearRect(-this._width / 2, -this._height / 2, this._width, this._height);
this._drawFrame();
this._drawDirectionArrow();
this._markMotors();
this._drawMotors();
if (this._keepDrawing) {
window.requestAnimationFrame(() =>
{
this._drawOnce();
});
}
}
_onMouseDown()
{
if (this.remappingReady) {
this._motorIndexToSpinOnMouseDown = this._getMouseHoverMotorIndex();
if (this._spinMotorCallback) {
this._spinMotorCallback(this._motorIndexToSpinOnMouseDown);
}
}
}
_onMouseUp()
{
if (-1 !== this._motorIndexToSpinOnMouseDown) {
this._motorIndexToSpinOnMouseDown = -1;
if (this._spinMotorCallback) {
this._spinMotorCallback(this._motorIndexToSpinOnMouseDown);
}
}
}
_onMouseClick()
{
const motorIndex = this._getMouseHoverMotorIndex();
if (this._motorClickCallback && -1 !== motorIndex && !this.readyMotors.includes(motorIndex)) {
this._motorClickCallback(motorIndex);
}
}
_onMouseMove(event)
{
const boundingRect = this._canvas[0].getBoundingClientRect();
this._mouse.x = event.clientX - boundingRect.left - this._width / 2;
this._mouse.y = event.clientY - boundingRect.top - this._height / 2;
}
_onMouseLeave()
{
this._mouse.x = Number.MIN_SAFE_INTEGER;
this._mouse.y = Number.MIN_SAFE_INTEGER;
if (-1 !== this._motorIndexToSpinOnMouseDown) {
this._motorIndexToSpinOnMouseDown = -1;
if (this._spinMotorCallback) {
this._spinMotorCallback(this._motorIndexToSpinOnMouseDown);
}
}
}
_markMotors()
{
const motors = this._config[this._droneConfiguration].Motors;
const mouseHoverMotorIndex = this._getMouseHoverMotorIndex();
if (-1 === this._motorIndexToSpinOnMouseDown) {
for (let i = 0; i < this.readyMotors.length; i++) {
const motorIndex = this.readyMotors[i];
this._ctx.beginPath();
this._ctx.arc(motors[motorIndex].x, motors[motorIndex].y, this._config[this._droneConfiguration].PropRadius, 0, 2 * Math.PI);
this._ctx.closePath();
this._ctx.fillStyle = this._config.MotorReadyColor;
this._ctx.fill();
}
if (-1 !== mouseHoverMotorIndex && !this.readyMotors.includes(mouseHoverMotorIndex)) {
this._ctx.beginPath();
this._ctx.arc(motors[mouseHoverMotorIndex].x, motors[mouseHoverMotorIndex].y, this._config[this._droneConfiguration].PropRadius, 0, 2 * Math.PI);
this._ctx.closePath();
this._ctx.fillStyle = this._config.MotorMouseHoverColor;
this._ctx.fill();
}
} else {
const spinningMotor = this._motorIndexToSpinOnMouseDown;
for (let i = 0; i < motors.length; i++) {
this._ctx.fillStyle = this._config.MotorReadyColor;
if (spinningMotor === i) {
this._ctx.fillStyle = this._config.MotorSpinningColor;
} else if (mouseHoverMotorIndex === i) {
this._ctx.fillStyle = this._config.MotorMouseHoverColor;
}
this._ctx.beginPath();
this._ctx.arc(motors[i].x, motors[i].y, this._config[this._droneConfiguration].PropRadius, 0, 2 * Math.PI);
this._ctx.closePath();
this._ctx.fill();
}
}
}
_getMouseHoverMotorIndex()
{
const x = this._mouse.x;
const y = this._mouse.y;
let result = -1;
let currentDist = Number.MAX_SAFE_INTEGER;
const motors = this._config[this._droneConfiguration].Motors;
for (let i = 0; i < motors.length; i++) {
const dist = Math.sqrt((x - motors[i].x) * (x - motors[i].x) + (y - motors[i].y) * (y - motors[i].y));
if (dist < this._config[this._droneConfiguration].PropRadius && dist < currentDist) {
currentDist = dist;
result = i;
}
}
return result;
}
_drawMotors()
{
this._ctx.lineWidth = this._config.PropEdgeLineWidth;
this._ctx.strokeStyle = this._config.PropEdgeColor;
const motors = this._config[this._droneConfiguration].Motors;
for (let i = 0; i < motors.length; i++) {
this._ctx.beginPath();
this._ctx.arc(motors[i].x, motors[i].y, this._config[this._droneConfiguration].PropRadius, 0, 2 * Math.PI);
this._ctx.stroke();
}
}
_drawDirectionArrow()
{
this._ctx.beginPath();
this._ctx.moveTo(this._config.DirectionArrowPoints[0].x, this._config.DirectionArrowPoints[0].y);
for (let i = 1; i < this._config.DirectionArrowPoints.length; i++) {
this._ctx.lineTo(this._config.DirectionArrowPoints[i].x, this._config.DirectionArrowPoints[i].y);
}
this._ctx.closePath();
this._ctx.fillStyle = this._config.ArrowColor;
this._ctx.fill();
}
_drawFrame()
{
this._ctx.beginPath();
this._ctx.lineWidth = this._config[this._droneConfiguration].ArmWidth;
this._ctx.lineCap = "round";
this._ctx.strokeStyle = this._config.FrameColor;
const motors = this._config[this._droneConfiguration].Motors;
switch(this._droneConfiguration) {
case "Quad X":
case "Quad +":
this._ctx.moveTo(motors[0].x, motors[0].y);
this._ctx.lineTo(motors[3].x, motors[3].y);
this._ctx.moveTo(motors[1].x, motors[1].y);
this._ctx.lineTo(motors[2].x, motors[2].y);
break;
case "Quad X 1234":
this._ctx.moveTo(motors[0].x, motors[0].y);
this._ctx.lineTo(motors[2].x, motors[2].y);
this._ctx.moveTo(motors[3].x, motors[3].y);
this._ctx.lineTo(motors[1].x, motors[1].y);
break;
case "Tricopter":
this._ctx.moveTo(motors[1].x, motors[1].y);
this._ctx.lineTo(motors[2].x, motors[2].y);
this._ctx.moveTo(motors[0].x, motors[0].y);
this._ctx.lineTo(motors[0].x, motors[2].y);
break;
case "Hex +":
case "Hex X":
this._ctx.moveTo(motors[0].x, motors[0].y);
this._ctx.lineTo(motors[3].x, motors[3].y);
this._ctx.moveTo(motors[1].x, motors[1].y);
this._ctx.lineTo(motors[2].x, motors[2].y);
this._ctx.moveTo(motors[4].x, motors[4].y);
this._ctx.lineTo(motors[5].x, motors[5].y);
break;
}
this._ctx.stroke();
}
}

View file

@ -0,0 +1,258 @@
'use strict';
class MotorOutputReorderComponent
{
constructor(contentDiv, onLoadedCallback, droneConfiguration, motorStopValue, motorSpinValue)
{
this._contentDiv = contentDiv;
this._onLoadedCallback = onLoadedCallback;
this._droneConfiguration = droneConfiguration;
this._motorStopValue = motorStopValue;
this._motorSpinValue = motorSpinValue;
this._config = new MotorOutputReorderConfig(100);
this._currentJerkingTimeout = -1;
this._currentJerkingMotor = -1;
this._currentSpinningMotor = -1;
this._contentDiv.load("./Components/MotorOutputReordering/Body.html", () =>
{
this._setupdialog();
});
}
_readDom()
{
this._domAgreeSafetyCheckBox = $('#motorsEnableTestMode-dialogMotorOutputReorder');
this._domAgreeButton = $('#dialogMotorOutputReorderAgreeButton');
this._domStartOverButton = $('#motorsRemapDialogStartOver');
this._domSaveButton = $('#motorsRemapDialogSave');
this._domMainContentBlock = $('#dialogMotorOutputReorderMainContent');
this._domWarningContentBlock = $('#dialogMotorOutputReorderWarning');
this._domActionHintBlock = $('#motorOutputReorderActionHint');
this._domCanvas = $('#motorOutputReorderCanvas');
}
_setupdialog()
{
i18n.localizePage();
this._readDom();
this._resetGui();
this._domAgreeSafetyCheckBox.change(() =>
{
const enabled = this._domAgreeSafetyCheckBox.is(':checked');
this._domAgreeButton.toggle(enabled);
});
this._domAgreeButton.click(() =>
{
this._onAgreeButtonClicked();
});
this._domStartOverButton.click(() =>
{
this._startOver();
});
this._domSaveButton.click(() =>
{
this._save();
});
this._onLoadedCallback();
}
close()
{
this._stopAnyMotorJerking();
this._stopMotor();
this._stopUserInteraction();
this._resetGui();
}
_resetGui()
{
this._domMainContentBlock.hide();
this._domWarningContentBlock.show();
this._domAgreeButton.hide();
this._domAgreeSafetyCheckBox.prop('checked', false);
this._domAgreeSafetyCheckBox.change();
this._showSaveStartOverButtons(false);
}
_save()
{
function save_to_eeprom()
{
MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, reboot);
}
function reboot()
{
GUI.log(i18n.getMessage('configurationEepromSaved'));
GUI.tab_switch_cleanup(function()
{
MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false);
reinitialiseConnection(self);
});
}
FC.MOTOR_OUTPUT_ORDER = Array.from(this._newMotorOutputReorder);
MSP.send_message(MSPCodes.MSP2_SET_MOTOR_OUTPUT_REORDERING, mspHelper.crunch(MSPCodes.MSP2_SET_MOTOR_OUTPUT_REORDERING));
save_to_eeprom();
}
_calculateNewMotorOutputReorder()
{
this._newMotorOutputReorder = [];
for (let i = 0; i < this.motorOutputReorderCanvas.readyMotors.length; i++) {
this._newMotorOutputReorder.push(this._remapMotorIndex(i));
}
}
_remapMotorIndex(motorIndex)
{
return FC.MOTOR_OUTPUT_ORDER[this.motorOutputReorderCanvas.readyMotors.indexOf(motorIndex)];
}
_startOver()
{
this._showSaveStartOverButtons(false);
this._startUserInteraction();
}
_showSaveStartOverButtons(show)
{
if (show) {
this._domStartOverButton.show();
this._domSaveButton.show();
} else {
this._domStartOverButton.hide();
this._domSaveButton.hide();
}
}
_onAgreeButtonClicked()
{
this._domActionHintBlock.text(i18n.getMessage("motorOutputReorderDialogSelectSpinningMotor"));
this._domWarningContentBlock.hide();
this._domMainContentBlock.show();
this._startUserInteraction();
}
_stopUserInteraction()
{
if (this.motorOutputReorderCanvas) {
this.motorOutputReorderCanvas.pause();
}
}
_startUserInteraction()
{
if (this.motorOutputReorderCanvas) {
this.motorOutputReorderCanvas.startOver();
} else {
this.motorOutputReorderCanvas = new MotorOutputReorderCanvas(this._domCanvas,
this._droneConfiguration,
(motorIndex) =>
{ // motor click callback
this._onMotorClick(motorIndex);
},
(motorIndex) =>
{ // motor spin callback
let indexToSpin = -1;
if (-1 !== motorIndex) {
indexToSpin = this.motorOutputReorderCanvas.readyMotors.indexOf(motorIndex);
}
this._spinMotor(indexToSpin);
},
);
}
this._startMotorJerking(0);
}
_stopAnyMotorJerking()
{
if (-1 !== this._currentJerkingTimeout) {
clearTimeout(this._currentJerkingTimeout);
this._currentJerkingTimeout = -1;
this._spinMotor(-1);
}
this._currentJerkingMotor = -1;
}
_startMotorJerking(motorIndex)
{
this._stopAnyMotorJerking();
this._currentJerkingMotor = motorIndex;
this._motorStartTimeout(motorIndex);
}
_motorStartTimeout(motorIndex)
{
this._spinMotor(motorIndex);
this._currentJerkingTimeout = setTimeout(() =>
{
this._motorStopTimeout(motorIndex);
}, 250);
}
_motorStopTimeout(motorIndex)
{
this._spinMotor(-1);
this._currentJerkingTimeout = setTimeout(() =>
{
this._motorStartTimeout(motorIndex);
}, 500);
}
_spinMotor(motorIndex)
{
this._currentSpinningMotor = motorIndex;
const buffer = [];
for (let i = 0; i < this._config[this._droneConfiguration].Motors.length; i++) {
if (i === motorIndex) {
buffer.push16(this._motorSpinValue);
} else {
buffer.push16(this._motorStopValue);
}
}
MSP.send_message(MSPCodes.MSP_SET_MOTOR, buffer);
}
_stopMotor()
{
if (-1 !== this._currentSpinningMotor) {
this._spinMotor(-1);
}
}
_onMotorClick(motorIndex)
{
this.motorOutputReorderCanvas.readyMotors.push(motorIndex);
this._currentJerkingMotor ++;
if (this._currentJerkingMotor < this._config[this._droneConfiguration].Motors.length) {
this._startMotorJerking(this._currentJerkingMotor);
} else {
this._stopAnyMotorJerking();
this._domActionHintBlock.text(i18n.getMessage("motorOutputReorderDialogRemapIsDone"));
this._calculateNewMotorOutputReorder();
this.motorOutputReorderCanvas.remappingReady = true;
this._showSaveStartOverButtons(true);
}
}
}

View file

@ -0,0 +1,140 @@
'use strict';
function MotorOutputReorderConfig(screenSize)
{
this.FrameColor = 'rgb(186, 186, 186)';
this.PropEdgeColor = 'rgb(255, 187, 0)';
this.PropEdgeLineWidth = 3;
this.MotorNumberTextFont = `${screenSize * 0.1}px 'Open Sans', 'Segoe UI', Tahoma, sans-serif`;
this.MotorNumberTextColor = 'rgb(0, 0, 0)';
this.MotorMouseHoverColor = 'rgba(255, 187, 0, 0.4)';
this.MotorSpinningColor = 'rgba(255, 0, 0, 0.4)';
this.MotorReadyColor = 'rgba(0,128,0,0.4)';
this.ArrowColor = 'rgb(182,67,67)';
this.DirectionArrowPoints = [
{x: -0.03 * screenSize, y: 0.11 * screenSize},
{x: -0.03 * screenSize, y: -0.01 * screenSize},
{x: -0.07 * screenSize, y: -0.01 * screenSize},
{x: 0.0 * screenSize, y: -0.13 * screenSize},
{x: 0.07 * screenSize, y: -0.01 * screenSize},
{x: 0.03 * screenSize, y: -0.01 * screenSize},
{x: 0.03 * screenSize, y: 0.11 * screenSize},
];
//===========================================
let frameRaduis = 0.28 * screenSize;
this["Quad X"] =
{
PropRadius: 0.2 * screenSize,
ArmWidth: 0.1 * screenSize,
Motors:
[
{x: frameRaduis, y: frameRaduis},
{x: frameRaduis, y: -frameRaduis},
{x: -frameRaduis, y: frameRaduis},
{x: -frameRaduis, y: -frameRaduis},
],
};
//===========================================
frameRaduis = 0.28 * screenSize;
this["Quad X 1234"] =
{
PropRadius: 0.2 * screenSize,
ArmWidth: 0.1 * screenSize,
Motors:
[
{x: -frameRaduis, y: -frameRaduis},
{x: frameRaduis, y: -frameRaduis},
{x: frameRaduis, y: frameRaduis},
{x: -frameRaduis, y: frameRaduis},
],
};
//===========================================
frameRaduis = 0.32 * screenSize;
this["Quad +"] =
{
PropRadius: 0.15 * screenSize,
ArmWidth: 0.1 * screenSize,
Motors:
[
{x: 0, y: frameRaduis},
{x: frameRaduis, y: 0 },
{x: -frameRaduis, y: 0 },
{x: 0, y: -frameRaduis},
],
};
//===========================================
frameRaduis = 0.30 * screenSize;
this["Tricopter"] =
{
PropRadius: 0.15 * screenSize,
ArmWidth: 0.1 * screenSize,
Motors:
[
{x: 0, y: frameRaduis},
{x: frameRaduis, y: -frameRaduis},
{x: -frameRaduis, y: -frameRaduis},
],
};
//===========================================
frameRaduis = 0.35 * screenSize;
this["Hex +"] =
{
PropRadius: 0.14 * screenSize,
ArmWidth: 0.1 * screenSize,
Motors: [],
};
let dAngle = Math.PI / 3;
let angle = 0;
angle = dAngle * 1;
this["Hex +"].Motors.push({x: Math.sin(angle) * frameRaduis, y: Math.cos(angle) * frameRaduis});
angle = dAngle * 2;
this["Hex +"].Motors.push({x: Math.sin(angle) * frameRaduis, y: Math.cos(angle) * frameRaduis});
angle = -dAngle * 1;
this["Hex +"].Motors.push({x: Math.sin(angle) * frameRaduis, y: Math.cos(angle) * frameRaduis});
angle = -dAngle * 2;
this["Hex +"].Motors.push({x: Math.sin(angle) * frameRaduis, y: Math.cos(angle) * frameRaduis});
angle = dAngle * 3;
this["Hex +"].Motors.push({x: Math.sin(angle) * frameRaduis, y: Math.cos(angle) * frameRaduis});
angle = dAngle * 0;
this["Hex +"].Motors.push({x: Math.sin(angle) * frameRaduis, y: Math.cos(angle) * frameRaduis});
//===========================================
frameRaduis = 0.35 * screenSize;
this["Hex X"] =
{
PropRadius: 0.14 * screenSize,
ArmWidth: 0.1 * screenSize,
Motors: [],
};
dAngle = Math.PI / 3;
angle = dAngle * 1;
this["Hex X"].Motors.push({x: Math.cos(angle) * frameRaduis, y: Math.sin(angle) * frameRaduis});
angle = -dAngle * 1;
this["Hex X"].Motors.push({x: Math.cos(angle) * frameRaduis, y: Math.sin(angle) * frameRaduis});
angle = dAngle * 2;
this["Hex X"].Motors.push({x: Math.cos(angle) * frameRaduis, y: Math.sin(angle) * frameRaduis});
angle = -dAngle * 2;
this["Hex X"].Motors.push({x: Math.cos(angle) * frameRaduis, y: Math.sin(angle) * frameRaduis});
angle = dAngle * 0;
this["Hex X"].Motors.push({x: Math.cos(angle) * frameRaduis, y: Math.sin(angle) * frameRaduis});
angle = dAngle * 3;
this["Hex X"].Motors.push({x: Math.cos(angle) * frameRaduis, y: Math.sin(angle) * frameRaduis});
}

View file

@ -0,0 +1,72 @@
.motorOutputReorderComponent {
display:flex;
height:100%;
flex-flow:column;
}
.motorOutputReorderComponentHeader {
border-bottom: 1px solid var(--accent);
margin-bottom: 10px;
padding-bottom: 12px;
}
#dialogMotorOutputReorderMainContent {
display:flex;
height:100%;
flex-flow:column;
}
#dialogMotorOutputReorderWarning {
display:flex;
height:100%;
flex-flow:column;
}
#motorOutputReorderCanvas {
width:100%;
flex-grow: 1;
}
#motorOutputReorderActionPanel {
height: 46px;
}
#dialogMotorOutputReorderSave {
margin-right: 0px;
}
.motorsRemapToggleParentContainer {
display: flex;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
#motorOutputReorderActionHint {
margin-top: 1.0em;
display: inline-block;
}
.motorsRemapToggleNarrow {
margin-right: 12px;
display: flex;
align-items: center;
}
.motorsRemapToggleWide {
flex: 1;
}
.motorsRemapDialogRiskNoticeText {
font-size: 1.2em;
}
.motorsRemapDialogRExplanationText {
font-size: 1.0em;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
#motorOutputReorderSaveStartOverButtonsPanel {
position: absolute;
bottom: 4px;
}

View file

@ -18,6 +18,27 @@
float: left;
}
.tab-motors #dialogMotorOutputReorder-closebtn {
margin-right: 0px;
margin-bottom: 0px;
}
.tab-motors #dialogMotorOutputReorder {
width: 400px;
height:440px
;}
.tab-motors #dialogMotorOutputReorderContentWrapper {
display: flex;
flex-flow: column;
width: 100%;
height: 100%;
}
.tab-motors #dialogMotorOutputReorderContent {
flex-grow: 1;
}
.tab-motors .mixerPreview img {
width: 120px;
height: 120px;
@ -424,4 +445,15 @@
.tab-motors .motor_testing .telemetry li {
font-size: 6px;
}
.tab-motors #dialogMotorOutputReorder {
position: fixed;
width: calc(100% - 2em);
bottom: 0;
top: 56px;
border-radius: unset;
border: none;
overflow: auto;
margin: 0;
height: auto;
}
}

View file

@ -41,6 +41,7 @@ const FC = {
MOTOR_3D_CONFIG: null,
MOTOR_CONFIG: null,
MOTOR_DATA: null,
MOTOR_OUTPUT_ORDER: null,
MOTOR_TELEMETRY_DATA: null,
MULTIPLE_MSP: null,
PID: null,
@ -605,6 +606,8 @@ const FC = {
vtxtable_powerlevel_label: 0,
};
this.MOTOR_OUTPUT_ORDER = [];
this.MULTIPLE_MSP = {
msp_commands: [],
};

View file

@ -179,4 +179,6 @@ var MSPCodes = {
// MSPv2 Betaflight specific
MSP2_BETAFLIGHT_BIND: 0x3000,
MSP2_MOTOR_OUTPUT_REORDERING: 0x3001,
MSP2_SET_MOTOR_OUTPUT_REORDERING: 0x3002,
};

View file

@ -147,6 +147,13 @@ MspHelper.prototype.process_data = function(dataHandler) {
FC.MOTOR_DATA[i] = data.readU16();
}
break;
case MSPCodes.MSP2_MOTOR_OUTPUT_REORDERING:
FC.MOTOR_OUTPUT_ORDER = [];
const arraySize = data.read8();
for (let i = 0; i < arraySize; i++) {
FC.MOTOR_OUTPUT_ORDER[i] = data.readU8();
}
break;
case MSPCodes.MSP_MOTOR_TELEMETRY:
var telemMotorCount = data.readU8();
for (let i = 0; i < telemMotorCount; i++) {
@ -1549,6 +1556,9 @@ MspHelper.prototype.process_data = function(dataHandler) {
case MSPCodes.MSP_SET_RTC:
console.log('Real time clock set');
break;
case MSPCodes.MSP2_SET_MOTOR_OUTPUT_REORDERING:
console.log('Motor output reordering set');
break;
case MSPCodes.MSP_MULTIPLE_MSP:
@ -2249,6 +2259,15 @@ MspHelper.prototype.crunch = function(code) {
break;
case MSPCodes.MSP2_SET_MOTOR_OUTPUT_REORDERING:
buffer.push8(FC.MOTOR_OUTPUT_ORDER.length);
for (let i = 0; i < FC.MOTOR_OUTPUT_ORDER.length; i++) {
buffer.push8(FC.MOTOR_OUTPUT_ORDER[i]);
}
break;
default:
return false;
}

View file

@ -57,7 +57,11 @@ TABS.motors.initialize = function (callback) {
}
function load_esc_protocol() {
MSP.send_message(MSPCodes.MSP_ADVANCED_CONFIG, false, false, load_motor_data);
MSP.send_message(MSPCodes.MSP_ADVANCED_CONFIG, false, false, load_motor_output_reordering);
}
function load_motor_output_reordering() {
MSP.send_message(MSPCodes.MSP2_MOTOR_OUTPUT_REORDERING, false, false, load_motor_data);
}
function load_motor_data() {
@ -222,6 +226,13 @@ TABS.motors.initialize = function (callback) {
}
$('.mixerPreview img').attr('src', './resources/motor_order/' + mixerList[mixer - 1].image + reverse + '.svg');
const motorOutputReorderConfig = new MotorOutputReorderConfig(100);
const domMotorOutputReorderDialogOpen = $('#motorOutputReorderDialogOpen');
const isMotorReorderingAvailable = (mixerList[mixer - 1].name in motorOutputReorderConfig)
&& (FC.MOTOR_OUTPUT_ORDER) && (FC.MOTOR_OUTPUT_ORDER.length > 0);
domMotorOutputReorderDialogOpen.toggle(isMotorReorderingAvailable);
}
function process_html() {
@ -726,7 +737,50 @@ TABS.motors.initialize = function (callback) {
// enable Status and Motor data pulling
GUI.interval_add('motor_and_status_pull', get_status, 50, true);
GUI.content_ready(callback);
let zeroThrottleValue = rangeMin;
if (self.feature3DEnabled) {
zeroThrottleValue = neutral3d;
}
setup_motor_output_reordering_dialog(content_ready, zeroThrottleValue);
function content_ready() {
GUI.content_ready(callback);
}
GUI.content_ready(callback);
}
function setup_motor_output_reordering_dialog(callbackFunction, zeroThrottleValue)
{
const domDialogMotorOutputReorder = $('#dialogMotorOutputReorder');
const motorOutputReorderComponent = new MotorOutputReorderComponent($('#dialogMotorOutputReorderContent'),
callbackFunction, mixerList[FC.MIXER_CONFIG.mixer - 1].name,
zeroThrottleValue, zeroThrottleValue + 200);
$('#dialogMotorOutputReorder-closebtn').click(closeDialog);
function closeDialog()
{
domDialogMotorOutputReorder[0].close();
motorOutputReorderComponent.close();
$(document).off("keydown", onDocumentKeyPress);
}
function onDocumentKeyPress(event)
{
if (27 === event.which) {
closeDialog();
}
}
$('#motorOutputReorderDialogOpen').click(function()
{
$(document).on("keydown", onDocumentKeyPress);
domDialogMotorOutputReorder[0].showModal();
});
}
};

View file

@ -38,6 +38,7 @@
<link type="text/css" rel="stylesheet" href="./css/dropdown-lists/css/style_lists.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./js/libraries/switchery/switchery.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./node_modules/@fortawesome/fontawesome-free/css/all.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./Components/MotorOutputReordering/Styles.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./css/dark-theme.css" media="all" disabled/>
@ -135,6 +136,9 @@
<script type="text/javascript" src="./js/TuningSliders.js"></script>
<script type="text/javascript" src="./js/phones_ui.js"></script>
<script type="text/javascript" src="./node_modules/jquery-touchswipe/jquery.touchSwipe.min.js"></script>
<script type="text/javascript" src="./Components/MotorOutputReordering/MotorOutputReorderingComponent.js"></script>
<script type="text/javascript" src="./Components/MotorOutputReordering/MotorOutputReorderingCanvas.js"></script>
<script type="text/javascript" src="./Components/MotorOutputReordering/MotorOutputReorderingConfig.js"></script>
<title i18n="windowTitle"></title>
</head>
<body>

View file

@ -99,8 +99,8 @@
</div>
</div>
</div>
<div class="power_info">
<a href="#" id="motorOutputReorderDialogOpen" class="regular-button" i18n="motorOutputReorderDialogOpen"></a>
<span i18n="motorsVoltage" class="power_text"></span><span class="motors-bat-voltage power_value"></span>
<span i18n="motorsADrawing" class="power_text"></span><span class="motors-bat-mah-drawing power_value"></span>
<span i18n="motorsmAhDrawn" class="power_text"></span><span class="motors-bat-mah-drawn power_value"></span>
@ -190,4 +190,14 @@
</div>
<div class="clear-both"></div>
</div>
<dialog id="dialogMotorOutputReorder">
<div id="dialogMotorOutputReorderContentWrapper">
<div id="dialogMotorOutputReorderContent">
</div>
<div>
<a href="#" id="dialogMotorOutputReorder-closebtn" class="regular-button right" i18n="motorOutputReorderDialogClose"></a>
</div>
</div>
</dialog>
</div>