Hello ROS Developers,
This is the 2nd of 4 posts of the series Developing web interfaces for ROS Robots.
In this post, we are going to add some styles to the web page using a very popular framework: Bootstrap. Furthermore, we will add a JavaScript framework to make our code look better: Vue.js.
1 – Loading the libraries
First things first! Let’s import the libraries we are going to use.
In our html file (the only one so far), we add the following code to the <head> section:
<head> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <script type="text/javascript" src="https://static.robotwebtools.org/roslibjs/current/roslib.min.js"> </script> <script src="https://cdn.jsdelivr.net/npm/vue"> </script> </head>
The first tag (<link>) is used to add the Bootstrap framework. It’s a css file, that’s why it looks different than the <script> tags. It will allow us to create prettier elements in our page without having the work to stylish them.
The second tag is the same we have used in the previous post. It includes roslib.js.
Finally, the third tag includes Vue.js framework. This framework will make our JavaScript code quite different, but more organized.
2 – New appearance for the page
Let’s create a new page for our project.
We want to have something pretty and easy to understand. In order to do so, let’s create some new HTML elements. Our <body> section will look like below:
<body> <div id="app" class="container"> <div class="jumbotron"> <h1>Hello from ROSDS!</h1> </div> <div class="row" style="max-height:200px;"> <div class="col-md-6"> <h3>Connection status</h3> <p class="text-success">Connected!</p> <p class="text-danger">Not connected!</p> <label>Websocket server address</label> <input type="text" /> <br /> <button class="btn btn-danger">Disconnect!</button> <button class="btn btn-success">Connect!</button> </div> <div class="col-md-6" style="max-height:200px; overflow:auto;"> <h3>Log messages</h3> <div> </div> </div> </div> </div> </body>
And the page (make sure you have the webserver running like we did in the previous post) must look like this:
We have created many elements and they are being shown all together. Let’s improve it!
3 – Adding JavaScript with Vue.js
This time we are creating a separeted file to keep our JavaScript. Create a file main.js in the same folder of the index.html
Our file will contain the following code:
var app = new Vue({ el: '#app', // storing the state of the page data: { connected: false, ros: null, ws_address: 'ws://3.91.38.82:9090', logs: [], }, // helper methods to connect to ROS methods: { connect: function() { this.logs.unshift('connect to rosbridge server!!') this.ros = new ROSLIB.Ros({ url: this.ws_address }) this.ros.on('connection', () => { this.connected = true this.logs.unshift('Connected!') // console.log('Connected!') }) this.ros.on('error', (error) => { this.logs.unshift('Error connecting to websocket server') // console.log('Error connecting to websocket server: ', error) }) this.ros.on('close', () => { this.connected = false this.logs.unshift('Connection to websocker server closed') // console.log('Connection to websocket server closed.') }) }, disconnect: function() { this.ros.close() }, }, })
Let’s understand what we have there:
We start instantiating a Vue.js object. This object refers the element #app of the webpage. (The element identified by the attribute id=”app”). It means that everything inside this element can be managed by our JavaScript code.
The next attribute the called data. This attribute is an object that contains other attributes. These are defined for our application, they are custom attributes, not pre-defined by the framework.
We have defined a boolean connected. A ros object, that will handle the connection to rosbridge server. The address of the server, ws_address. And the last one logs, that is an array, which will contain messages of the events.
Then, we have the methods attribute. This is pre-defined by the framework and is the place to define functions to help us developing our application. We have created two: connect and disconnect.
The first one is very similar to what we have done in the previous post, but now interacting with the attributes inside data. We start the connect method creating a new log and pushing to the this.logs array. Then we define the connection object and assign the this.ros to handle it. Finally, the same callbacks we have defined before are defined here, but changing the value of the boolean connected and adding the logs to the array, instead of showing in the console.
The disconnect method is just closing the connection.
4 – Integrating with the webpage
In order to allow the main.js to do its “magic”, we need to adjust the html code. First thing is to include it at the end of the <body> section.
... <script type="text/javascript" src="main.js"> </script> </body>
Then, we need to adjust the elements to be manipulated.
The text message that represents the status of the connection must depend on the connected variable.
<p class="text-success" v-if="connected">Connected!</p> <p class="text-danger" v-else>Not connected!</p>
The <input> element must fill the variable ws_address and, vice-versa, the value of the variable must update the element:
<input type="text" v-model="ws_address" />
The buttons must trigger the methods we have defined and be shown depending on the connected value:
<button @click="disconnect" class="btn btn-danger" v-if="connected">Disconnect!</button> <button @click="connect" class="btn btn-success" v-else>Connect!</button>
And we want to iterate the logs array to show the messages we have added on the events changing:
<div class="col-md-6" style="max-height:200px; overflow:auto;"> <h3>Log messages</h3> <div> <p v-for="log in logs"> {{ log }} </p> </div> </div>
5 – Testing the webpage
Reload the page and connect to the address you have for rosbridge. Try the disconnect button as well and reconnect.
Check how they behave: connection message, buttons and logs. You must have something similar when you don’t have rosbridge running:
And when you have it running (remember to launch it using roslaunch rosbridge_server rosbridge_websocket.launch):
ROSJect created along the post:
This code is broken because it’s written in Vue2 but it’s importing latest Vue version (>=3.x)
To fix it change:
to:
This code is broken because it’s written in Vue2 but it’s importing latest Vue version (>=3.x)
To fix it change:
src=”https://unpkg.com/vue@3/dist/vue.global.js”
to:
src=”https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js”
PS. Apparently you can’t post html tags in comment because it gets deleted