Remote Lab
Version 0.0.1
Introduction
The Remote Lab framework provides ready to deploy services for managing the scheduling, programming and control of remote assets such as development boards with remotely controllable periferals. In order to interact with a remote asset, a driver for the asset MUST be provided. Drivers are written for Node.js and MUST include a function for providing basic information, a function for starting the service and a function for stopping the service. It MAY also include a remote programming service and/or a remote control service.
Remote Lab services are designed to be delpoyed on a remote computer such as a Raspberry Pi. Each remote computer provides agent services that can communicate with other remote systems to form a multi-agent system. Agents on the same network within the same broadcast domain will automatically discover each other and share information about the services they provide. Due to this agent discovery mechanism any agent can be used to schedule and access other agents.
This document provides details of the Remote Lab components and instructions for developing drivers and for interacting with the respective services.
Architecture
The Remote Lab system is divided into a series of backend services, frontend, web-based user interfaces and hardware. Front end services are typically in the form of a rich browser application that communicates with a backend ReST service. For example, the Remote Lab front end is a browser application developed with the Angular framework and communicates with the ellipsis-embedded ReST API which is a Node.js application running on the Remote Lab Server.
A high-level overview of all components of a Remote Lab are laid out below. Installation details can be found in the Deployment section below.
Client Computer
Programmer Client (e.g. Vivado)
Remote Lab Server
(e.g. Raspberry Pi)
Apache Web Server
Remote Lab UI (html, css, js source | port 443)
Scheduling and device user interface.
Remote Programming UI (html, css, js source)
Device specific programming user interface; e.g. Virtual Dev Board
Remote Control UI (html, css, js source)
Device specific control user interface; e.g. Virtual Dev Board
Device Driver (Node.js module)
- Information: Version, Programming URL, Control URL
- Start Trigger: Function triggered when a schedule begins.
- Stop Trigger: Function triggered when a schedule ends.
Remote Lab UI
Remote Services
embeddedDevice
will automatically have their device registered.
Repository
Install Location
Remote Lab Server - Node.js application
Framework
Device Driver
info
, onStart
and onStop
functions.
Driver details are specified in a configuration file. For further details see Driver development.
Deployment
This section relates to the setup of a Raspberry Pi (or similar computer) with all required backend services. If you are using a Remote Lab SD card image on a Raspberry Pi (recommended) then only the following are needed:
- SD Card Setup (option 1)
- Azure AD App Registration
- Device specific setup
Note that you will need an internet connection for many of the below commands.
SD Card Setup with Raspberry Pi OS
Assuming you are building a remote lab on a Raspberry Pi you have two options:
- Flash a remote lab image onto an SD card, or
- Flash a Raspberry Pi OS image onto the SD card and manually install all services.
Option 1 is the easiest option and is recommended. You can use a tool such as Balena Etcher to flash the image which can be found here: TODO.
If you are deploying from a Raspberry Pi OS image then you can either use the Raspberry Pi Imager or download an image and use a tool such as Balena Etcher to flash the image. Some useful instruction on setting up a Pi image including SSH and Wifi can be found here. You will then need to continue to follow all steps below in this section to complete the Raspberry Pi software setup.
All remote lab specific configuration is in the blackbox
directory, so that should be created first:
sudo mkdir /opt/blackbox
sudo chown pi /opt/blackbox
Apache Deployment
Apache Web Server is used for browser based user interface hosting including the Remote Lab UI. Install Apache2 on a Raspberry Pi as follows:
sudo apt-get update
sudo apt-get upgrade
sudo apt install apache2 openssl -y
The Remote Lab UI is the starting point for a user to interact with a remote lab.
As such it must be the default page when browsing to the remote lab and is therefore deployed to Apache's default root /var/www/html/
.
Deploy the Remote Lab UI by executing the following commands on the remote lab computer (you may either run each line one by one or copy the below into a script file):
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/index.html --output /var/www/html/index.html
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/facicon.ico --output /var/www/html/favicon.ico
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/main.js --output /var/www/html/main.js
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/main.js.map --output /var/www/html/main.js.map
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/polyfills.js --output /var/www/html/polyfills.js
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/polyfills.js.map --output /var/www/html/polyfills.js.map
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/runtime.js --output /var/www/html/runtime.js
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/runtime.js.map --output /var/www/html/runtime.js.map
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/styles.css --output /var/www/html/styles.css
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/styles.css.map --output /var/www/html/styles.css.map
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/vendor.js --output /var/www/html/vendor.js
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/vendor.js.map --output /var/www/html/vendor.js.map
curl https://bitbucket.org/ellipsistechnology/remote-lab/raw/4356afcdf41bc81201c7fe20f812424e3f4b3949/dist/remote-lab/config.json --output /var/www/html/config.json
Apache will need to serve via HTTPS. To enable HTTPS:
sudo a2enmod ssl
sudo a2enmod rewrite
Then edit /etc/apache2/apache2.conf
and add the following at its end:
<Directory /var/www/html>
AllowOverride ALL
</Directory>
You will need to generate a certificate for which we can use openssl
installed previously.
First, create a directory to store the certificate:
mkdir /opt/blackbox/certs
cd /opt/blackbox/certs
Then create the certificate:
sudo openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out blackbox.crt -keyout blackbox.key
Fill in the answers as prompted, e.g.:
Generating a RSA private key
.......++++
............................................++++
writing new private key to 'blackbox.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:Tas
Locality Name (eg, city) []:Hobart
Organization Name (eg, company) [Internet Widgits Pty Ltd]:UTas
Organizational Unit Name (eg, section) []:Engineering
Common Name (e.g. server FQDN or YOUR name) []:2c1c9a75-d658-4fe5-80f7-fe7a39c3e360
Email Address []:
The certificates will then need to be configured in Apache.
Edit /etc/apache2/sites-enabled/000-default.conf
and add the following:
<VirtualHost *:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile /opt/blackbox/certs/blackbox.crt
SSLCertificateKeyFile /opt/blackbox/certs/blackbox.key
</VirtualHost>
In the port 80 VirtualHost
add the following to redirect users to use HTTPS:
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]
Restart apache and we're ready to test:
sudo service apache2 restart
Open the Remote Lab in your browser using it's IP address. Note that if you are using a self signed certificate as per the above instructions, then you will need to import the certificate and trust it before your browser will allow you to see the page.
If all works correctly you should be redirected to HTTPS, then redirected to login with Azure AD. For the Azure AD login to succeed you will need to follow the below instructions. Once logged in you will need the Remote Services installed and running through the Node.js Deployment before you can interact with any devices.
Azure AD App Registration
User login is managed through Azure AD. You will need to register the remote lab application with Azure in order to allow users to log in:
- Open your Azure AD portal via https://portal.azure.com/.
- Navigate to Azure AD Security: Click on All Services, then Identity, then Azure Active Directory.
- Click on App registrations.
- Click on New registration.
- Fill in requested details as required, making sure that the Redirect URI is set to the specific remote lab address.
Once you have registered the application you will need to take note of the Application (client) ID and copy it into the config file:
- On the Raspberry Pi, open
/var/www/html/config.json
for editing. - Enter the Application ID into
authentication/appID
, save and close the file.
Authentication should now automatically log you in when you navigate to the Remote Lab web page.
Node.js Deployment
The Remote Services used for managing devices and schedules are web servers running on Node.js. Install Node.js on the Raspberry Pi as follows (make sure you are in a temporary directory such as ~/tmp):
wget https://nodejs.org/download/release/v14.3.0/node-v14.3.0-linux-armv7l.tar.gz
tar -xzf node-v14.3.0-linux-armv7l.tar.gz
cd node-v14.3.0-linux-armv7l/
sudo cp -R * /usr/local/
To check that the install was successful, run the following command:
node -v
The output should be v14.3.0
.
To install the Remote Services execute the following commands:
cd /opt/blackbox
mkdir ellipsis-embedded
cd ellipsis-embedded
curl https://bitbucket.org/ellipsistechnology/ellipsis-embedded/raw/bbc86a9a184935d8822f33ab77f6f2d884dd53be/prod/ellipsis-embedded-services_0.0.1.zip --output ellipsis-embedded.zip
unzip ellipsis-embedded.zip
rm ellipsis-embedded.zip
The remote services should run on startup and run as a background task.
To set that up create /lib/systemd/system/remotelab.service
and add the following to it:
[Unit]
Description=Remote Lab
After=multi-user.target
[Service]
Type=idle
ExecStart=/usr/local/bin/node /opt/blackbox/ellipsis-embedded/index.js
[Install]
WantedBy=multi-user.target
Set the service file's permissions:
sudo chmod 644 /lib/systemd/system/remotelab.service
Then make the service run during boot:
sudo systemctl daemon-reload
sudo systemctl enable remotelab.service
sudo reboot
If you have any issues at this point (e.g. the service is not running) you can check by running
sudo systemctl list-units --failed
and if it has failed view the log with
journalctl -u remotelab.service -b
Note that the service will be running on port 8100 over HTTP by default.
You will need to configure TLS in the Agent Configuration file.
You can reuse the certificates created when setting up Apache above;
i.e. /opt/blackbox/certs/blackbox.crt
and /opt/blackbox/certs/blackbox.key
.
Camera Setup
https://www.ics.com/blog/raspberry-pi-camera-module#.VJFhbyvF-b8Motion - also not so good
ref. https://raspberry-valley.azurewebsites.net/Streaming-Video-with-Motion/
Old Process - FAILED
sudo apt-get install git
sudo apt-get install cmake
cd ~
git clone http://github.com/raspberrypi/userland
./buildme
Proxy
Agent Configuration
The ellipsis-embedded project
is configured via /etc/blackbox-config.json
which MUST have the following format:
{
"agent": {
"name": "<agent name>",
"port": "<service port>",
"tls": {
"key": "<file path>",
"cert": "<file path>",
"ca": "<file path, optional>"
}
},
"devices": [
{
"name": "<device name>",
"status": "<'stopped', 'starting' or 'running'>",
"type": "<device type; e.g. basys3>",
"programmer": {
"type": "<programmer type; e.g. xvc>"
},
"location": "<'local' or 'remote'>",
"driver": {
"path": "<path to js file>",
"onStart": "<start function name>",
"onStop": "<stop function name>",
"getInfo": "<info function name>"
}
}
]
}
The default port is 8100 and the agent will be polled on this port for device information.
If the tls
property is present then the ellipsis-embedded server will use HTTPS, otherwise is will default to HTTP.
Currently, most device fields are for information only and are not used except for display to a user. The device driver setup is used by Remote Services to interact with device specific software. For more information on setting up a driver for a custom device, refer to Driver Development.
Driver Development
Devices are configured through a driver object in the agent configuration. A device driver is used by Remote Services to interact with device specific software. There are three points at which the driver functions will be called: Device registration, device start, device stop. The start and stop events are triggered by the schedule managed by the Remote Services.
A driver is simply a Node.js compatible Javascript file that includes the following 3 functions:
getInfo({req})
Returns a JSON object with device information.onStart({owner})
Triggered when the device schedule starts.onStop()
Triggered when the device schedule ends.
Driver Functions
getInfo(context)
The getInfo
function provides general information specific to the device.
It MAY be given a different name by specifying the driver getInfo
property in the Configuration file.
Parameters
context
A JSON object including contextural information relating to the origins of the information request.
Currently this only contains a req
member that is the HTTP request object.
Return Value
The return value MUST be either a JSON object containing device information or a Promise to return such an object.
These details will be displayed to the user through the Remote Lab UI
when a specific device is selected.
Additionally, programUrl
and controlUrl
string properties MAY be added to the returned object
which will be used to embed a program and a control user interface in the Remote Lab UI
when a specific device is selected.
onStart(state)
The onStart
function is called when a device start event is triggered by the commencement of a schedule.
Note that this may be triggered at any time during a schedule and may be triggered multiple times.
It MAY be given a different name by specifying the driver onStart
property in the Configuration file.
Parameters
state
A JSON object that includes device state information.
Currently this only contains an owner
member that is a string specifying the owner's credentials.
The owner of a device is the user owning the current booking if an active booking exists, otherwise undefined
will be returned.
Return Value
The return value MUST be either undefined
or a Promise that resolves when the start procedure is complete.
onStop()
The onStop
function is called when a device stop event is triggered by the ending of a schedule.
It MAY be given a different name by specifying the driver onStop
property in the Configuration file.
Return Value
The return value MUST be either undefined
or a Promise that resolves when the stop procedure is complete.
Agent Discovery
The discovery service MUST be compatible with the blackbox-discovery protocol. This simply broadcasts messages on port 5555 and listens for broadcasts from agents on the same port.
Messages have the following format:
BB ANNOUNCE <Agent Info>where
<Agent Info>
is a JSON object conforming to the following definition:
{
name: string
path: string
port: number
address?: string
protocol?: string
}
name
The agent's name used for identification.path
The agent's ReST API root path.port
The agent's ReST API port.address
The agent's ReST API address.protocol
The agent's ReST API protocol.
If using the ellipsis-embedded project then these announcements will
begin automatically when the service is started and a Blackbox ReST API will be made available. If using blackbox-discovery
directly (without ellipsis-embedded) you will need to create an Agent
and start announcing:
import {Agent} from 'blackbox-discovery'
...
const agent = new Agent({
name: 'some name',
path: '/',
port: 1234,
protocol: 'http'
})
agent.startAnnouncing()
If you need to listen for newly discovered agents you can push a new callback function onto the agent's listeners:
agent.listeners.push((discoveredAgent) => {
/* Do stuff. */
})