diff --git a/CMakeLists.txt b/CMakeLists.txt index 575d37d..9deae7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,3 +68,5 @@ if(CATKIN_ENABLE_TESTING) find_package(rostest REQUIRED) add_rostest(launch/test_report.test) endif() + +execute_process(COMMAND npm --prefix ${PROJECT_SOURCE_DIR} install) diff --git a/package.json b/package.json index a017589..7043fc8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,14 @@ { - "name": "FlexBE App", - "version": "2.0.10", + "name": "flexbe_app", + "version": "2.0.11", "main": "src/main.js", + "description": "flexbe_app provides a user interface (editor + runtime control) for the FlexBE behavior engine.", + "license": "BSD-3-Clause", + "repository": { + "type": "git", + "url": "git@github.com:FlexBE/flexbe_app.git" + }, + "readme": "README.md", "window": { "icon": "src/img/icon-128.png", "toolbar": false, @@ -9,5 +16,8 @@ "height": 830, "min_width": 1340, "min_height": 650 + }, + "dependencies" : { + "scp2" : "^0.5.0" } } diff --git a/src/events.js b/src/events.js index 30b4116..d8c952d 100644 --- a/src/events.js +++ b/src/events.js @@ -119,6 +119,10 @@ document.addEventListener('DOMContentLoaded', function() { document.getElementById('select_default_package').addEventListener('change', UI.Settings.defaultPackageChanged); document.getElementById('select_code_indentation').addEventListener('change', UI.Settings.codeIndentationChanged); document.getElementById('input_editor_command').addEventListener('change', UI.Settings.editorCommandChanged); + document.getElementById('cb_save_on_robot').addEventListener('change', UI.Settings.saveOnRobotClicked); + document.getElementById('input_ssh_user_name').addEventListener('change', UI.Settings.sshUsernameChanged); + document.getElementById('input_ssh_host_name').addEventListener('change', UI.Settings.sshHostnameChanged); + document.getElementById('select_transition_mode').addEventListener('change', UI.Settings.transitionEndpointsChanged); document.getElementById('input_gridsize').addEventListener('change', UI.Settings.gridsizeChanged); diff --git a/src/io/io_behaviorsaver.js b/src/io/io_behaviorsaver.js index bca6ccf..25b1984 100644 --- a/src/io/io_behaviorsaver.js +++ b/src/io/io_behaviorsaver.js @@ -58,13 +58,89 @@ IO.BehaviorSaver = new (function() { } } + String.prototype.format = function() { + var formatted = this; + for( var arg in arguments ) { + formatted = formatted.replace("{" + arg + "}", arguments[arg]); + } + return formatted; + }; + var saveSuccessCallback = function() { T.logInfo("Save successful!"); UI.Panels.Terminal.hide(); UI.Settings.updateBehaviorlib(); UI.Tools.notifyRosCommand('save'); - } + } + + var saveOnRobot = function() { + var names = Behavior.createNames(); + var package_name = names.rosnode_name; + var ssh_username = UI.Settings.getRobotUsername(); + var ssh_hostname = UI.Settings.getRobotHostname(); + var ssh_package_path = "" + + if(ssh_username === "" || ssh_hostname === "" /*|| ssh_password === ""*/) { + T.logError("SSH configurations are not well defined, fix them from Configuration Tab -> Code Generation Box"); + return; + } + + var ssh_password = window.prompt("Please enter {0}:{1} password: ".format(ssh_username, ssh_hostname)); + if(ssh_password == null || ssh_password == "") { + return; + } + ROS.getPackagePath(package_name, (package_path) => + { + try + { + var SSHClient = require('ssh2').Client; + conn = new SSHClient(); + conn.on('ready', function() { + var ros_distro = process.env.ROS_DISTRO; + conn.exec('bash -c "source .profile && rospack find {1}"'.format(ros_distro, package_name), function(err, stream) { + if (err) { + T.logError(err); + } + stream.on('close', function(code, signal) { + conn.end(); + }) + .on('data', function(remote_package_path) { + ssh_package_path = String(remote_package_path).trim(); + T.logInfo('Path to package on the robot: ' + ssh_package_path); + conn.end() + var scp_client = require('scp2') + var ssh_query = '{0}:{1}@{2}:{3}/'.format( + ssh_username, + ssh_password, + ssh_hostname, + ssh_package_path); + scp_client.scp(package_path+"/", String(ssh_query), function(err) { + var err_str = String(err); + if( err_str != 'undefined') { + T.logError("scp failed "+err); + } + }); + }).stderr.on('data', function(data) { + T.logError(data+", Make sure robot's ROS environment is configured properly in \".profile\" script"); + }) + }); + }) + .on('error', function(err) { + T.logError(err); + T.logError("Please check your ssh configuration and entered password."); + }) + .connect({ + host: ssh_hostname, + username: ssh_username, + password: ssh_password + }); + } + catch(ex) { + T.logError("An exception occurred: "+ex.message+", Make sure you installed the dependencies properly!") + } + }) + } this.saveStateMachine = function() { T.clearLog(); @@ -91,15 +167,19 @@ IO.BehaviorSaver = new (function() { return; } - // store in file - storeBehaviorCode(generated_code, () => { - // make sure code file exists before creating the manifest - // this reduces the risk for orphan manifests - storeBehaviorManifest(generated_manifest, () => { - saveSuccessCallback(); - }); - }); - }); - } + // store in file + storeBehaviorCode(generated_code, () => { + // make sure code file exists before creating the manifest + // this reduces the risk for orphan manifests + storeBehaviorManifest(generated_manifest, () => { + if(UI.Settings.isSaveOnRobotEnabled()) { + //Save/Overwrite the behavior on the robot + saveOnRobot(); + } + saveSuccessCallback(); + }); + }); + }); + } -}) (); \ No newline at end of file +}) (); diff --git a/src/ui/ui_settings.js b/src/ui/ui_settings.js index 70e5436..9e458f0 100644 --- a/src/ui/ui_settings.js +++ b/src/ui/ui_settings.js @@ -17,6 +17,9 @@ UI.Settings = new (function() { var default_package; var code_indentation; var editor_command; + var save_on_robot; + var ssh_hostname; + var ssh_username; var transition_mode; var gridsize; @@ -42,6 +45,9 @@ UI.Settings = new (function() { 'default_package': default_package, 'code_indentation': code_indentation, 'editor_command': editor_command, + 'save_on_robot': save_on_robot, + 'ssh_username' : ssh_username, + 'ssh_hostname' : ssh_hostname, 'transition_mode': transition_mode, 'gridsize': gridsize, 'commands_enabled': commands_enabled, @@ -69,6 +75,9 @@ UI.Settings = new (function() { 'default_package': 'flexbe_behaviors', 'code_indentation': 0, 'editor_command': 'gedit --new-window $FILE +$LINE', + 'save_on_robot' : false, + 'ssh_username': "", + 'ssh_hostname' : "", 'transition_mode': 1, 'gridsize': 50, 'commands_enabled': false, @@ -101,6 +110,16 @@ UI.Settings = new (function() { document.getElementById("select_code_indentation").selectedIndex = items.code_indentation; editor_command = items.editor_command; document.getElementById("input_editor_command").value = items.editor_command; + save_on_robot = items.save_on_robot; + document.getElementById("cb_save_on_robot").checked = items.save_on_robot; + ssh_username = items.ssh_username; + document.getElementById("input_ssh_user_name").value = items.ssh_username; + ssh_hostname = items.ssh_hostname; + document.getElementById("input_ssh_host_name").value = items.ssh_hostname; + document.getElementById("input_ssh_user_name").disabled = !items.save_on_robot; + document.getElementById("input_ssh_host_name").disabled = !items.save_on_robot; + + transition_mode = items.transition_mode; document.getElementById("select_transition_mode").selectedIndex = items.transition_mode; @@ -443,6 +462,33 @@ UI.Settings = new (function() { return editor_command.replace("$LINE", line_number).replace("$FILE", file_path); } + this.isSaveOnRobotEnabled = function() { return save_on_robot; } + + this.saveOnRobotClicked = function(evt) { + save_on_robot = evt.target.checked; + storeSettings(); + document.getElementById("input_ssh_user_name").disabled = !save_on_robot; + document.getElementById("input_ssh_host_name").disabled = !save_on_robot; + } + + this.sshUsernameChanged = function(evt) { + ssh_username = document.getElementById('input_ssh_user_name').value; + storeSettings(); + } + + this.sshHostnameChanged = function(evt) { + ssh_hostname = document.getElementById('input_ssh_host_name').value; + storeSettings(); + } + + this.getRobotHostname = function() { + return document.getElementById('input_ssh_host_name').value; + } + + this.getRobotUsername = function() { + return document.getElementById('input_ssh_user_name').value; + } + // Editor //======== @@ -594,4 +640,4 @@ UI.Settings = new (function() { } } -}) (); \ No newline at end of file +}) (); diff --git a/src/window.html b/src/window.html index 54f9a1c..5234fd3 100644 --- a/src/window.html +++ b/src/window.html @@ -503,6 +503,18 @@

Code Generation

Editor command: + + Save on the robot: + + + + Robot hostname: + + + + Robot username: + +