Skip to content

Commit

Permalink
enhanced error handling #48; cli args take presendence over config #21
Browse files Browse the repository at this point in the history
  • Loading branch information
AndiDittrich committed Feb 17, 2018
1 parent 5f04e47 commit e98c86d
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 96 deletions.
6 changes: 4 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
### 3.0.0 ###
Refactored the codebase - make extensive use of ES6 feature like async/await

* Added: return codes
* Added: experimental esp32 support - thanks to [Spiritdude on GitHub](https://github.com/AndiDittrich/NodeMCU-Tool/pull/47) #47
* Added: return codes (0-success; 1-general_error; 127-lowlevel_error) - feature [requested on GitHub](https://github.com/AndiDittrich/NodeMCU-Tool/issues/48) #48
* Added: **debug mode** to show low-level error messages `--debug` flag
* Added: experimental **esp32** support - thanks to [Spiritdude on GitHub](https://github.com/AndiDittrich/NodeMCU-Tool/pull/47) #47
* Changed: cli arguments take presendence over config file - feature [requested on GitHub](https://github.com/AndiDittrich/NodeMCU-Tool/issues/21) #21
* Changed: the connector API has been splitted into multiple files
* Changed: added new cli logger
* Changed: requires **Node.js >= 7.6**
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (X11 License)

Copyright (c) 2015-2017 Andi Dittrich
Copyright (c) 2015-2018 Andi Dittrich

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
Expand Down
24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
NodeMCU-Tool
============
Upload/Download Lua files to your ESP8266 module with NodeMCU firmware.
Upload/Download Lua files to your ESP8266/ESP32 module with NodeMCU firmware.

**Simple. Command Line. Cross-Platform. File Management. [NodeMCU](http://nodemcu.com/index_en.html).**

Expand All @@ -14,7 +14,7 @@ Tool Summary
-------------
NodeMCU Tool allows you to

* Upload Lua files to your ESP8266/NodeMCU module
* Upload Lua files to your ESP8266/ESP32/NodeMCU module
* Upload any file-types (binary save)
* Bulk/Multi file uploads
* Download any file-type (binary save)
Expand All @@ -31,16 +31,20 @@ NodeMCU Tool allows you to

directly from the command line.

*Successful tested on Windows10, Debian 8.2 and Ubuntu 14 LTS - works out of the box without any tweaks*
*Successful tested on Windows10, Debian 8,9 and Ubuntu 14,15,16,17 - works out of the box without any tweaks*

Compatibility
-------------
The following NodeMCU firmware versions are verified

**ESP8266**
* NodeMCU LUA 1.4
* NodeMCU LUA 1.5.1
* NodeMCU LUA 1.5.4

**ESP32**
* preliminary support (esp32-dev.latest)

Related Documents
-----------------

Expand Down Expand Up @@ -236,6 +240,7 @@ In this Example, the baudrate is changed to 19.2k and COM3 is selected as defaul
{
"baudrate": "19200",
"port": "COM3",
"connectionDelay": 100,
"compile": true,
"minify": true,
"keeppath": true
Expand All @@ -248,9 +253,9 @@ All configuration options are **optional**

* **baudrate** (int) - the default baudrate in bits per second
* **port** (string) - the comport to use
* **connectionDelay** (int) - connection-delay in ms
* **compile** (boolean) - compile lua files after upload
* **minify** (boolean) - minifies files before uploading
* **optimize** (boolean) - optimize files before uploading (Deprecated! Use minify instead.)
* **keeppath** (boolean) - keep the relative file path in the destination filename (i.e: static/test.html will be named static/test.html)


Expand All @@ -274,11 +279,12 @@ The required encoding (file downloads) / decoding (file uploads) functions are a
The Tool is separated into the following components (ordered by its invocation)

1. `bin/nodemcu-tool.js` - the command line user interface handler based on [commander](https://www.npmjs.com/package/commander)
2. `lib/NodeMCU-Tool.js` - Highlevel Access to the main functions. Error and Status messages are handled there
3. `lib/NodeMcuConnector.js` - the Core which handles the Lua command based communication to the NodeMCU Module
4. `lib/ScriptableSerialTerminal.js` - the lowlevel part - a terminal session to the NodeMCU Module to run the Lua commands


2. `cli/nodemcu-tool.js` - Highlevel Access to the main functions. Error and Status messages are handled there
3. `lib/nodemcu-connector.js` - the Core which handles the Lua command based communication to the NodeMCU Module
4. `lib/connector/*.js` - low-level command handlers
5. `lib/transport/scriptable-serial-terminal.js` - the lowlevel part - a terminal session to the NodeMCU Module to run the Lua commands
6. `lib/transport/serialport.js` - a wrapper to handle the serial transport

### Application Stack ###

```
Expand Down
22 changes: 14 additions & 8 deletions bin/nodemcu-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const _loggingFacility = require('logging-facility');
const _logger = _loggingFacility.getLogger('NodeMCU-Tool');
_loggingFacility.addBackend('fancy-cli');

// general content
// general content passhrough to stdin
_nodemcutool.onOutput(function(message){
console.log(message);
process.stdout.write(message);
});

// wrap async tasks
Expand All @@ -34,8 +34,10 @@ function asyncWrapper(promise){

// trigger disconnect
.then(() => {
_logger.log('disconnecting device');
return _nodemcutool.disconnect();
if (_nodemcutool.Connector.isConnected()){
_logger.log('disconnecting');
return _nodemcutool.disconnect();
}
})

// gracefull exit
Expand All @@ -45,15 +47,17 @@ function asyncWrapper(promise){

// handle low-level errors
.catch(err => {
_logger.error(err.message, err.stack);
_logger.error(err.message);
_logger.debug(err.stack);
process.exit(1);
});
}
}

// low level com errors
_nodemcutool.onError(err => {
_logger.error(err.message, err.stack);
_logger.error(err.message);
_logger.debug(err.stack);
process.exit(127);
});

Expand All @@ -72,7 +76,10 @@ _cli
.option('--silent', 'Enable silent mode - no status messages are shown', null)

// connection delay between opening the serial device and starting the communication
.option('--connection-delay <delay>', 'Connection delay between opening the serial device and starting the communication', null);
.option('--connection-delay <delay>', 'Connection delay between opening the serial device and starting the communication', null)

// debug mode - display detailed error messages
.option('--debug', 'Enable debug mode - all status messages + stacktraces are shown', null);

_cli
.command('fsinfo')
Expand Down Expand Up @@ -259,7 +266,6 @@ _cli

// set defaults
data.minify = false;
data.optimize = false;
data.compile = false;
data.keeppath = false;

Expand Down
25 changes: 13 additions & 12 deletions lib/cli/nodemcu-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ const _mculogger = _loggingFacility.getLogger('NodeMCU');
const _terminallogger = _loggingFacility.getLogger('SerialTerminal');

// output
let outputHandler = function(message){
console.log(message);
};
let outputHandler = function(){};

let writeOutput = function(message){
function writeOutput(message){
if (outputHandler){
outputHandler(message);
}
};
}

// global options
const _options = {
Expand Down Expand Up @@ -115,8 +113,6 @@ async function upload(localFiles, options, onProgess){
try{
const stats = await _fs.statx(localFile);



// check if file is directory
if (stats.isDirectory()) {
_mculogger.error('Path "' + localFile + '" is a directory.');
Expand All @@ -126,6 +122,7 @@ async function upload(localFiles, options, onProgess){
// local file available
}catch (err){
_logger.error('Local file not found "' + localFile + '" skipping...');
_logger.debug(err);
return;
}

Expand Down Expand Up @@ -221,15 +218,17 @@ async function download(remoteFile){
_logger.log('Data Transfer complete!');

}catch(e){
throw new Error('Data Transfer FAILED!' + e);
_logger.debug(e);
throw new Error('Data Transfer FAILED!');
}

// store the file
try{
await _fs.writeFile(localFilename, data);
_logger.log('File "' + localFilename + '" created');
}catch(e){
throw new Error('cannot store file - ' + e);
_logger.debug(e);
throw new Error('i/o error - cannot save file');
}
}

Expand Down Expand Up @@ -260,7 +259,8 @@ async function mkfs(){
// just show complete message
_mculogger.log('File System created | ' + response);
}catch(e){
_mculogger.error('Formatting failed', e);
_mculogger.error('Formatting failed');
_logger.debug(e);
}
}

Expand Down Expand Up @@ -299,7 +299,7 @@ async function run(filename){
// show command response
_mculogger.log('Running "' + filename + '"');
_mculogger.log('>----------------------------->');
writeOutput(output);
writeOutput(output + '\n');
_mculogger.log('>----------------------------->');
}

Expand Down Expand Up @@ -338,7 +338,8 @@ async function devices(showAll, jsonOutput){
}
}
}catch(e){
_mculogger.alert('Cannot retrieve serial device list - ' + e);
_mculogger.alert('Cannot retrieve serial device list - ');
_logger.debug(e);
}
}

Expand Down
87 changes: 54 additions & 33 deletions lib/cli/options-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,83 @@ async function parseOptions(options){
// default
options = options || {};

// debug mode enabled by flag ?
if (options.parent.debug===true){
// enable silent mode (decrease logging level)
_loggingFacility.setBackend(_loggingFacility.LOGGER.FANCY(_loggingFacility.LEVEL.DEBUG));

// silent mode enabled by flag ?
// silent mode enabled by json output format ?
// silent mode enabled by json raw format ?
if (options.parent.silent===true || options.json === true || options.raw === true){
} else if (options.parent.silent===true || options.json === true || options.raw === true){
// enable silent mode (decrease logging level)
_loggingFacility.setBackend(_loggingFacility.LOGGER.FANCY(_loggingFacility.LEVEL.WARNING));
}else{
_loggingFacility.setBackend(_loggingFacility.LOGGER.FANCY(_loggingFacility.LEVEL.INFO));
}

// merge global flags, command flags and global defaults
const defaultConfig = {
// global flags
baudrate: options.parent.baud || '115200',
port: options.parent.port || '/dev/ttyUSB0',
connectionDelay: options.parent.connectionDelay || 0,

// command specific flags
minify: options.minify || false,
compile: options.compile || false,
keeppath: options.keeppath || false,
remotename: options.remotename || null,
run: options.run || false,
all: options.all || false,
json: options.json || false,
raw: options.raw || false,
softreset: options.softreset || false
};

// project based configuration
let configFile = {};
if (await _fs.isFile(_configFilename)){

// try to load project based configuration
const data = await _fs.readFile(_configFilename, 'utf8');

if (data.length > 10){
// decode json based data
const d = JSON.parse(data);

// extract values
defaultConfig.baudrate = d.baudrate || defaultConfig.baudrate;
defaultConfig.port = d.port || defaultConfig.port;
defaultConfig.minify = (d.minify && d.minify === true);
defaultConfig.optimize = (d.optimize && d.optimize === true);
defaultConfig.compile = (d.compile && d.compile === true);
defaultConfig.keeppath = (d.keeppath && d.keeppath === true);
configFile = JSON.parse(data);
_logger.log('Project based configuration loaded');
}
}

// generated config
const config = {};

// utility function to merge different options
// cli args take presendence over config
function mergeOptions(...opt){
// extract default (last argument)
const result = opt.pop();

// try to find first match
while (opt.length > 0){
// extract first argument (priority)
const o = opt.shift();

// value set ?
if (typeof o !== 'undefined' && o !== null){
return o;
}
}

return result;
}

// merge global flags, command flags and global defaults
// config file + cli args
config.baudrate = mergeOptions(options.parent.baud, configFile.baudrate, '115200');
config.port = mergeOptions(options.parent.port, configFile.port, '/dev/ttyUSB0');
config.connectionDelay = mergeOptions(options.parent.connectionDelay, configFile.connectionDelay, 0);
config.minify = mergeOptions(options.minify, configFile.minify, false);
config.compile = mergeOptions(options.compile, configFile.compile, false);
config.keeppath = mergeOptions(options.keeppath, configFile.keeppath, false);

// CLI args only
config.remotename = mergeOptions(options.minify, null);
config.run = mergeOptions(options.run, false);
config.all = mergeOptions(options.all, false);
config.json = mergeOptions(options.json, false);
config.raw = mergeOptions(options.raw, false);
config.softreset = mergeOptions(options.softreset, false);

// set port/baud options
_nodemcutool.setOptions({
device: defaultConfig.port,
baudrate: defaultConfig.baudrate,
connectionDelay: defaultConfig.connectionDelay
device: config.port,
baudrate: config.baudrate,
connectionDelay: config.connectionDelay
});

return defaultConfig;
return config;
}

// write options to file
Expand Down
3 changes: 3 additions & 0 deletions lib/connector/check-connection.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const _virtualTerminal = require('../transport/scriptable-serial-terminal');
const _luaCommandBuilder = require('../lua/command-builder');
const _logger = require('logging-facility').getLogger('NodeMCU-Connector');

// checks the node-mcu connection
function checkConnection(){
Expand All @@ -22,6 +23,8 @@ function checkConnection(){
if (response == 'echo1337' && echo == 'print("echo1337")') {
resolve();
} else {
_logger.log('Echo:', echo);
_logger.debug('Response:', response);
reject(new Error('No response detected - is NodeMCU online and the Lua interpreter ready ?'));
}
})
Expand Down
4 changes: 3 additions & 1 deletion lib/connector/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const _async = require('async-magic');
const _checkConnection = require('./check-connection');
const _deviceInfo = require('./device-info');
const _luaCommandBuilder = require('../lua/command-builder');
const _logger = require('logging-facility').getLogger('NodeMCU-Connector');

async function connect(devicename, baudrate, applyConnectionCheck=true, connectDelay=0){
// handle connection errors (device not available, permission errors..)
Expand All @@ -12,7 +13,8 @@ async function connect(devicename, baudrate, applyConnectionCheck=true, connectD

// custom error messagr
}catch(e){
throw new Error('Cannot open port "' + devicename + '" - ' + e);
_logger.debug(e);
throw new Error('Cannot open port "' + devicename + '"');
}

// no connection check ?
Expand Down
Loading

0 comments on commit e98c86d

Please sign in to comment.