Merge branch 'release/0.0.1'

This commit is contained in:
David Walsh
2019-05-07 12:28:09 -04:00
30 changed files with 3514 additions and 677 deletions

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.3)
project(rosbridge_gui)
project(aescape_lab_ui)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
@@ -82,7 +82,7 @@ find_package(catkin REQUIRED COMPONENTS
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES rosbridge_gui
# LIBRARIES aescape_lab_ui
# CATKIN_DEPENDS roscpp rospy std_msgs
# DEPENDS system_lib
)
@@ -99,8 +99,8 @@ include_directories(
)
## Declare a cpp library
# add_library(rosbridge_gui
# src/${PROJECT_NAME}/rosbridge_gui.cpp
# add_library(aescape_lab_ui
# src/${PROJECT_NAME}/aescape_lab_ui.cpp
# )
## Declare a cpp executable
@@ -108,7 +108,7 @@ include_directories(
## Add cmake target dependencies of the executable/library
## as an example, message headers may need to be generated before nodes
# add_dependencies(rosbridge_gui_node rosbridge_gui_generate_messages_cpp)
# add_dependencies(aescape_lab_ui_node aescape_lab_ui_generate_messages_cpp)
## Specify libraries to link a library or executable target against
@@ -127,7 +127,7 @@ include_directories(
# )
## Mark executables and/or libraries for installation
# install(TARGETS rosbridge_gui rosbridge_gui_node
# install(TARGETS aescape_lab_ui aescape_lab_ui_node
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
@@ -152,7 +152,7 @@ include_directories(
#############
## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_rosbridge_gui.cpp)
# catkin_add_gtest(${PROJECT_NAME}-test test/test_aescape_lab_ui.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

View File

@@ -1,51 +1,19 @@
# ROSBRIDGE GUI
# Aescape Lab UI
The purpose of this project is an example of how to write a web page that can be accessed by any device on the same network as the host computer by accessing the IP of that host computer.
## To install
```
sudo apt install npm -y
sudo npm install http-server -g
sudo ln -s /usr/bin/nodejs /usr/bin/node
```
The best way to contribute to this project is to make a new brach from the master branch called the name of the robot that it is used for. This will also alow maximum colaberation between labs. This can be accomplished by going to the tabs Commits->Branches then select "New Branch"
### Quick Start Guide
If you are connecting to a remote computer that is running the roscore, you need to run the following lines in the terminal:
sudo route add 192.168.2.1 gw <Host computer IP>
export ROS_MASTER_URI=http://<Host computer IP>:11311
export ROS_IP=<your IP>
Make sure you have the web_video_server installed:
sudo apt-get install ros-indigo-web-video-server
Make sure you have ROSBridge installed:
sudo apt-get install ros-indigo-rosbridge-suite
Then run the bash file in the working directory by running the following in the terminal.
. launch.bash
#### Just display the webpage
If you just want to test the webpage, navigate to the working directory in the terminal and type
python -m SimpleHTTPServer
Then open http://localhost:8000/. or yourIPAddress:8000
# Making Changes
Feel free to clone this project, make a new branch and use it as a base for your own GUI.
You can easily customise the "Data Display" pannel by changing the HTML "data\_dsisplay\data\_display.html" file.
All ROS related Javascript should go in the "data\_display/js/ros\_scripts.js" file.
All functions that are used to update the GUI should go in the "data\_display/js/update_guis.js" file.
DO NOT MAKE CHANGES OUTISDE OF THIS DIECTORY AS IT WILL CAUSE MERG PROBLEMS.
By using ROSBridge, this GUI will attempt to display relevant information over the web for any device.
# Screen Shots
Screen shots are in the root working directory
## To run
Set your master to be what you want - default is `phoebe`.
Then run
```
roslaunch aescape_lab_ui lab_ui.launch
```
## To access
Use Hamachi network:
http://titan.aescape.co:8000

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,32 @@ ros.recording = false;
ros.connected = false;
// ros.connectionName = 'ws://192.168.1.105:9090';
ros.connectionName = 'ws://localhost:9090';
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){
return pair[1];
}
}
}
var master = "localhost"
master = getQueryVariable("master")
if(master == null)
{
master = window.location.hostname
//"localhost"
}
console.log('Master = ' + master);
ros.connectionName = 'ws://' + master + ':9090';
// ros.connectionName = 'ws://localhost:9090';
// ros.connectionName = 'ws://titan.aescape.co:9090';
console.log('ros.connectionName = ' + ros.connectionName);
// If there is an error on the backend, an 'error' emit will be emitted.
ros.on('error', function(error) {
@@ -19,7 +44,7 @@ ros.on('error', function(error) {
document.getElementById("ROSNodes").innerHTML = "No Nodes Detected";
rosbridgeconnection_badge
console.log(error);192
console.log(error);
});
// Find out exactly when we made a connection.
ros.on('connection', function() {
@@ -29,7 +54,7 @@ ros.on('connection', function() {
document.getElementById("ConnectionIPInput").value = ros.connectionName;
document.getElementById("ConnectionIPLabel").innerHTML = 'Connection made at:';
document.getElementById("rosbridgeconnection_badge").innerHTML = 'Connected';
document.getElementById("ConnectionButton").className = "btn btn-success"
document.getElementById("ConnectionButton").className = "btn btn-success";
ros.nodes = Array();
ros.topics = Array();
@@ -47,7 +72,7 @@ ros.on('close', function() {
ros.topics = Array();
});
// Create a connection to the rosbridge WebSocket server.
ros.connect(ros.connectionName);
//ros.connect(ros.connectionName);
///////////////////////////////////////////////////////////////////////////////////
@@ -115,65 +140,10 @@ bagNotifier.subscribe(function(message) {
///////////////////////////////////////////////////////////////////////////////////
ros.buildTopicList = function(fresh_topics)
{
// console.log(topics)
// fresh_topics = fresh_topics.sort();
// First check that all our current topics are in the list of fresh_topics
for( var topic = 0; topic < ros.topics.length; topic ++)
{
if (fresh_topics.indexOf(ros.topics[topic].name) > -1)
{
// We know that we have already found that topic so we don't need to remove it
}
else
{
// That topic is no longer being published so we need ro remove it
ros.topics.splice(topic,1);
}
}
for (var topic =0; topic < fresh_topics.length; topic ++ )
{
if (ros.topics.indexOfTopic(fresh_topics[topic]) > -1)
{
// We know that we have already found that topic so we don't need to re-add it
}
else
{
// We do not have that topic so we need to add it
// create the new topic
var new_topic = new Topic(fresh_topics[topic]);
// make sure to put in in the list in the order
ros.topics.push(new_topic);
}
}
ros.topics.sort(function(a, b) {
if ( a.name < b.name )
return -1;
if ( a.name > b.name )
return 1;
return 0;
})
};
ros.getTopicsList = function()
{
// var topicList = [];
return ros.topics;
}
// attept to connect to the ros master from the IP given orgrab it from the form
ros.attemptConnection = function(ipAddress)
{
ros.close();
if( typeof ipAddress !== "undefined")
{
ros.connectionName = ipAddress;
@@ -184,6 +154,7 @@ ros.attemptConnection = function(ipAddress)
}
console.log('Connection = ' + ros.connectionName);
ros.connect(ros.connectionName);
getMasterName();
}
function toggleRecording()
@@ -210,3 +181,22 @@ function toggleRecording()
}
}
}
function getMasterName()
{
var service = new ROSLIB.Service({
ros : ros,
name : '/rosapi/service_host',
serviceType : 'rosapi/ServiceHost'
});
var request = new ROSLIB.ServiceRequest({
service: '/rosout/get_loggers' // Only the master hosts this service.
});
service.callService(request, function(result) {
console.log('Master Name = ' + result.host);
document.getElementById("MasterName").innerHTML = result.host;
});
}

View File

@@ -1,99 +1,27 @@
$("#myImage").click ( function (evt) {
var jThis = $(this);
var offsetFromParent = jThis.position ();
var topThickness = (jThis.outerHeight(true) - jThis.height() ) / 2;
var leftThickness = (jThis.outerWidth (true) - jThis.width () ) / 2;
//--- (x,y) coordinates of the mouse click relative to the image.
var x = evt.pageX - offsetFromParent.left - leftThickness;
var y = evt.pageY - offsetFromParent.top - topThickness;
ReportDims ();
$('#rez').append ('<p>User clicked at: ' + x + ', ' + y + ' (x,y).</p>')
} );
function ReportDims () {
w = $("#myImage").width ();
h = $("#myImage").height ();
$('#rez').text ('The image is ' + w + ' by ' + h + ' (w by h).');
}
ReportDims ();
function updateVoltage(voltage)
{
var voltage_min = 9.5;
var voltage_max = 12.5;
var voltage_range = voltage_max - voltage_min;
var voltage_percentage = (voltage - voltage_min) / voltage_range * 100;
var voltage_string = "";
voltage_string = voltage_string.concat(voltage_percentage);
voltage_string = voltage_string.substring(0,4);
voltage_string = voltage_string.concat('%');
document.getElementById("VoltageDisplay").style.width = voltage_string;
document.getElementById("VoltageDisplay").innerHTML = voltage_string;
if (voltage_percentage < 50 && voltage_percentage > 25 ) {
document.getElementById("VoltageDisplay").className = "progress-bar progress-bar-warning"
}
else if ( voltage_percentage <= 25)
{
document.getElementById("VoltageDisplay").className = "progress-bar progress-bar-danger"
var sound = document.getElementById("audio");
sound.play()
console.log("Pay Sound")
}
else
{
document.getElementById("VoltageDisplay").className = "progress-bar progress-bar-success"
}
};
function updateTopicsGUI()
{
ros.getTopics(ros.buildTopicList);
ros.getTopics(function(result)
{
ros.topics = result;
});
var topics = ros.getTopicsList();
var topics = ros.topics;
if(topics != null){
var innerHTML = "";
for (var i = 0; i < topics.length; i++ )
{
innerHTML = innerHTML.concat(generateTopicCheckbox(topics[i]));
innerHTML = innerHTML.concat(topics[i]);
innerHTML = innerHTML.concat("<br>");
}
document.getElementById("ROSTopics").innerHTML = innerHTML;
}
};
function generateTopicCheckbox(topic)
{
var str = "<div class=\"checkbox\">";
var checked_str = "unchecked";
if (topic.bag == true)
{
checked_str = "checked"
}
str =str.concat("<label><input type=\"checkbox\" onclick=\"toggleToBag(\'"+ topic.name + "\')\" id=\"" + topic.name + "_checkbox\" "+ checked_str +">");
str = str.concat(topic.name);
str = str.concat("</label></div>");
return str;
};
function toggleToBag(topic_name)
{
// if($.inArray(topic_name, ros.topics))
ros.topics[ros.topics.indexOfTopic(topic_name)].bag = !ros.topics[ros.topics.indexOfTopic(topic_name)].bag ;
}
function updateNodesGUI()
{
@@ -113,19 +41,18 @@ function updateNodesGUI()
{
innerHTML = innerHTML.concat(nodes[i]);
innerHTML = innerHTML.concat("<br>");
}
document.getElementById("ROSNodes").innerHTML = innerHTML;
}
};
window.setInterval(function(){
// window.setInterval(function(){
updateTopicsGUI();
updateNodesGUI();
// updateTopicsGUI();
// updateNodesGUI();
}, 500);
// }, 5000);
function validateForm()
{

View File

@@ -0,0 +1,186 @@
<div class="panel panel-default">
<div class="panel-heading">
Select Operation Mode
</div>
<div class="panel-body text-center">
<div class="col-md-6">
<button id="teachingModeButton" type="button" onclick="triggerService('/aescape/mode/activateTeachingController')" class="btn btn-primary">
Teaching Mode
</button>
<button id="executionModeButton" type="button" onclick="triggerService('/aescape/mode/activateExecutionController')" class="btn btn-primary">
Massage Mode
</button>
<button id="readyModeButton" type="button" onclick="triggerService('/aescape/mode/activateReadyController')" class="btn btn-primary">
Ready Mode
</button>
<button id="standbyModeButton" type="button" onclick="triggerService('/aescape/mode/activateStandbyController')" class="btn btn-primary">
Standby Mode
</button>
<button id="stoppedModeButton" type="button" onclick="triggerService('/aescape/mode/stopControllers')" class="btn btn-primary">
Stopped Mode
</button>
</div>
<div class="col-md-6">
Safety Monitor Status:
<div class="row-md-3">
<h4><span id="safetyRunning" class="label label-default">Running</span></h4>
</div>
<div class="row-md-3">
<h4><span id="safetyStopped" class="label label-default">Stopped</span></h4>
</div>
<div class="row-md-3">
<button type="button" onclick="triggerService('/aescape/mode/activateSafetyController')" class="btn btn-sm btn-default">
Activate Safety Monitor
</button>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Franka Arm Status
</div>
<div class="panel-body text-center">
<div class="col-md-4">
Current Robot Status:
<!-- <div class="col-md-3" id="FrankaStatus">
N/A
</div> -->
<div class="row-md-3">
<h4><span id="frankaModeOther" class="label label-default">Other</span></h4>
</div>
<div class="row-md-3">
<h4><span id="frankaModeIdle" class="label label-default">Idle</span></h4>
</div>
<div class="row-md-3">
<h4><span id="frankaModeMove" class="label label-default">Move</span></h4>
</div>
<div class="row-md-3">
<h4><span id="frankaModeGuiding" class="label label-default">Guiding</span></h4>
</div>
<div class="row-md-3">
<h4><span id="frankaModeReflex" class="label label-default">Reflex</span></h4>
</div>
<div class="row-md-3">
<h4><span id="frankaModeUserStopped" class="label label-default">User Stopped</span></h4>
</div>
<div class="row-md-3">
<h4><span id="frankaModeErrorRecovery" class="label label-default">Automatic Error Recovery</span></h4>
</div>
</div>
<button id="fixFrankaButton" type="button" onclick="triggerService('/aescape/hardware/resetFrankaError')" class="btn btn-primary">
Fix Franka Errors
</button>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Robotiq
</div>
<div class="panel-body text-center">
<div class="col-md-4">
Current Robotiq Values:
<div class="row-md-3">
<h4>X: <span id="robotiqX" class="label label-default">0.0</span></h4>
</div>
<div class="row-md-3">
<h4>Y: <span id="robotiqY" class="label label-default">0.0</span></h4>
</div>
<div class="row-md-3">
<h4>Z: <span id="robotiqZ" class="label label-default">0.0</span></h4>
</div>
</div>
<div class="row-md-3">
<button id="calibrateButton" type="button" onclick="triggerService('/aescape/hardware/calibrateRobotiq')" class="btn btn-primary">
Calibrate Robotiq
</button>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Teaching Mode Operations
</div>
<div class="panel-body text-center">
<span class="row">
<label id="RecordingStatusLabel" class="label-warning">
Bag manager not connected!
</label>
</span>
<button type="button" onclick="triggerService('/aescape/mode/activateReadyController')" class="btn btn-default">
Activate Ready Mode
</button>
<button id="recordingStartButton" type="button" onclick="triggerService('/aescape/hardware/calibrateRobotiq'); triggerService('/aescape/mode/activateTeachingController'); triggerService('/aescape/bags/startTeachRecording')" class="btn btn-primary">
Start Teach Recording
</button>
<button id="recordingStopButton" type="button" onclick="triggerService('/aescape/bags/stopTeachRecording'); triggerService('/aescape/mode/activateReadyController')" class="btn btn-primary">
Stop Teach Recording
</button>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Execution Mode Operations
</div>
<div class="panel-body text-center">
<span class="row">
<label id="ExecutionStatusLabel" class="label-warning">
Bag manager not connected!
</label>
</span>
Last Bag Playing:
<span id="lastbagText">
None
</span>
<div>
<button type="button" onclick="triggerService('/aescape/mode/activateReadyController')" class="btn btn-default">
Activate Ready Mode
</button>
<button id="executionStartButton" type="button" onclick="triggerService('/aescape/mode/activateExecutionController'); triggerService('/aescape/bags/startPlayingLastRecording')" class="btn btn-primary">
Play Last Recording
</button>
<button id="executionStopButton" type="button" onclick="triggerService('/aescape/bags/stopPlayingBag'); triggerService('/aescape/mode/activateReadyController')" class="btn btn-primary">
Stop Playing Recording
</button>
<div class="row">
<div class="col-md-3">
Bagfile name must not start with "/"
<div class="input-group">
<input type="text" id="bagNameText" class="form-control" placeholder="bag_filename">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" onclick="triggerMessageService('/startPlayingRecording', 'bagNameText')">
<span>
Start Playing Bag
</span>
</button>
</span>
</div>
</div>
</div>
<!-- </div> -->
</div>
</div>
<!-- <div class="panel panel-default">
<div class="panel-heading">
Execution Recording Operations
</div>
<div class="panel-body text-center">
<button id="executionStartButton" type="button" onclick="triggerService('/startExecutionRecording')" class="btn btn-primary">
Start Recording Execution
</button>
<button id="executionRecordingStopButton" type="button" onclick="triggerService('/stopExecutionRecording')" class="btn btn-primary">
Stop Recording Execution
</button>
</div>
</div> -->

View File

@@ -0,0 +1,261 @@
///////////////////////////////////////////////////////////////////////////////////
// Publishers
///////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Topics
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Subscribers
////////////////////////////////////////////////////////////////
// Operation Mode
var modeStatus = new ROSLIB.Topic({
ros : ros,
name : '/aescape/mode/status',
messageType : 'std_msgs/String',
throttle_rate : 500 // 2Hz
});
modeStatus.subscribe(function(message) {
document.getElementById("stoppedModeButton").className = "btn btn-secondary"
document.getElementById("standbyModeButton").className = "btn btn-primary"
document.getElementById("readyModeButton").className = "btn btn-primary"
document.getElementById("teachingModeButton").className = "btn btn-primary"
document.getElementById("executionModeButton").className = "btn btn-primary"
document.getElementById("recordingStartButton").disabled = true
document.getElementById("executionStartButton").disabled = true
if (message.data == "stopped")
{
document.getElementById("stoppedModeButton").className = "btn btn-warning"
}
else if (message.data == "standby")
{
document.getElementById("standbyModeButton").className = "btn btn-primary btn-success"
}
else if (message.data == "teach")
{
document.getElementById("teachingModeButton").className = "btn btn-primary btn-success"
}
else if (message.data == "execution")
{
document.getElementById("executionModeButton").className = "btn btn-primary btn-success"
}
else if (message.data == "ready")
{
document.getElementById("readyModeButton").className = "btn btn-primary btn-success"
document.getElementById("recordingStartButton").disabled = false
document.getElementById("executionStartButton").disabled = false
}
});
// Safety Status
var safetyStatusTopic = new ROSLIB.Topic({
ros : ros,
name : '/aescape/mode/safety_status',
messageType : 'std_msgs/String',
throttle_rate : 500 // 2Hz
});
safetyStatusTopic.subscribe(function(message) {
document.getElementById("safetyRunning").className = 'label label-default';
document.getElementById("safetyStopped").className = 'label label-default';
if (message.data == "stopped")
{
document.getElementById("safetyStopped").className = 'label label-danger';
}
else if (message.data == "running")
{
document.getElementById("safetyRunning").className = 'label label-success';
}
});
// Recording Bag
var recordingBagTopic = new ROSLIB.Topic({
ros : ros,
name : '/aescape/bags/recording_status',
messageType : 'std_msgs/String'
});
recordingBagTopic.subscribe(function(message) {
if (message.data == "stopped")
{
document.getElementById("RecordingStatusLabel").innerHTML = 'Not Running';
document.getElementById("RecordingStatusLabel").className = 'label label-warning';
}
else if (message.data == "running")
{
document.getElementById("RecordingStatusLabel").innerHTML = 'RUNNING!';
document.getElementById("RecordingStatusLabel").className = 'label label-success';
}
});
// Executing Bag
var executingBagTopic = new ROSLIB.Topic({
ros : ros,
name : '/aescape/bags/execution_status',
messageType : 'std_msgs/String'
});
executingBagTopic.subscribe(function(message) {
if (message.data == "stopped")
{
document.getElementById("ExecutionStatusLabel").innerHTML = 'Not Running';
document.getElementById("ExecutionStatusLabel").className = 'label label-warning';
}
else if (message.data == "running")
{
document.getElementById("ExecutionStatusLabel").innerHTML = 'RUNNING!';
document.getElementById("ExecutionStatusLabel").className = 'label label-success';
}
});
// Last Bag
var bagPlayingTopic = new ROSLIB.Topic({
ros : ros,
name : '/aescape/bags/last_played',
messageType : 'std_msgs/String'
});
bagPlayingTopic.subscribe(function(message) {
document.getElementById("lastbagText").innerHTML = message.data
});
// FrankaState
var frankaStatus = new ROSLIB.Topic({
ros : ros,
name : '/franka_state_controller/franka_states',
messageType : 'franka_msgs/FrankaState',
throttle_rate : 500 // 2Hz
});
frankaStatus.subscribe(function(message) {
document.getElementById("frankaModeOther").className = "label label-default"
document.getElementById("frankaModeIdle").className = "label label-default"
document.getElementById("frankaModeMove").className = "label label-default"
document.getElementById("frankaModeGuiding").className = "label label-default"
document.getElementById("frankaModeReflex").className = "label label-default"
document.getElementById("frankaModeUserStopped").className = "label label-default"
document.getElementById("frankaModeErrorRecovery").className = "label label-default"
document.getElementById("fixFrankaButton").className = "btn btn-primary"
if (message.robot_mode == 0)
{
document.getElementById("frankaModeOther").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-danger"
}
else if (message.robot_mode == 1)
{
document.getElementById("frankaModeIdle").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-danger"
}
else if (message.robot_mode == 2)
{
document.getElementById("frankaModeMove").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-success"
}
else if (message.robot_mode == 3)
{
document.getElementById("frankaModeGuiding").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-default"
}
else if (message.robot_mode == 4)
{
document.getElementById("frankaModeReflex").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-danger"
}
else if (message.robot_mode == 5)
{
document.getElementById("frankaModeUserStopped").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-default"
}
else if (message.robot_mode == 6)
{
document.getElementById("frankaModeErrorRecovery").className = "label label-warning"
document.getElementById("fixFrankaButton").className = "btn btn-default"
}
});
// Robotiq Data
var robotiqDataTopic = new ROSLIB.Topic({
ros : ros,
name : '/robotiq_ft_wrench',
messageType : 'geometry_msgs/WrenchStamped',
throttle_rate : 500 // 2Hz
});
robotiqDataTopic.subscribe(function(message) {
var force = message.wrench.force
document.getElementById("robotiqX").innerHTML = force.x.toFixed(1)
document.getElementById("robotiqY").innerHTML = force.y.toFixed(1)
document.getElementById("robotiqZ").innerHTML = force.z.toFixed(1)
if ((Math.abs(force.x) > 0.5) || (Math.abs(force.y) > 0.5) || (Math.abs(force.z) > 0.5))
{
document.getElementById("calibrateButton").className = "btn btn-danger"
} else {
document.getElementById("calibrateButton").className = "btn btn-default"
}
});
////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////
function triggerService(serviceName)
{
var service = new ROSLIB.Service({
ros : ros,
name : serviceName,
serviceType : 'std_srvs/Trigger'
});
var request = new ROSLIB.ServiceRequest({});
service.callService(request, function(result) {
console.log('Result for service call on '
+ serviceName
+ ': '
+ result.sum);
});
}
function triggerMessageService(serviceName, textInput)
{
var text = document.getElementById(textInput).value
var service = new ROSLIB.Service({
ros : ros,
name : serviceName,
serviceType : 'demobot.TriggerMessage'
});
var request = new ROSLIB.ServiceRequest({
message : text
});
service.callService(request, function(result) {
console.log('Result for service call on '
+ serviceName
+ ': '
+ result.sum);
});
}

View File

View File

@@ -42,61 +42,3 @@
</div>
<!-- ##################################### D-PAD ######################################### -->
<!--
<p>Click in the actual image. Relative coordinates will be displayed.</p>
<p id="rez"></p>
<div>
<img id="myImage" src="/backend/images/D-pad.png">
-->
<!-- ##################################### END D-PAD ######################################### -->
<!-- ##################################### Example Progress Bar ######################################### -->
<div class="panel panel-default">
<div class="panel-heading">
Example Pannel Heading
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-2">
<label>
Bar Title
</label>
</div>
<div class="col-md-2">
<div class="progress">
<div id="progressBar1" class="progress-bar progress-bar-striped active" role="progressbar"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width:100%" >
No data recieved yet. </div>
</div>
</div>
</div>
</div>
</div>
<!-- ##################################### END Example Progress Bar ######################################### -->
<!-- ##################################### Example Image ######################################### -->
<div class="panel panel-default">
<div class="panel-heading">
Camera Image
</div>
<div class="panel-body">
<div class="col-lg-4">
<img height="350" alt="No Camera Image" id="imageStream" src="http://localhost:8080/stream?topic=/camera/image"></img>
</div>
</div>
</div>
<!-- ##################################### END Example Image ######################################### -->

View File

@@ -1,44 +0,0 @@
function sendRosMessageExample()
{
publishStringExample(String(document.getElementById("sendRosMessageExample").value))
}
function update_sendRosMessageExample(msg)
{
document.getElementById("recievedMessageDiv").innerHTML = msg
}
function update_ProgressBar(value)
{
// This would be a great way of getting a float or int from a rosTopic and displaying it.
// I would suggest storing the data as a data field in the ros_scripts then updating it every 500 or 1000 ms rather than updating the gui every time you recieve a message
var num_string = "";
num_string = num_string.concat(value);
num_string = num_string.substring(0,4);
num_string = num_string.concat("%");
doc = document.getElementById("progressBar1");
doc.style.width = num_string;
doc.innerHTML = num_string;
if (value < 50 && value > 25 ) {
doc.className = "progress-bar progress-bar-warning";
}
else if ( value <= 25)
{
doc.className = "progress-bar progress-bar-danger";
}
else
{
doc.className = "progress-bar progress-bar-success";
}
}
window.setInterval(function(){
update_ProgressBar(Math.random() * 100);
}, 1000);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8" />
<title>Lab UI</title>
<!--
Link to included files. These are typically done by fetching from the web.
Because this needs to run without an internet conection, the files were saved
@@ -26,87 +27,80 @@ Tutorials can be found here: http://www.w3schools.com/bootstrap/default.asp
<script type="text/javascript" src="backend/js/Topic.js"></script>
<audio id="audio" src="audio/beep-04.mp3" autostart="false" ></audio>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
$(function(){
$("#includedContent").load("data_display/data_display.html");
$("#coordinatorContent").load("coordinator/coordinator.html");
});
</script>
<script type="text/javascript" src="coordinator/js/ros_scripts.js"></script>
<script type="text/javascript" src="coordinator/js/update_guis.js"></script>
<script type="text/javascript" src="data_display/js/ros_scripts.js"></script>
<script type="text/javascript" src="data_display/js/update_guis.js"></script>
<!-- <script>
$(function(){
$("#visionContent").load("vision/vision.html");
});
</script>
<script type="text/javascript" src="vision/js/ros_scripts.js"></script>
<script type="text/javascript" src="vision/js/update_guis.js"></script> -->
<meta content="text/html; charset=UTF-8; X-Content-Type-Options=nosniff" http-equiv="Content-Type" />
</head>
<body>
<div class="container">
<div id="jumbotronTitle" class="jumbotron">
<h1>ROS Display</h1>
</div>
<!-- ##################################### ROS BAG PANEL ######################################### -->
<!-- ROS Bag Panel -->
<div class="panel panel-default">
<div class="panel-heading">
ROS Bag
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-3">
Bagfile name must not start with "/"
<div class="input-group">
<input type="text" id="recordText" class="form-control" placeholder="bag_filename">
<span class="input-group-btn">
<button class="btn btn-primary" id="recordButton" type="button" onclick="toggleRecording()">
<span id="recordButtonText">
Start
</span>
</button>
</span>
</div>
</div>
</div>
</div>
<div id="jumbotronTitle" class="jumbotron text-center">
<h1>Lab UI</h1>
</div>
<!-- ##################################### END ROS BAG PANEL ######################################### -->
<ul id="tabs" class="nav nav-pills" data-tabs="tabs" >
<li class="active">
<a href="#DataDisplay" data-toggle="tab">
Data Display
</a>
</li>
<li> <a href="#rosbridgeconnection" data-toggle="tab">
ROS Bridge Connection
<span id="rosbridgeconnection_badge" class="badge">
Not Connected
</span></a></li>
</ul>
<div class="container">
<div class="col-md-1">
Master:
</div>
<div class="col-md-3">
<label id="MasterName"></label>
<script>
getMasterName();
</script>
</div>
</div>
<div class="row-md-3">
<ul id="tabs" class="nav nav-pills" data-tabs="tabs" >
<li> <a href="#rosbridgeconnection" data-toggle="tab">
ROS Bridge Connection
<span id="rosbridgeconnection_badge" class="badge">
Not Connected
</span></a>
</li>
<li class="active">
<a href="#Coordinator" data-toggle="tab">
Coordinator
</a>
</li>
<li>
<a href="vision/vision.html">
Vision
</a>
</li>
</ul>
</div>
<div id="my-tab-content" class="tab-content">
<div class="tab-pane active" id="DataDisplay">
<!-- ##################################### ROBOT DISPLAY PANEL ######################################### -->
<div id="includedContent"></div>
<!-- ##################################### END DISPLAY PANEL ######################################### -->
<div class="tab-pane active" id="Coordinator">
<div id="coordinatorContent"></div>
</div>
<!-- <div class="tab-pane" id="Vision">
<div id="visionContent"></div>
</div> -->
<div class="tab-pane" id="rosbridgeconnection">
<div class="panel panel-default">
<div class="panel-body">
@@ -132,18 +126,53 @@ Tutorials can be found here: http://www.w3schools.com/bootstrap/default.asp
</div>
</span>
</div>
</form>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Predefined ROSBridge Connections
</div>
<div class="panel-body">
<button id="localhostButton" type="button" onclick="ros.attemptConnection('ws://localhost:9090')" class="btn btn-primary">
localhost
<script type="text/javascript">
document.getElementById("ConnectionIPInput").value = ros.connectionName;
</script>
</button>
<button id="TitanButton" type="button" onclick="ros.attemptConnection('ws://titan.local:9090')" class="btn btn-primary">
Titan
<script type="text/javascript">
document.getElementById("ConnectionIPInput").value = ros.connectionName;
</script>
</button>
<button id="PhoebeButton" type="button" onclick="ros.attemptConnection('ws://phoebe.local:9090')" class="btn btn-primary">
Phoebe
<script type="text/javascript">
document.getElementById("ConnectionIPInput").value = ros.connectionName;
</script>
</button>
<button id="RheaButton" type="button" onclick="ros.attemptConnection('ws://rhea.local:9090')" class="btn btn-primary">
Rhea
<script type="text/javascript">
document.getElementById("ConnectionIPInput").value = ros.connectionName;
</script>
</button>
</div>
</div>
</div>
<script type="text/javascript">
// Create a connection to the rosbridge WebSocket server.
ros.connect(ros.connectionName);
</script>
<div class="panel panel-default">
<div class="panel-heading">
ROS Topics. Checked topics will be bagged when "Start" is selected.
ROS Topics.
</div>
<div class="panel-body">
<div id="ROSTopics">
<div class="col-sm-1" id="ROSTopics">
N/A
</div>
</div>
@@ -151,7 +180,7 @@ Tutorials can be found here: http://www.w3schools.com/bootstrap/default.asp
<div class="panel panel-default">
<div class="panel-heading">
ROS Nodes
ROS Nodes.
</div>
<div class="panel-body">

View File

@@ -1,26 +0,0 @@
#!/bin/bash
kill_child_processes() {
isTopmost=$1
curPid=$2
childPids=`ps -o pid --no-headers --ppid ${curPid}`
for childPid in $childPids
do
kill_child_processes 0 $childPid
done
if [ $isTopmost -eq 0 ]; then
kill -9 $curPid 2> /dev/null
fi
}
# Ctrl-C trap. Catches INT signal
trap "kill_child_processes 1 $$; exit 0" INT
chmod a+x src/bagger.py
x-terminal-emulator -e "roslaunch rosbridge_gui all.launch" &
x-terminal-emulator -e "python -m SimpleHTTPServer 8000" &
x-terminal-emulator -e "rosrun web_video_server web_video_server"

View File

@@ -1,5 +0,0 @@
<launch>
<include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" />
<include file="$(find rosbridge_gui)/launch/bagger.launch" />
<include file="$(find rosbridge_gui)/launch/talker.launch" />
</launch>

View File

@@ -1,3 +0,0 @@
<launch>
<node name="bagger" pkg="rosbridge_gui" type="bagger.py" />
</launch>

8
launch/lab_ui.launch Executable file
View File

@@ -0,0 +1,8 @@
<launch>
<include file="$(find rosbridge_server)/launch/rosbridge_websocket.launch" />
<node name="lab_ui_web_server" pkg="aescape_lab_ui" type="webserver.sh" respawn="false" />
<node name="lab_ui_video_server" pkg="web_video_server" type="web_video_server" respawn="false" />
</launch>

View File

@@ -1,3 +0,0 @@
<launch>
<node name="talker" pkg="rosbridge_gui" type="talker.py" />
</launch>

View File

@@ -1,13 +1,13 @@
<?xml version="1.0"?>
<package>
<name>rosbridge_gui</name>
<name>aescape_lab_ui</name>
<version>0.0.0</version>
<description>The rosbridge_gui package</description>
<description>The aescape_lab_ui package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="turner.glen@gmail.com">glen</maintainer>
<maintainer email="david@aescape.co">David</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
@@ -41,6 +41,8 @@
<run_depend>rospy</run_depend>
<run_depend>std_msgs</run_depend>
<run_depend>web_video_server</run_depend>
<run_depend>rosbridge_server</run_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

7
scripts/webserver.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
cd $(rospack find aescape_lab_ui)
# python3 -m http.server 8000
# Node webserver: https://www.npmjs.com/package/http-server
http-server -p 8000

View File

@@ -1,49 +0,0 @@
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
import rospkg
import subprocess
import os
import signal
class Bagger(object):
def __init__(self):
rospy.init_node("bagger", anonymous=True)
rospy.Subscriber("bag_publisher", String, self.callback)
self.pub = rospy.Publisher("bag_notifier", String, queue_size=10)
self.proc = None
rospack = rospkg.RosPack()
self.data_path = os.path.join(rospack.get_path("rosbridge_gui"), "data")
def callback(self, msg):
if msg.data == "STOP" and self.proc is not None:
os.killpg(self.proc.pid, signal.SIGINT)
self.pub.publish("STOPPED")
else:
msg_data = msg.data.split()
self.pub.publish(str(len(msg_data)))
self.pub.publish(str(msg_data))
if len(msg_data) == 0: # We only got the name of the bag file and not any topics
bag_file_name = os.path.join(self.data_path, "bag_file")
self.proc = subprocess.Popen(["rosbag", "record",
"--all", "-o", bag_file_name], preexec_fn=os.setsid)
self.pub.publish("STARTED")
elif len(msg_data) == 1: # We only got the name of the bag file and not any topics
bag_file_name = os.path.join(self.data_path, msg_data)
self.proc = subprocess.Popen(["rosbag", "record",
"--all", "-o", bag_file_name], preexec_fn=os.setsid)
self.pub.publish("STARTED")
else: #we posibly have a bag file name and a list of topics
if msg_data[0].startswith("/"): #then we know there is no bag file name
msg_data = ["bagfile"] + msg_data
msg_data[0] = os.path.join(self.data_path, msg_data[0])
processList = ["rosbag", "record", "-o"] + msg_data
self.pub.publish(" ".join(processList))
self.proc = subprocess.Popen( processList, preexec_fn=os.setsid)
self.pub.publish("STARTED")
if __name__ == '__main__':
Bagger()
rospy.spin()

View File

@@ -1,20 +0,0 @@
#!/usr/bin/env python
# license removed for brevity
import rospy
from std_msgs.msg import String
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10)
rospy.init_node('talker', anonymous=True)
rate = rospy.Rate(10) # 10hz
while not rospy.is_shutdown():
hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str)
pub.publish(hello_str)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass

104
vision/js/ros_scripts.js Normal file
View File

@@ -0,0 +1,104 @@
///////////////////////////////////////////////////////////////////////////////////
// Publishers
///////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Topics
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Subscribers
////////////////////////////////////////////////////////////////
// Operation Mode
var modeStatus = new ROSLIB.Topic({
ros : ros,
name : '/aescape/mode/status',
messageType : 'std_msgs/String'
});
modeStatus.subscribe(function(message) {
document.getElementById("stoppedModeButton").className = "btn btn-secondary"
document.getElementById("standbyModeButton").className = "btn btn-primary"
document.getElementById("teachingModeButton").className = "btn btn-primary"
document.getElementById("executionModeButton").className = "btn btn-primary"
if (message.data === "stopped") {
document.getElementById("stoppedModeButton").className = "btn btn-warning"
} else if (message.data === "standby")
{
document.getElementById("standbyModeButton").className = "btn btn-primary btn-success"
} else if (message.data === "teach")
{
document.getElementById("teachingModeButton").className = "btn btn-primary btn-success"
} else if (message.data === "execution")
{
document.getElementById("executionModeButton").className = "btn btn-primary btn-success"
}
});
// Playing Bag
var bagPlayingTopic = new ROSLIB.Topic({
ros : ros,
name : '/aescape/bags/playing',
messageType : 'std_msgs/String'
});
bagPlayingTopic.subscribe(function(message) {
document.getElementById("bagPlayingText").innerHTML = message.data
});
////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////
function triggerService(serviceName)
{
var service = new ROSLIB.Service({
ros : ros,
name : serviceName,
serviceType : 'std_srvs/Trigger'
});
var request = new ROSLIB.ServiceRequest({});
service.callService(request, function(result) {
console.log('Result for service call on '
+ serviceName
+ ': '
+ result.sum);
});
}
function triggerMessageService(serviceName, textInput)
{
var text = document.getElementById(textInput).value
var service = new ROSLIB.Service({
ros : ros,
name : serviceName,
serviceType : 'demobot.TriggerMessage'
});
var request = new ROSLIB.ServiceRequest({
message : text
});
service.callService(request, function(result) {
console.log('Result for service call on '
+ serviceName
+ ': '
+ result.sum);
});
}

0
vision/js/update_guis.js Normal file
View File

122
vision/vision.html Normal file
View File

@@ -0,0 +1,122 @@
<!-- <div class="panel panel-default">
<div class="panel-heading">
Select Operation Mode
</div>
<div class="panel-body text-center">
<button id="teachingModeButton" type="button" onclick="triggerService('/aescape/mode/activateTeachingController')" class="btn btn-primary">
Teaching Mode
</button>
<button id="executionModeButton" type="button" onclick="triggerService('/aescape/mode/activateExecutionController')" class="btn btn-primary">
Massage Mode
</button>
<button id="standbyModeButton" type="button" onclick="triggerService('/aescape/mode/activateStandbyController')" class="btn btn-primary">
Standby Mode
</button>
<button id="stoppedModeButton" type="button" onclick="triggerService('/aescape/mode/stopControllers')" class="btn btn-primary">
Stopped Mode
</button>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Hardware Commands
</div>
<div class="panel-body text-center">
<button id="fixFrankaButton" type="button" onclick="triggerService('/aescape/hardware/resetFrankaError')" class="btn btn-primary">
Fix Franka Errors
</button>
<button id="calibrateButton" type="button" onclick="triggerService('/aescape/hardware/calibrateRobotiq')" class="btn btn-primary">
Calibrate Robotiq
</button>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Teaching Mode Operations
</div>
<div class="panel-body text-center">
<button id="recordingStartButton" type="button" onclick="triggerService('/aescape/bags/startTeachRecording')" class="btn btn-primary">
Start Teach Recording
</button>
<button id="recordingStopButton" type="button" onclick="triggerService('/aescape/bags/stopTeachRecording')" class="btn btn-primary">
Stop Teach Recording
</button>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Execution Mode Operations
</div>
<div class="panel-body text-center">
Last Bag Playing:
<span id="bagPlayingText">
None
</span>
<div>
<button id="executionStartButton" type="button" onclick="triggerService('/aescape/bags/startPlayingLastRecording')" class="btn btn-primary">
Play Last Recording
</button>
<button id="executionStopButton" type="button" onclick="triggerService('/aescape/bags/stopPlayingBag')" class="btn btn-primary">
Stop Playing Recording
</button>
<div class="row">
<div class="col-md-3">
Bagfile name must not start with "/"
<div class="input-group">
<input type="text" id="bagNameText" class="form-control" placeholder="bag_filename">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" onclick="triggerMessageService('/startPlayingRecording', 'bagNameText')">
<span>
Start Playing Bag
</span>
</button>
</span>
</div>
</div>
</div>
</div>
</div> -->
<script type="text/javascript" src="vision/js/ros_scripts.js"></script>
<script type="text/javascript" src="vision/js/update_guis.js"></script>
<div class="panel panel-default">
<div class="panel-heading">
Camera Views
</div>
<div class="panel-body">
Webcam
<div class="row-lg-4">
<img height="350" alt="No Camera Image" id="imageStream" src="http://titan.aescape.co:8080/stream?topic=/webcam/image_raw&type=ros_compressed"></img>
</div>
Realsense Red
<div class="row-lg-4">
<img height="350" alt="No Camera Image" id="imageStream" src="http://titan.aescape.co:8080/stream?topic=/real_red/color/image_raw&type=ros_compressed"></img>
</div>
Realsense Green
<div class="row-lg-4">
<img height="350" alt="No Camera Image" id="imageStream" src="http://titan.aescape.co:8080/stream?topic=/real_green/color/image_raw&type=ros_compressed"></img>
</div>
Thermal
<div class="row-lg-4">
<img height="350" alt="No Camera Image" id="imageStream" src="http://titan.aescape.co:8080/stream?topic=/thermal/image_raw&type=ros_compressed"></img>
</div>
</div>
</div>
<!-- <div class="panel panel-default">
<div class="panel-heading">
Execution Recording Operations
</div>
<div class="panel-body text-center">
<button id="executionStartButton" type="button" onclick="triggerService('/startExecutionRecording')" class="btn btn-primary">
Start Recording Execution
</button>
<button id="executionRecordingStopButton" type="button" onclick="triggerService('/stopExecutionRecording')" class="btn btn-primary">
Stop Recording Execution
</button>
</div>
</div> -->