1
0
Fork 0
mirror of https://github.com/iNavFlight/inav-configurator.git synced 2025-07-16 04:45:18 +03:00

Merge pull request #604 from nmaggioni/cli_rebase

Rebase CLI tab on latest CF
This commit is contained in:
Konstantin Sharlaimov 2018-12-10 11:37:41 +01:00 committed by GitHub
commit 08d50f7fee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 326 additions and 105 deletions

View file

@ -1441,6 +1441,21 @@
"cliReboot": { "cliReboot": {
"message": "CLI reboot detected" "message": "CLI reboot detected"
}, },
"cliSaveToFileBtn": {
"message": "Save to File"
},
"cliSaveToFileFailed": {
"message": "Failed to save CLI output to file"
},
"cliSaveToFileAborted": {
"message": "Saving CLI output to file was aborted"
},
"cliSaveToFileCompleted": {
"message": "CLI output successfully saved to file"
},
"cliClearOutputHistoryBtn": {
"message": "Clear output history"
},
"loggingNote": { "loggingNote": {
"message": "Data will be logged in this tab <span style=\"color: red\">only</span>, leaving the tab will <span style=\"color: red\">cancel</span> logging and application will return to its normal <strong>\"configurator\"</strong> state.<br /> You are free to select the global update period, data will be written into the log file every <strong>1</strong> second for performance reasons." "message": "Data will be logged in this tab <span style=\"color: red\">only</span>, leaving the tab will <span style=\"color: red\">cancel</span> logging and application will return to its normal <strong>\"configurator\"</strong> state.<br /> You are free to select the global update period, data will be written into the log file every <strong>1</strong> second for performance reasons."

View file

@ -103,7 +103,8 @@ var FC = {
battery_profile: 0, battery_profile: 0,
uid: [0, 0, 0], uid: [0, 0, 0],
accelerometerTrims: [0, 0], accelerometerTrims: [0, 0],
armingFlags: 0 armingFlags: 0,
name: ''
}; };
BF_CONFIG = { BF_CONFIG = {
@ -567,7 +568,7 @@ var FC = {
features.push( features.push(
{bit: 28, group: 'esc-priority', name: 'PWM_OUTPUT_ENABLE', haveTip: true} {bit: 28, group: 'esc-priority', name: 'PWM_OUTPUT_ENABLE', haveTip: true}
); );
/* /*
* Transponder disabled until not implemented in firmware * Transponder disabled until not implemented in firmware
*/ */
@ -1068,8 +1069,8 @@ var FC = {
return { return {
0: "Land", 0: "Land",
1: "Drop", 1: "Drop",
2: "RTH", 2: "RTH",
3: "Do Nothing", 3: "Do Nothing",
} }
}, },
getRcMapLetters: function () { getRcMapLetters: function () {

View file

@ -12,4 +12,37 @@ function constrain(input, min, max) {
} }
return input; return input;
}
function zeroPad(value, width) {
value = "" + value;
while (value.length < width) {
value = "0" + value;
}
return value;
}
function generateFilename(prefix, suffix) {
var date = new Date();
var filename = prefix;
if (CONFIG) {
if (CONFIG.flightControllerIdentifier) {
filename = CONFIG.flightControllerIdentifier + '_' + filename;
}
if (CONFIG.name && CONFIG.name.trim() !== '') {
filename = filename + '_' + CONFIG.name.trim().replace(' ', '_');
}
}
filename = filename + '_' + date.getFullYear()
+ zeroPad(date.getMonth() + 1, 2)
+ zeroPad(date.getDate(), 2)
+ '_' + zeroPad(date.getHours(), 2)
+ zeroPad(date.getMinutes(), 2)
+ zeroPad(date.getSeconds(), 2);
return filename + '.' + suffix;
} }

View file

@ -525,7 +525,7 @@ var mspHelper = (function (gui) {
break; break;
case MSPCodes.MSP_SET_SERVO_MIX_RULE: case MSPCodes.MSP_SET_SERVO_MIX_RULE:
console.log("Servo mix saved"); console.log("Servo mix saved");
break; break;
@ -1345,6 +1345,11 @@ var mspHelper = (function (gui) {
console.log('OSD char uploaded'); console.log('OSD char uploaded');
break; break;
case MSPCodes.MSP_NAME: case MSPCodes.MSP_NAME:
CONFIG.name = '';
var char;
while ((char = data.readU8()) !== null) {
CONFIG.name += String.fromCharCode(char);
}
break; break;
case MSPCodes.MSP_SET_NAME: case MSPCodes.MSP_SET_NAME:
console.log("Craft name set"); console.log("Craft name set");
@ -1420,7 +1425,7 @@ var mspHelper = (function (gui) {
case MSPCodes.MSP2_INAV_SET_MC_BRAKING: case MSPCodes.MSP2_INAV_SET_MC_BRAKING:
console.log('Braking config saved'); console.log('Braking config saved');
break; break;
default: default:
console.log('Unknown code detected: ' + dataHandler.code); console.log('Unknown code detected: ' + dataHandler.code);
} else { } else {
@ -2186,7 +2191,7 @@ var mspHelper = (function (gui) {
// send one at a time, with index // send one at a time, with index
var servoRule = SERVO_RULES.get()[servoIndex]; var servoRule = SERVO_RULES.get()[servoIndex];
buffer.push(servoIndex); buffer.push(servoIndex);
buffer.push(servoRule.getTarget()); buffer.push(servoRule.getTarget());
buffer.push(servoRule.getInput()); buffer.push(servoRule.getInput());

View file

@ -306,7 +306,12 @@ function onOpen(openInfo) {
googleAnalytics.sendEvent('Firmware', 'Variant', CONFIG.flightControllerIdentifier + ',' + CONFIG.flightControllerVersion); googleAnalytics.sendEvent('Firmware', 'Variant', CONFIG.flightControllerIdentifier + ',' + CONFIG.flightControllerVersion);
GUI.log(chrome.i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion])); GUI.log(chrome.i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion]));
if (semver.gte(CONFIG.flightControllerVersion, CONFIGURATOR.firmwareVersionAccepted)) { if (semver.gte(CONFIG.flightControllerVersion, CONFIGURATOR.firmwareVersionAccepted)) {
onValidFirmware(); mspHelper.getCraftName(function(name) {
if (name) {
CONFIG.name = name;
}
onValidFirmware();
});
} else { } else {
onInvalidFirmwareVersion(); onInvalidFirmwareVersion();
} }

View file

@ -3,7 +3,7 @@
} }
.tab-cli .content_wrapper { .tab-cli .content_wrapper {
height: calc(100% - 50px); height: calc(100% - 92px);
} }
.tab-cli p { .tab-cli p {
@ -56,4 +56,19 @@
.tab-cli .window .wrapper { .tab-cli .window .wrapper {
white-space: pre-wrap; white-space: pre-wrap;
}
.tab-cli .save {
color: white;
}
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
.tab-cli .content_wrapper {
height: calc(100% - 87px);
}
.tab-cli .content_toolbar {
margin-top: 5px;
}
} }

View file

@ -5,6 +5,7 @@
<p i18n="cliInfo"></p> <p i18n="cliInfo"></p>
</div> </div>
</div> </div>
<div class="backdrop"> <div class="backdrop">
<div class="window"> <div class="window">
<div class="wrapper"></div> <div class="wrapper"></div>
@ -12,4 +13,10 @@
</div> </div>
<textarea name="commands" i18n_placeholder="cliInputPlaceholder" rows="1" cols="0"></textarea> <textarea name="commands" i18n_placeholder="cliInputPlaceholder" rows="1" cols="0"></textarea>
</div> </div>
</div> <div class="content_toolbar">
<div class="btn save_btn pull-right">
<a class="save" href="#" i18n="cliSaveToFileBtn"></a>
<a class="clear" href="#" i18n="cliClearOutputHistoryBtn"></a>
</div>
</div>
</div>

View file

@ -1,11 +1,46 @@
'use strict'; 'use strict';
/*global chrome*/ /*global chrome*/
TABS.cli = { TABS.cli = {
'validateText': "", lineDelayMs: 15,
'currentLine': "", profileSwitchDelayMs: 100,
'sequenceElements': 0 outputHistory: "",
cliBuffer: ""
}; };
function removePromptHash(promptText) {
return promptText.replace(/^# /, '');
}
function cliBufferCharsToDelete(command, buffer) {
var commonChars = 0;
for (var i = 0;i < buffer.length;i++) {
if (command[i] === buffer[i]) {
commonChars++;
} else {
break;
}
}
return buffer.length - commonChars;
}
function commandWithBackSpaces(command, buffer, noOfCharsToDelete) {
const backspace = String.fromCharCode(127);
return backspace.repeat(noOfCharsToDelete) + command.substring(buffer.length - noOfCharsToDelete, command.length);
}
function getCliCommand(command, cliBuffer) {
const buffer = removePromptHash(cliBuffer);
const bufferRegex = new RegExp('^' + buffer, 'g');
if (command.match(bufferRegex)) {
return command.replace(bufferRegex, '');
}
const noOfCharsToDelete = cliBufferCharsToDelete(command, buffer);
return commandWithBackSpaces(command, buffer, noOfCharsToDelete);
}
TABS.cli.initialize = function (callback) { TABS.cli.initialize = function (callback) {
var self = this; var self = this;
@ -14,12 +49,13 @@ TABS.cli.initialize = function (callback) {
googleAnalytics.sendAppView('CLI'); googleAnalytics.sendAppView('CLI');
} }
/* // Flush MSP queue as well as all MSP registered callbacks
* Flush MSP queue as well as all MSP registered callbacks
*/
helper.mspQueue.flush(); helper.mspQueue.flush();
MSP.callbacks_cleanup(); MSP.callbacks_cleanup();
self.outputHistory = "";
self.cliBuffer = "";
$('#content').load("./tabs/cli.html", function () { $('#content').load("./tabs/cli.html", function () {
// translate to user-selected language // translate to user-selected language
localize(); localize();
@ -28,18 +64,101 @@ TABS.cli.initialize = function (callback) {
var textarea = $('.tab-cli textarea'); var textarea = $('.tab-cli textarea');
$('.tab-cli .save').click(function() {
var prefix = 'cli';
var suffix = 'txt';
var filename = generateFilename(prefix, suffix);
var accepts = [{
description: suffix.toUpperCase() + ' files', extensions: [suffix],
}];
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename, accepts: accepts}, function(entry) {
if (chrome.runtime.lastError) {
if (chrome.runtime.lastError.message === 'User cancelled') {
GUI.log(chrome.i18n.getMessage('cliSaveToFileAborted'));
} else {
GUI.log(chrome.i18n.getMessage('cliSaveToFileFailed'));
console.error(chrome.runtime.lastError.message);
}
return;
}
if (!entry) {
GUI.log(chrome.i18n.getMessage('cliSaveToFileAborted'));
return;
}
entry.createWriter(function (writer) {
writer.onerror = function (){
GUI.log(chrome.i18n.getMessage('cliSaveToFileFailed'));
};
writer.onwriteend = function () {
if (self.outputHistory.length > 0 && writer.length === 0) {
writer.write(new Blob([self.outputHistory], {type: 'text/plain'}));
} else {
GUI.log(chrome.i18n.getMessage('cliSaveToFileCompleted'));
}
};
writer.truncate(0);
}, function (){
GUI.log(chrome.i18n.getMessage('cliSaveToFileFailed'));
console.error('Failed to get file writer');
});
});
});
$('.tab-cli .clear').click(function() {
self.outputHistory = "";
$('.tab-cli .window .wrapper').empty();
});
// Tab key detection must be on keydown,
// `keypress`/`keyup` happens too late, as `textarea` will have already lost focus.
textarea.keydown(function (event) {
const tabKeyCode = 9;
if (event.which == tabKeyCode) {
// prevent default tabbing behaviour
event.preventDefault();
const outString = textarea.val();
const lastCommand = outString.split("\n").pop();
const command = getCliCommand(lastCommand, self.cliBuffer);
if (command) {
self.sendAutoComplete(command);
textarea.val('');
}
}
});
textarea.keypress(function (event) { textarea.keypress(function (event) {
if (event.which == 13) { // enter const enterKeyCode = 13;
if (event.which == enterKeyCode) {
event.preventDefault(); // prevent the adding of new line event.preventDefault(); // prevent the adding of new line
var out_string = textarea.val(); var out_string = textarea.val();
var out_arr = out_string.split("\n");
self.history.add(out_string.trim()); self.history.add(out_string.trim());
var timeout_needle = 0; var outputArray = out_string.split("\n");
for (var i = 0; i < out_arr.length; i++) { Promise.reduce(outputArray, function(delay, line, index) {
self.sendSlowly(out_arr, i, timeout_needle++); return new Promise(function (resolve) {
} helper.timeout.add('CLI_send_slowly', function () {
var processingDelay = self.lineDelayMs;
if (line.toLowerCase().startsWith('profile')) {
processingDelay = self.profileSwitchDelayMs;
}
const isLastCommand = outputArray.length === index + 1;
if (isLastCommand && self.cliBuffer) {
line = getCliCommand(line, self.cliBuffer);
}
self.sendLine(line, function () {
resolve(processingDelay);
});
}, delay)
})
}, 0);
textarea.val(''); textarea.val('');
} }
@ -84,29 +203,33 @@ TABS.cli.history.add = function (str) {
this.history.push(str); this.history.push(str);
this.index = this.history.length; this.index = this.history.length;
}; };
TABS.cli.history.prev = function () { TABS.cli.history.prev = function () {
if (this.index > 0) this.index -= 1; if (this.index > 0) this.index -= 1;
return this.history[this.index]; return this.history[this.index];
}; };
TABS.cli.history.next = function () { TABS.cli.history.next = function () {
if (this.index < this.history.length) this.index += 1; if (this.index < this.history.length) this.index += 1;
return this.history[this.index - 1]; return this.history[this.index - 1];
}; };
TABS.cli.sendSlowly = function (out_arr, i, timeout_needle) { const backspaceCode = 8;
helper.timeout.add('CLI_send_slowly', function () { const lineFeedCode = 10;
var bufferOut = new ArrayBuffer(out_arr[i].length + 1); const carriageReturnCode = 13;
var bufView = new Uint8Array(bufferOut);
for (var c_key = 0; c_key < out_arr[i].length; c_key++) { function writeToOutput(text) {
bufView[c_key] = out_arr[i].charCodeAt(c_key); $('.tab-cli .window .wrapper').append(text);
} $('.tab-cli .window').scrollTop($('.tab-cli .window .wrapper').height());
}
bufView[out_arr[i].length] = 0x0D; // enter (\n) function writeLineToOutput(text) {
writeToOutput(text + "<br>");
}
serial.send(bufferOut); function setPrompt(text) {
}, timeout_needle * 100); $('.tab-cli textarea').val(text);
}; }
TABS.cli.read = function (readInfo) { TABS.cli.read = function (readInfo) {
/* Some info about handling line feeds and carriage return /* Some info about handling line feeds and carriage return
@ -117,93 +240,106 @@ TABS.cli.read = function (readInfo) {
MAC only understands CR MAC only understands CR
Linux and Unix only understand LF Linux and Unix only understand LF
Windows understands (both) CRLF Windows understands (both) CRLF
Chrome OS currenty unknown Chrome OS currently unknown
*/ */
var data = new Uint8Array(readInfo.data), var data = new Uint8Array(readInfo.data),
text = ""; validateText = "",
sequenceCharsToSkip = 0;
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
if (CONFIGURATOR.cliValid) { const currentChar = String.fromCharCode(data[i]);
if (data[i] == 27 || this.sequenceElements > 0) { // ESC + other
this.sequenceElements++;
// delete previous space if (!CONFIGURATOR.cliValid) {
if (this.sequenceElements == 1) {
text = text.substring(0, text.length -1);
}
// Reset
if (this.sequenceElements >= 5) {
this.sequenceElements = 0;
}
}
if (this.sequenceElements == 0) {
switch (data[i]) {
case 10: // line feed
if (GUI.operating_system != "MacOS") {
text += "<br />";
}
this.currentLine = "";
break;
case 13: // carriage return
if (GUI.operating_system == "MacOS") {
text += "<br />";
}
this.currentLine = "";
break;
case 60:
text += '&lt';
break;
case 62:
text += '&gt';
break;
default:
text += String.fromCharCode(data[i]);
this.currentLine += String.fromCharCode(data[i]);
}
}
if (this.currentLine == 'Rebooting') {
CONFIGURATOR.cliActive = false;
CONFIGURATOR.cliValid = false;
GUI.log(chrome.i18n.getMessage('cliReboot'));
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
GUI.handleReconnect();
}
} else {
// try to catch part of valid CLI enter message // try to catch part of valid CLI enter message
this.validateText += String.fromCharCode(data[i]); validateText += currentChar;
text += String.fromCharCode(data[i]); writeToOutput(currentChar);
continue;
} }
const escapeSequenceCode = 27;
const escapeSequenceCharLength = 3;
if (data[i] == escapeSequenceCode && !sequenceCharsToSkip) { // ESC + other
sequenceCharsToSkip = escapeSequenceCharLength;
}
if (sequenceCharsToSkip) {
sequenceCharsToSkip--;
continue;
}
switch (data[i]) {
case lineFeedCode:
if (GUI.operating_system === "Windows") {
writeLineToOutput(this.cliBuffer);
this.cliBuffer = "";
}
break;
case carriageReturnCode:
if (GUI.operating_system !== "Windows") {
writeLineToOutput(this.cliBuffer);
this.cliBuffer = "";
}
break;
case 60:
this.cliBuffer += '&lt';
break;
case 62:
this.cliBuffer += '&gt';
break;
case backspaceCode:
this.cliBuffer = this.cliBuffer.slice(0, -1);
break;
default:
this.cliBuffer += currentChar;
}
this.outputHistory += currentChar;
if (this.cliBuffer == 'Rebooting') {
CONFIGURATOR.cliActive = false;
CONFIGURATOR.cliValid = false;
GUI.log(chrome.i18n.getMessage('cliReboot'));
GUI.log(chrome.i18n.getMessage('deviceRebooting'));
GUI.handleReconnect();
}
} }
if (!CONFIGURATOR.cliValid && this.validateText.indexOf('CLI') != -1) { if (!CONFIGURATOR.cliValid && validateText.indexOf('CLI') !== -1) {
GUI.log(chrome.i18n.getMessage('cliEnter')); GUI.log(chrome.i18n.getMessage('cliEnter'));
CONFIGURATOR.cliValid = true; CONFIGURATOR.cliValid = true;
this.validateText = ""; validateText = "";
} }
$('.tab-cli .window .wrapper').append(text); setPrompt(removePromptHash(this.cliBuffer));
$('.tab-cli .window').scrollTop($('.tab-cli .window .wrapper').height()); };
TABS.cli.sendLine = function (line, callback) {
this.send(line + '\n', callback);
};
TABS.cli.sendAutoComplete = function (line, callback) {
this.send(line + '\t', callback);
};
TABS.cli.send = function (line, callback) {
var bufferOut = new ArrayBuffer(line.length);
var bufView = new Uint8Array(bufferOut);
for (var c_key = 0; c_key < line.length; c_key++) {
bufView[c_key] = line.charCodeAt(c_key);
}
serial.send(bufferOut, callback);
}; };
TABS.cli.cleanup = function (callback) { TABS.cli.cleanup = function (callback) {
if (!CONFIGURATOR.connectionValid || !CONFIGURATOR.cliValid) { if (!(CONFIGURATOR.connectionValid && CONFIGURATOR.cliValid && CONFIGURATOR.cliActive)) {
if (callback) callback(); if (callback) callback();
return; return;
} }
this.send(getCliCommand('exit\r', this.cliBuffer), function (writeInfo) {
var bufferOut = new ArrayBuffer(5);
var bufView = new Uint8Array(bufferOut);
bufView[0] = 0x65; // e
bufView[1] = 0x78; // x
bufView[2] = 0x69; // i
bufView[3] = 0x74; // t
bufView[4] = 0x0D; // enter
serial.send(bufferOut, function (writeInfo) {
// we could handle this "nicely", but this will do for now // we could handle this "nicely", but this will do for now
// (another approach is however much more complicated): // (another approach is however much more complicated):
// we can setup an interval asking for data lets say every 200ms, when data arrives, callback will be triggered and tab switched // we can setup an interval asking for data lets say every 200ms, when data arrives, callback will be triggered and tab switched

View file

@ -12,10 +12,14 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
var craftName = null; var craftName = null;
var loadCraftName = function(callback) { var loadCraftName = function(callback) {
mspHelper.getCraftName(function(name) { if (!CONFIG.name || CONFIG.name.trim() === '') {
craftName = name; mspHelper.getCraftName(function(name) {
craftName = name;
callback();
});
} else {
callback(); callback();
}); }
}; };
var saveCraftName = function(callback) { var saveCraftName = function(callback) {
@ -625,7 +629,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
$attitudeFrequency.change(function () { $attitudeFrequency.change(function () {
INAV_PID_CONFIG.attitudeTaskFrequency = $attitudeFrequency.val(); INAV_PID_CONFIG.attitudeTaskFrequency = $attitudeFrequency.val();
}); });
if (semver.gte(CONFIG.flightControllerVersion, "1.5.0")) { if (semver.gte(CONFIG.flightControllerVersion, "1.5.0")) {
var $sensorAcc = $('#sensor-acc'), var $sensorAcc = $('#sensor-acc'),