diff options
author | Konstantin Sharlaimov <konstantin.sharlaimov@gmail.com> | 2018-12-10 13:37:41 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-10 13:37:41 +0300 |
commit | 08d50f7feec3631592e30cc45e92788966c8a15b (patch) | |
tree | 59ea0cc785eab47e82ce1658d7d866579f15365f | |
parent | 2a7a2649e02661717123e96ed90ea33cc3b67597 (diff) | |
parent | 43da66e2b42c515e5d557376a250a6d024e4b0ff (diff) |
Merge pull request #604 from nmaggioni/cli_rebase
Rebase CLI tab on latest CF
-rwxr-xr-x | _locales/en/messages.json | 15 | ||||
-rw-r--r-- | js/fc.js | 9 | ||||
-rw-r--r-- | js/helpers.js | 33 | ||||
-rw-r--r-- | js/msp/MSPHelper.js | 11 | ||||
-rwxr-xr-x | js/serial_backend.js | 7 | ||||
-rw-r--r-- | src/css/tabs/cli.css | 17 | ||||
-rw-r--r-- | tabs/cli.html | 9 | ||||
-rw-r--r-- | tabs/cli.js | 314 | ||||
-rw-r--r-- | tabs/configuration.js | 12 |
9 files changed, 324 insertions, 103 deletions
diff --git a/_locales/en/messages.json b/_locales/en/messages.json index e2015e8f..ab7a367b 100755 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1441,6 +1441,21 @@ "cliReboot": { "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": { "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." @@ -103,7 +103,8 @@ var FC = { battery_profile: 0, uid: [0, 0, 0], accelerometerTrims: [0, 0], - armingFlags: 0 + armingFlags: 0, + name: '' }; BF_CONFIG = { @@ -567,7 +568,7 @@ var FC = { features.push( {bit: 28, group: 'esc-priority', name: 'PWM_OUTPUT_ENABLE', haveTip: true} ); - + /* * Transponder disabled until not implemented in firmware */ @@ -1068,8 +1069,8 @@ var FC = { return { 0: "Land", 1: "Drop", - 2: "RTH", - 3: "Do Nothing", + 2: "RTH", + 3: "Do Nothing", } }, getRcMapLetters: function () { diff --git a/js/helpers.js b/js/helpers.js index 9207089d..ecf72651 100644 --- a/js/helpers.js +++ b/js/helpers.js @@ -12,4 +12,37 @@ function constrain(input, min, max) { } 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; }
\ No newline at end of file diff --git a/js/msp/MSPHelper.js b/js/msp/MSPHelper.js index f22d8be0..8b2c78c7 100644 --- a/js/msp/MSPHelper.js +++ b/js/msp/MSPHelper.js @@ -525,7 +525,7 @@ var mspHelper = (function (gui) { break; - case MSPCodes.MSP_SET_SERVO_MIX_RULE: + case MSPCodes.MSP_SET_SERVO_MIX_RULE: console.log("Servo mix saved"); break; @@ -1345,6 +1345,11 @@ var mspHelper = (function (gui) { console.log('OSD char uploaded'); break; case MSPCodes.MSP_NAME: + CONFIG.name = ''; + var char; + while ((char = data.readU8()) !== null) { + CONFIG.name += String.fromCharCode(char); + } break; case MSPCodes.MSP_SET_NAME: console.log("Craft name set"); @@ -1420,7 +1425,7 @@ var mspHelper = (function (gui) { case MSPCodes.MSP2_INAV_SET_MC_BRAKING: console.log('Braking config saved'); break; - + default: console.log('Unknown code detected: ' + dataHandler.code); } else { @@ -2186,7 +2191,7 @@ var mspHelper = (function (gui) { // send one at a time, with index var servoRule = SERVO_RULES.get()[servoIndex]; - + buffer.push(servoIndex); buffer.push(servoRule.getTarget()); buffer.push(servoRule.getInput()); diff --git a/js/serial_backend.js b/js/serial_backend.js index da2312da..2226d030 100755 --- a/js/serial_backend.js +++ b/js/serial_backend.js @@ -306,7 +306,12 @@ function onOpen(openInfo) { googleAnalytics.sendEvent('Firmware', 'Variant', CONFIG.flightControllerIdentifier + ',' + CONFIG.flightControllerVersion); GUI.log(chrome.i18n.getMessage('fcInfoReceived', [CONFIG.flightControllerIdentifier, CONFIG.flightControllerVersion])); if (semver.gte(CONFIG.flightControllerVersion, CONFIGURATOR.firmwareVersionAccepted)) { - onValidFirmware(); + mspHelper.getCraftName(function(name) { + if (name) { + CONFIG.name = name; + } + onValidFirmware(); + }); } else { onInvalidFirmwareVersion(); } diff --git a/src/css/tabs/cli.css b/src/css/tabs/cli.css index 52597896..d7f1643d 100644 --- a/src/css/tabs/cli.css +++ b/src/css/tabs/cli.css @@ -3,7 +3,7 @@ } .tab-cli .content_wrapper { - height: calc(100% - 50px); + height: calc(100% - 92px); } .tab-cli p { @@ -56,4 +56,19 @@ .tab-cli .window .wrapper { 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; +} + }
\ No newline at end of file diff --git a/tabs/cli.html b/tabs/cli.html index 19872413..9a533704 100644 --- a/tabs/cli.html +++ b/tabs/cli.html @@ -5,6 +5,7 @@ <p i18n="cliInfo"></p> </div> </div> + <div class="backdrop"> <div class="window"> <div class="wrapper"></div> @@ -12,4 +13,10 @@ </div> <textarea name="commands" i18n_placeholder="cliInputPlaceholder" rows="1" cols="0"></textarea> </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>
\ No newline at end of file diff --git a/tabs/cli.js b/tabs/cli.js index 82a3d894..0a7a62c7 100644 --- a/tabs/cli.js +++ b/tabs/cli.js @@ -1,11 +1,46 @@ 'use strict'; /*global chrome*/ TABS.cli = { - 'validateText': "", - 'currentLine': "", - 'sequenceElements': 0 + lineDelayMs: 15, + profileSwitchDelayMs: 100, + 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) { var self = this; @@ -14,12 +49,13 @@ TABS.cli.initialize = function (callback) { 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(); MSP.callbacks_cleanup(); + self.outputHistory = ""; + self.cliBuffer = ""; + $('#content').load("./tabs/cli.html", function () { // translate to user-selected language localize(); @@ -28,18 +64,101 @@ TABS.cli.initialize = function (callback) { 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) { - if (event.which == 13) { // enter + const enterKeyCode = 13; + if (event.which == enterKeyCode) { event.preventDefault(); // prevent the adding of new line var out_string = textarea.val(); - var out_arr = out_string.split("\n"); self.history.add(out_string.trim()); - var timeout_needle = 0; - for (var i = 0; i < out_arr.length; i++) { - self.sendSlowly(out_arr, i, timeout_needle++); - } + var outputArray = out_string.split("\n"); + Promise.reduce(outputArray, function(delay, line, index) { + 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(''); } @@ -84,29 +203,33 @@ TABS.cli.history.add = function (str) { this.history.push(str); this.index = this.history.length; }; + TABS.cli.history.prev = function () { if (this.index > 0) this.index -= 1; return this.history[this.index]; }; + TABS.cli.history.next = function () { if (this.index < this.history.length) this.index += 1; return this.history[this.index - 1]; }; -TABS.cli.sendSlowly = function (out_arr, i, timeout_needle) { - helper.timeout.add('CLI_send_slowly', function () { - var bufferOut = new ArrayBuffer(out_arr[i].length + 1); - var bufView = new Uint8Array(bufferOut); +const backspaceCode = 8; +const lineFeedCode = 10; +const carriageReturnCode = 13; - for (var c_key = 0; c_key < out_arr[i].length; c_key++) { - bufView[c_key] = out_arr[i].charCodeAt(c_key); - } +function writeToOutput(text) { + $('.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); - }, timeout_needle * 100); -}; +function setPrompt(text) { + $('.tab-cli textarea').val(text); +} TABS.cli.read = function (readInfo) { /* Some info about handling line feeds and carriage return @@ -117,93 +240,106 @@ TABS.cli.read = function (readInfo) { MAC only understands CR Linux and Unix only understand LF Windows understands (both) CRLF - Chrome OS currenty unknown + Chrome OS currently unknown */ var data = new Uint8Array(readInfo.data), - text = ""; + validateText = "", + sequenceCharsToSkip = 0; for (var i = 0; i < data.length; i++) { - if (CONFIGURATOR.cliValid) { - if (data[i] == 27 || this.sequenceElements > 0) { // ESC + other - this.sequenceElements++; + const currentChar = String.fromCharCode(data[i]); - // delete previous space - if (this.sequenceElements == 1) { - text = text.substring(0, text.length -1); - } + if (!CONFIGURATOR.cliValid) { + // try to catch part of valid CLI enter message + validateText += currentChar; + writeToOutput(currentChar); + continue; + } - // Reset - if (this.sequenceElements >= 5) { - this.sequenceElements = 0; - } - } + const escapeSequenceCode = 27; + const escapeSequenceCharLength = 3; + if (data[i] == escapeSequenceCode && !sequenceCharsToSkip) { // ESC + other + sequenceCharsToSkip = escapeSequenceCharLength; + } - 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 += '<'; - break; - case 62: - text += '>'; - break; - - default: - text += String.fromCharCode(data[i]); - this.currentLine += String.fromCharCode(data[i]); + if (sequenceCharsToSkip) { + sequenceCharsToSkip--; + continue; + } + + switch (data[i]) { + case lineFeedCode: + if (GUI.operating_system === "Windows") { + writeLineToOutput(this.cliBuffer); + this.cliBuffer = ""; } - } - 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 - this.validateText += String.fromCharCode(data[i]); - text += String.fromCharCode(data[i]); + break; + case carriageReturnCode: + if (GUI.operating_system !== "Windows") { + writeLineToOutput(this.cliBuffer); + this.cliBuffer = ""; + } + break; + case 60: + this.cliBuffer += '<'; + break; + case 62: + this.cliBuffer += '>'; + 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')); CONFIGURATOR.cliValid = true; - this.validateText = ""; + validateText = ""; } - $('.tab-cli .window .wrapper').append(text); - $('.tab-cli .window').scrollTop($('.tab-cli .window .wrapper').height()); + setPrompt(removePromptHash(this.cliBuffer)); }; -TABS.cli.cleanup = function (callback) { - if (!CONFIGURATOR.connectionValid || !CONFIGURATOR.cliValid) { - if (callback) callback(); - return; - } +TABS.cli.sendLine = function (line, callback) { + this.send(line + '\n', callback); +}; - var bufferOut = new ArrayBuffer(5); +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); - bufView[0] = 0x65; // e - bufView[1] = 0x78; // x - bufView[2] = 0x69; // i - bufView[3] = 0x74; // t - bufView[4] = 0x0D; // enter + for (var c_key = 0; c_key < line.length; c_key++) { + bufView[c_key] = line.charCodeAt(c_key); + } + + serial.send(bufferOut, callback); +}; - serial.send(bufferOut, function (writeInfo) { +TABS.cli.cleanup = function (callback) { + if (!(CONFIGURATOR.connectionValid && CONFIGURATOR.cliValid && CONFIGURATOR.cliActive)) { + if (callback) callback(); + return; + } + this.send(getCliCommand('exit\r', this.cliBuffer), function (writeInfo) { // we could handle this "nicely", but this will do for now // (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 diff --git a/tabs/configuration.js b/tabs/configuration.js index 35108a8b..f7adfc6a 100644 --- a/tabs/configuration.js +++ b/tabs/configuration.js @@ -12,10 +12,14 @@ TABS.configuration.initialize = function (callback, scrollPosition) { var craftName = null; var loadCraftName = function(callback) { - mspHelper.getCraftName(function(name) { - craftName = name; + if (!CONFIG.name || CONFIG.name.trim() === '') { + mspHelper.getCraftName(function(name) { + craftName = name; + callback(); + }); + } else { callback(); - }); + } }; var saveCraftName = function(callback) { @@ -625,7 +629,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) { $attitudeFrequency.change(function () { INAV_PID_CONFIG.attitudeTaskFrequency = $attitudeFrequency.val(); }); - + if (semver.gte(CONFIG.flightControllerVersion, "1.5.0")) { var $sensorAcc = $('#sensor-acc'), |