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:
parent
b596c5fc76
commit
e4a85ccc2f
13 changed files with 930 additions and 3 deletions
30
src/Components/MotorOutputReordering/Body.html
Normal file
30
src/Components/MotorOutputReordering/Body.html
Normal 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>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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});
|
||||
}
|
72
src/Components/MotorOutputReordering/Styles.css
Normal file
72
src/Components/MotorOutputReordering/Styles.css
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: [],
|
||||
};
|
||||
|
|
|
@ -179,4 +179,6 @@ var MSPCodes = {
|
|||
|
||||
// MSPv2 Betaflight specific
|
||||
MSP2_BETAFLIGHT_BIND: 0x3000,
|
||||
MSP2_MOTOR_OUTPUT_REORDERING: 0x3001,
|
||||
MSP2_SET_MOTOR_OUTPUT_REORDERING: 0x3002,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue