From 8f14b59275d190826ef1f0907cb326f128338f64 Mon Sep 17 00:00:00 2001
From: Paul Beuchat <beuchatp@control.ee.ethz.ch>
Date: Tue, 28 Apr 2020 16:25:41 +0200
Subject: [PATCH] Web interface now working for launching both the master and
 agent. VERY IMPORTANT is the command added to the web interface install
 script that the www-data user must be added to the plugdev group so that
 nodes launched by the web interface have permission to access the CrazyRadio
 USB dongle

---
 dfall_ws/src/dfall_pkg/launch/pi_agent.launch | 144 ++++++++++++++++
 .../src/dfall_pkg/launch/pi_master.launch     |  40 +++++
 web_interface/html/agent_tab_launch.html      |  14 +-
 .../html/bashscripts/killRosAgent.sh          |  22 +--
 .../html/bashscripts/killRosMaster.sh         |  27 +++
 .../html/bashscripts/launchRosAgent.sh        |  33 +++-
 .../html/bashscripts/launchRosMaster.sh       |  38 +++++
 .../rosCrazyRadioCommand_forAgent.sh          |  35 ++--
 .../bashscripts/rosSetNewSetpoint_forAgent.sh |  47 +++++-
 web_interface/html/callBashScript.php         |  17 +-
 .../html/callBashScript_roslaunch.php         |  60 +++++++
 .../html/callBashScript_setNewSetpoint.php    |  25 ++-
 web_interface/html/js/callbashscript.js       |  14 +-
 web_interface/html/js/roslaunch.js            | 110 ++++++++++++
 web_interface/html/js/temp.js                 | 156 ------------------
 web_interface/html/page_header.html           |   1 +
 web_interface/install_dfall_server_WIP.sh     |  11 ++
 17 files changed, 574 insertions(+), 220 deletions(-)
 create mode 100755 dfall_ws/src/dfall_pkg/launch/pi_agent.launch
 create mode 100755 dfall_ws/src/dfall_pkg/launch/pi_master.launch
 create mode 100755 web_interface/html/bashscripts/killRosMaster.sh
 create mode 100755 web_interface/html/bashscripts/launchRosMaster.sh
 create mode 100644 web_interface/html/callBashScript_roslaunch.php
 create mode 100644 web_interface/html/js/roslaunch.js
 delete mode 100644 web_interface/html/js/temp.js

diff --git a/dfall_ws/src/dfall_pkg/launch/pi_agent.launch b/dfall_ws/src/dfall_pkg/launch/pi_agent.launch
new file mode 100755
index 00000000..a4e6f97e
--- /dev/null
+++ b/dfall_ws/src/dfall_pkg/launch/pi_agent.launch
@@ -0,0 +1,144 @@
+<launch>
+
+	<!-- INPUT ARGUMENT OF THE AGENT's ID -->
+	<arg name="agentID" default="$(optenv DFALL_DEFAULT_AGENT_ID)" />
+
+	<!-- INPUT ARGUMENT OF THE COORDINATOR's ID -->
+	<arg name="coordID" default="$(optenv DFALL_DEFAULT_COORD_ID)" />
+
+	<!-- INPUT ARGUMENT FOR EMULATING THE CRAZY RADIO -->
+	<arg name="emulateRadio" default="false" />
+
+	<!-- Example of how to use the value in agentID -->
+    <!-- <param name="param" value="$(arg agentID)"/> -->
+
+    <!-- Example of how to specify the agentID from command line -->
+    <!-- roslaunch dfall_pkg agentID:=1 -->
+
+    <!-- Example of how to specify the withGUI from command line -->
+    <!-- roslaunch dfall_pkg withGUI:=false -->
+
+    <group ns="$(eval 'agent' + str(agentID).zfill(3))">
+
+		<!-- CRAZY RADIO -->
+		<group unless="$(arg emulateRadio)">
+			<node
+				pkg    = "dfall_pkg"
+				name   = "CrazyRadio"
+				output = "screen"
+				type   = "CrazyRadio.py"
+				>
+				<rosparam
+					command = "load"
+					file    = "$(find dfall_pkg)/param/BatteryMonitor.yaml"
+					ns      = "CrazyRadioCopyOfBatteryMonitor"
+				/>
+				<rosparam
+					command = "load"
+					file    = "$(find dfall_pkg)/param/CrazyRadioConfig.yaml"
+					ns      = "CrazyRadioConfig"
+				/>
+			</node>
+		</group>
+		<group if="$(arg emulateRadio)">
+			<node
+				pkg    = "dfall_pkg"
+				name   = "CrazyRadio"
+				output = "screen"
+				type   = "CrazyRadioEmulator"
+				>
+				<rosparam
+					command = "load"
+					file    = "$(find dfall_pkg)/param/CrazyRadioConfig.yaml"
+					ns      = "CrazyRadioConfig"
+				/>
+			</node>
+		</group>
+
+
+		<!-- FLYING AGENT CLIENT -->
+		<node
+			pkg    = "dfall_pkg"
+			name   = "FlyingAgentClient"
+			output = "screen"
+			type   = "FlyingAgentClient"
+			>
+			<param name="agentID" value="$(arg agentID)" />
+			<param name="coordID" value="$(arg coordID)" />
+		</node>
+
+		<!-- BATTERY MONITOR -->
+		<node
+			pkg    = "dfall_pkg"
+			name   = "BatteryMonitor"
+			output = "screen"
+			type   = "BatteryMonitor"
+			>
+		</node>
+
+		<!-- DEFAULT CONTROLLER -->
+		<node
+			pkg    = "dfall_pkg"
+			name   = "DefaultControllerService"
+			output = "screen"
+			type   = "DefaultControllerService"
+			>
+		</node>
+
+		<!-- STUDENT CONTROLLER -->
+		<node
+			pkg    = "dfall_pkg"
+			name   = "StudentControllerService"
+			output = "screen"
+			type   = "StudentControllerService"
+			>
+		</node>
+
+		<!-- TEST MOTORS CONTROLLER -->
+		<node
+			pkg    = "dfall_pkg"
+			name   = "TestMotorsControllerService"
+			output = "screen"
+			type   = "TestMotorsControllerService"
+			>
+		</node>
+
+		<!-- PARAMETER SERVICE -->
+		<node
+			pkg    = "dfall_pkg"
+			name   = "ParameterService"
+			output = "screen"
+			type   = "ParameterService"
+			>
+			<param name="type"     type="str"  value="agent" />
+			<param name="agentID"  value="$(arg agentID)" />
+			<rosparam
+				command = "load"
+				file    = "$(find dfall_pkg)/param/YamlFileNames.yaml"
+				ns      = "YamlFileNames"
+			/>
+			<rosparam
+				command = "load"
+				file    = "$(find dfall_pkg)/param/FlyingAgentClientConfig.yaml"
+				ns      = "FlyingAgentClientConfig"
+			/>
+			<rosparam
+				command = "load"
+				file    = "$(find dfall_pkg)/param/BatteryMonitor.yaml"
+				ns      = "BatteryMonitor"
+			/>
+			<rosparam
+				command = "load"
+				file    = "$(find dfall_pkg)/param/DefaultController.yaml"
+				ns      = "DefaultController"
+			/>
+			<rosparam
+				command = "load"
+				file    = "$(find dfall_pkg)/param/StudentController.yaml"
+				ns      = "StudentController"
+			/>
+		</node>
+
+	</group>
+
+</launch>
diff --git a/dfall_ws/src/dfall_pkg/launch/pi_master.launch b/dfall_ws/src/dfall_pkg/launch/pi_master.launch
new file mode 100755
index 00000000..3165b14b
--- /dev/null
+++ b/dfall_ws/src/dfall_pkg/launch/pi_master.launch
@@ -0,0 +1,40 @@
+<launch>
+
+	<!-- INPUT ARGUMENT FOR EMULATING THE MOTION CAPTURE -->
+	<arg name="emulateMocap" default="false" />
+
+	<!-- Example of how to specify the emulateMocap from command line -->
+    <!-- roslaunch dfall_pkg emulateMocap:=true -->
+
+	<!-- CENTRAL MANAGER -->
+	<node
+		pkg="dfall_pkg"
+		name="CentralManagerService"
+		output="screen"
+		type="CentralManagerService"
+		>
+	</node>
+
+	<!-- VICON DATA PUBLISHER -->
+	<group unless="$(arg emulateMocap)">
+		<node
+			pkg="dfall_pkg"
+			name="ViconDataPublisher"
+			output="screen"
+			type="ViconDataPublisher"
+			>
+			<rosparam command="load" file="$(find dfall_pkg)/param/ViconConfig.yaml" />
+		</node>
+	</group>
+	<group if="$(arg emulateMocap)">
+		<node
+			pkg="dfall_pkg"
+			name="ViconDataPublisher"
+			output="screen"
+			type="MocapEmulator"
+			>
+			<rosparam command="load" file="$(find dfall_pkg)/param/MocapEmulatorConfig.yaml" />
+		</node>
+	</group>
+
+</launch>
diff --git a/web_interface/html/agent_tab_launch.html b/web_interface/html/agent_tab_launch.html
index c8850726..06c04845 100644
--- a/web_interface/html/agent_tab_launch.html
+++ b/web_interface/html/agent_tab_launch.html
@@ -11,7 +11,7 @@
 						<button
 							class="button-push navy fullwidth"
 							id="buttonRosMasterStatus"
-							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('checkForRosMaster', 'rosMasterStatus', null, null)"
+							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('checkForRosMaster', 'rosMasterStatus', ['rosLaunchMasterStatus','killMasterStatus'], null)"
 							>
 							Check for ROS Master
 							<div class="div-for-button-highlight-on-touchscreen navy"></div>
@@ -27,7 +27,7 @@
 						<button
 							class="button-push navy fullwidth"
 							id="buttonLaunchRosMaster"
-							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('launchRosMaster', 'rosLaunchMasterStatus', 'killMasterStatus', ['buttonRosMasterStatus','buttonRosAgentStatus','buttonRosNodeList'])"
+							onclick="roslaunch_outputLabelID_clearOtherLabels_clickOtherButtons('master', 'rosLaunchMasterStatus', ['rosMasterStatus','killMasterStatus'], ['buttonRosAgentStatus','buttonRosNodeList'])"
 							>
 							Launch ROS Master
 							<div class="div-for-button-highlight-on-touchscreen navy"></div>
@@ -59,7 +59,7 @@
 						<button
 							class="button-push red fullwidth"
 							id="buttonKillRosMaster"
-							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('killRosMaster', 'killMasterStatus', 'rosLaunchMasterStatus', ['buttonRosMasterStatus','buttonRosAgentStatus','buttonRosNodeList'])"
+							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('killRosMaster', 'killMasterStatus', ['rosMasterStatus','rosLaunchMasterStatus'], ['buttonRosAgentStatus','buttonRosNodeList'])"
 							>
 							Kill ROS Master
 							<div class="div-for-button-highlight-on-touchscreen red"></div>
@@ -83,7 +83,7 @@
 						<button
 							class="button-push navy fullwidth"
 							id="buttonRosAgentStatus"
-							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('checkForRosAgent', 'rosAgentStatus', null, ['buttonRosMasterStatus','buttonRosNodeList'])"
+							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('checkForRosAgent', 'rosAgentStatus', ['rosLaunchAgentStatus','killAgentStatus'], 'buttonRosNodeList')"
 							>
 							Check for ROS Agent
 							<div class="div-for-button-highlight-on-touchscreen navy"></div>
@@ -99,7 +99,7 @@
 						<button
 							class="button-push navy fullwidth"
 							id="buttonLaunchRosAgent"
-							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('launchRosAgent', 'rosLaunchAgentStatus', 'killAgentStatus', ['buttonRosMasterStatus','buttonRosAgentStatus','buttonRosNodeList'])"
+							onclick="roslaunch_outputLabelID_clearOtherLabels_clickOtherButtons('agent', 'rosLaunchAgentStatus', ['rosAgentStatus','killAgentStatus'], 'buttonRosNodeList')"
 							>
 							Launch ROS Agent
 							<div class="div-for-button-highlight-on-touchscreen navy"></div>
@@ -131,7 +131,7 @@
 						<button
 							class="button-push red fullwidth"
 							id="buttonKillRosAgent"
-							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('killRosAgent', 'killAgentStatus', 'rosLaunchAgentStatus', ['buttonRosMasterStatus','buttonRosAgentStatus','buttonRosNodeList'])"
+							onclick="callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons('killRosAgent', 'killAgentStatus', ['rosAgentStatus','rosLaunchAgentStatus'], 'buttonRosNodeList')"
 							>
 							Kill ROS Agent
 							<div class="div-for-button-highlight-on-touchscreen red"></div>
@@ -168,7 +168,7 @@
 
 			<table class="ros-table">
 				<tr>
-					<td class="ros-info-cell">
+					<td class="ros-info-cell" style="height:100px;vertical-align:text-top;">
 						<div id="rosnodeList"></div>
 					</td>
 				</tr>
diff --git a/web_interface/html/bashscripts/killRosAgent.sh b/web_interface/html/bashscripts/killRosAgent.sh
index 27fa7de6..1b687066 100755
--- a/web_interface/html/bashscripts/killRosAgent.sh
+++ b/web_interface/html/bashscripts/killRosAgent.sh
@@ -10,16 +10,16 @@ source /home/www-share/dfall/dfall-system/dfall_ws/src/dfall_pkg/launch/Config.s
 # > Note: the -q options converts the 
 #   grep output to a true/false
 if rosnode list | grep -q /rosout; then
-    # Check that if the agent is already
-    # launched
-    if rosnode list | grep -q "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)"; then
-        # Kill all nodes starting with
-        # /dfall/agentXXX/
-        rosnode list | grep "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)" | xargs rosnode kill > /dev/null
-        echo "Agent $DFALL_DEFAULT_AGENT_ID killed"
-    else
-        echo "Agent $DFALL_DEFAULT_AGENT_ID not found"
-    fi
+	# Check that if the agent is already
+	# launched
+	if rosnode list | grep -q "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)"; then
+		# Kill all nodes starting with
+		# /dfall/agentXXX/
+		rosnode list | grep "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)" | xargs rosnode kill > /dev/null
+		echo "Agent $DFALL_DEFAULT_AGENT_ID killed"
+	else
+		echo "Agent $DFALL_DEFAULT_AGENT_ID not found"
+	fi
 else
-    echo "ROS Master not found"
+	echo "ROS Master not found"
 fi
diff --git a/web_interface/html/bashscripts/killRosMaster.sh b/web_interface/html/bashscripts/killRosMaster.sh
new file mode 100755
index 00000000..8945b1ca
--- /dev/null
+++ b/web_interface/html/bashscripts/killRosMaster.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Make the ROS commands available
+# NOTE: these paths should NOT use ~
+source /opt/ros/melodic/setup.bash
+#source /home/www-share/dfall/dfall-system/dfall_ws/devel/setup.bash
+source /home/www-share/dfall/dfall-system/dfall_ws/src/dfall_pkg/launch/Config.sh
+#
+# Check that the ROS Master exists
+# > Note: the -q options converts the 
+#   grep output to a true/false
+if rosnode list | grep -q /rosout; then
+	# Kill all nodes
+	rosnode kill --all > /dev/null 2>&1 &
+	# The /rosout will respawn, so all kill any
+	# "roslaunch" process owned by the
+	# "www-data" user
+	pkill -u www-data roslaunch
+	# For debugging it is usefull to running the
+	# following commands on the server machine
+	# >> ps -aux
+	# >> pgrep -u www-data
+	# Inform the user
+	echo "Killed all ros nodes"
+else
+	echo "ROS Master not found"
+fi
diff --git a/web_interface/html/bashscripts/launchRosAgent.sh b/web_interface/html/bashscripts/launchRosAgent.sh
index b8fec36a..a2ba643f 100755
--- a/web_interface/html/bashscripts/launchRosAgent.sh
+++ b/web_interface/html/bashscripts/launchRosAgent.sh
@@ -1,5 +1,20 @@
 #!/bin/bash
 #
+# Check that exactly one command is supplied
+if [ "$#" -ne 1 ]; then
+	echo "exactly one argument must be supplied"
+	exit 1
+fi
+#
+# Check that the first command supplied is valid
+if [ "$1" != "true" ] && [ "$1" != "false" ]; then
+	echo "emulate crazyradio = $1, is not a valid boolean."
+	exit 1
+fi
+#
+# Put the commands into variables for make things more readable
+emulateradio=$1
+#
 # Make the ROS commands available
 # NOTE: these paths should NOT use ~
 source /opt/ros/melodic/setup.bash
@@ -16,14 +31,14 @@ export ROS_LOG_DIR=/var/www/html/.ros/log
 # > Note: the -q options converts the
 #   grep output to a true/false
 if rosnode list | grep -q /rosout; then
-    # Check that if the agent is already
-    # launched
-    if rosnode list | grep -q "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)"; then
-        echo "Agent $DFALL_DEFAULT_AGENT_ID is already running"
-    else
-        nohup roslaunch dfall_pkg agent.launch withGUI:=false emulateRadio:=true > /dev/null 2>&1 &
-        echo "Agent $DFALL_DEFAULT_AGENT_ID successfully launched"
-    fi
+	# Check that if the agent is already
+	# launched
+	if rosnode list | grep -q "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)"; then
+		echo "Agent $DFALL_DEFAULT_AGENT_ID is already running"
+	else
+		nohup roslaunch dfall_pkg pi_agent.launch emulateRadio:=$emulateradio > /dev/null 2>&1 &
+		echo "Agent $DFALL_DEFAULT_AGENT_ID successfully launched"
+	fi
 else
-    echo "ROS Master not found"
+	echo "ROS Master not found"
 fi
diff --git a/web_interface/html/bashscripts/launchRosMaster.sh b/web_interface/html/bashscripts/launchRosMaster.sh
new file mode 100755
index 00000000..a6b95473
--- /dev/null
+++ b/web_interface/html/bashscripts/launchRosMaster.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Check that exactly one command is supplied
+if [ "$#" -ne 1 ]; then
+	echo "exactly one argument must be supplied"
+	exit 1
+fi
+#
+# Check that the first command supplied is valid
+if [ "$1" != "true" ] && [ "$1" != "false" ]; then
+	echo "emulate mocap = $1, is not a valid boolean."
+	exit 1
+fi
+#
+# Put the commands into variables for make things more readable
+emultemocap=$1
+#
+# Make the ROS commands available
+# NOTE: these paths should NOT use ~
+source /opt/ros/melodic/setup.bash
+source /home/www-share/dfall/dfall-system/dfall_ws/devel/setup.bash
+source /home/www-share/dfall/dfall-system/dfall_ws/src/dfall_pkg/launch/Config.sh
+#
+# Set the ROS log location
+export ROS_LOG_DIR=/var/www/html/.ros/log
+#
+# Mount the dfall workspace folder
+#sudo mount --bind /home/pbeuchat/gitrep/dfall/dfall-system/dfall_ws /var/www/html/dfall_ws
+#
+# Check that the ROS Master exists
+# > Note: the -q options converts the
+#   grep output to a true/false
+if rosnode list | grep -q /rosout; then
+	echo "ROS Master is already running"
+else
+	nohup roslaunch dfall_pkg pi_master.launch emulateMocap:=$emultemocap > /dev/null 2>&1 &
+	echo "ROS Master successfully launched"
+fi
\ No newline at end of file
diff --git a/web_interface/html/bashscripts/rosCrazyRadioCommand_forAgent.sh b/web_interface/html/bashscripts/rosCrazyRadioCommand_forAgent.sh
index ed6c359c..1d826fd3 100755
--- a/web_interface/html/bashscripts/rosCrazyRadioCommand_forAgent.sh
+++ b/web_interface/html/bashscripts/rosCrazyRadioCommand_forAgent.sh
@@ -2,13 +2,13 @@
 #
 # Check that exactly one command is supplied
 if [ "$#" -ne 1 ]; then
-	echo "failed"
+	echo "exactly one argument must be supplied"
 	exit 1
 fi
 #
 # Check that the command supplied is valid
 if [ "$1" != "0" ] && [ "$1" != "1" ]; then
-	echo "failed"
+	echo "command = $1, is not a valid option"
 	exit 1
 fi
 #
@@ -25,18 +25,25 @@ source /home/www-share/dfall/dfall-system/dfall_ws/src/dfall_pkg/launch/Config.s
 # > Note: the -q options converts the
 #   grep output to a true/false
 if rosnode list | grep -q /rosout; then
-    # Convert the agent ID to a zero padded string
-    agentnamespace=$(printf "agent%03d" $DFALL_DEFAULT_AGENT_ID)    
-    # Send the message
-    if [ "$command" == "0" ]; then
-		temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/FlyingAgentClient/crazyRadioCommand dfall_pkg/IntWithHeader '{data: 0, shouldCheckForAgentID: False}')"
+	# Convert the agent ID to a zero padded string
+	agentnamespace=$(printf "agent%03d" $DFALL_DEFAULT_AGENT_ID)
+	# Send the message
+	if [ "$command" == "0" ]; then
+		# Publish the request
+		temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/FlyingAgentClient/CrazyRadioCommand dfall_pkg/IntWithHeader "{data: 0, shouldCheckForAgentID: False}")"
+		# Return that the message was sent
+		echo "sent"
+	#
+	elif [ "$command" == "1" ]; then
+		# Publish the request
+		temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/FlyingAgentClient/CrazyRadioCommand dfall_pkg/IntWithHeader "{data: 1, shouldCheckForAgentID: False}")"
+		# Return that the message was sent
+		echo "sent"
+	else
+		# Return that the command is not recognised
+		echo "command = $command is not a valid option"
 	fi
-	if [ "$command" == "1" ]; then
-		temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/FlyingAgentClient/crazyRadioCommand dfall_pkg/IntWithHeader '{data: 1, shouldCheckForAgentID: False}')"
-	fi
-    #
-    # Return that the message was sent
-    echo "sent"
+
 else
-    echo "ROS Master not found"
+	echo "ROS Master not found"
 fi
diff --git a/web_interface/html/bashscripts/rosSetNewSetpoint_forAgent.sh b/web_interface/html/bashscripts/rosSetNewSetpoint_forAgent.sh
index e81dffdf..26fd8756 100755
--- a/web_interface/html/bashscripts/rosSetNewSetpoint_forAgent.sh
+++ b/web_interface/html/bashscripts/rosSetNewSetpoint_forAgent.sh
@@ -2,13 +2,13 @@
 #
 # Check that exactly five commands are supplied
 if [ "$#" -ne 5 ]; then
-	echo "failed"
+	echo "exaclty five arguments must be supplied"
 	exit 1
 fi
 #
 # Check that the first command supplied is valid
 if [ "$1" != "default" ] && [ "$1" != "student" ]; then
-	echo "failed"
+	echo "command = $1, is not a valid option."
 	exit 1
 fi
 #
@@ -19,6 +19,28 @@ ynew=$3
 znew=$4
 yawnew=$5
 #
+# Check that the new setpoint values are purely numerical
+# > For x:
+if ! [[ $xnew =~ ^[+-]?[0-9]+\.?[0-9]*$ ]];then
+	echo "x = $xnew, is not a float."
+	exit 1
+fi
+# > For y:
+if ! [[ $ynew =~ ^[+-]?[0-9]+\.?[0-9]*$ ]];then
+	echo "y = $ynew, is not a float."
+	exit 1
+fi
+# > For z:
+if ! [[ $znew =~ ^[+-]?[0-9]+\.?[0-9]*$ ]];then
+	echo "z = $znew, is not a float."
+	exit 1
+fi
+# > For yaw:
+if ! [[ $yawnew =~ ^[+-]?[0-9]+\.?[0-9]*$ ]];then
+	echo "yaw = $yawnew, is not a float."
+	exit 1
+fi
+#
 # Make the ROS commands available
 # NOTE: these paths should NOT use ~
 source /opt/ros/melodic/setup.bash
@@ -33,14 +55,21 @@ if rosnode list | grep -q /rosout; then
 	agentnamespace=$(printf "agent%03d" $DFALL_DEFAULT_AGENT_ID)    
 	# Send the message
 	if [ "$command" == "default" ]; then
-		"$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/DefaultControllerService/RequestSetpointChange dfall_pkg/SetpointWithHeader "{x: $xnew, y: $ynew, z: $znew, yaw: $yawnew, shouldCheckForAgentID: False}")"
-	fi
-	if [ "$command" == "student" ]; then
-		"$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/StudentControllerService/RequestSetpointChange dfall_pkg/SetpointWithHeader "{x: $xnew, y: $ynew, z: $znew, yaw: $yawnew, shouldCheckForAgentID: False}")"
-	fi
+		# Publish the request
+		temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/DefaultControllerService/RequestSetpointChange dfall_pkg/SetpointWithHeader "{x: $xnew, y: $ynew, z: $znew, yaw: $yawnew, shouldCheckForAgentID: False}")"
+		# Return that the message was sent
+		echo "sent"
+	#
+	elif [ "$command" == "student" ]; then
+		# Publish the request
+		temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/StudentControllerService/RequestSetpointChange dfall_pkg/SetpointWithHeader "{x: $xnew, y: $ynew, z: $znew, yaw: $yawnew, shouldCheckForAgentID: False}")"
+		# Return that the message was sent
+		echo "sent"
 	#
-	# Return that the message was sent
-	echo "sent"
+	else
+		# Return that the command is not recognised
+		echo "controller = $command is not a valid option"
+	fi
 
 else
 	echo "ROS Master not found"
diff --git a/web_interface/html/callBashScript.php b/web_interface/html/callBashScript.php
index a89f9031..0e2b4553 100644
--- a/web_interface/html/callBashScript.php
+++ b/web_interface/html/callBashScript.php
@@ -30,19 +30,25 @@
 		$temp = shell_exec("./bashscripts/catkin_make.sh");
 		$output = "<pre>$temp</pre>";
 	}
+	//
 	// For the LAUNCH tab
 	elseif ($scriptname == "checkForRosMaster") {
 		$output = shell_exec("./bashscripts/checkForRosMaster.sh");
 	}
-	elseif ($scriptname == "checkForRosAgent") {
-		$output = shell_exec("./bashscripts/checkForRosAgent.sh");
+	//
+	// "launch Ros Master" is handled separately
+	// because it requires an input argument
+	//
+	elseif ($scriptname == "killRosMaster") {
+		$output = shell_exec("./bashscripts/killRosMaster.sh");
 	}
 	elseif ($scriptname == "checkForRosAgent") {
 		$output = shell_exec("./bashscripts/checkForRosAgent.sh");
 	}
-	elseif ($scriptname == "launchRosAgent") {
-		$output = shell_exec("./bashscripts/launchRosAgent.sh");
-	}
+	//
+	// "launch Ros Agent" is handled separately
+	// because it requires an input argument
+	//
 	elseif ($scriptname == "killRosAgent") {
 		$output = shell_exec("./bashscripts/killRosAgent.sh");
 	}
@@ -50,6 +56,7 @@
 		$temp = shell_exec("./bashscripts/rosnodeList.sh");
 		$output = "<pre>$temp</pre>";
 	}
+	//
 	// For the CONTROL tab:
 	// { RADIO CONNECTION, TAKE-OFF, LAND, MOTORS-OFF, LOAD CONTROLLER }
 	elseif ($scriptname == "rosConnect") {
diff --git a/web_interface/html/callBashScript_roslaunch.php b/web_interface/html/callBashScript_roslaunch.php
new file mode 100644
index 00000000..bca63740
--- /dev/null
+++ b/web_interface/html/callBashScript_roslaunch.php
@@ -0,0 +1,60 @@
+<?php
+	// GET THE BASH SCRIPT NAME
+	$scriptname = $_GET['scriptname'];
+
+	// ONLY EXECUTE "SCRIPT NAMES" WITH AN EXACT MATCH
+	//
+	// > For the MASTER
+	if ($scriptname == "master")
+	{
+		// GET THE VALUES OF THE EMULATE MOCAP FLAG
+		// > This will be a string
+		$emulate_mocap = strtolower( $_GET['emulatemocap'] );
+		// Check that the new setpoint values are numerical
+		if (in_array($emulate_mocap, array("true", "1", "yes"), true))
+		{
+			$emulate_mocap = "true";
+		}
+		elseif (in_array($emulate_mocap, array("false", "0", "no"), true))
+		{
+			$emulate_mocap = "false";
+		}
+		else
+		{
+			echo "emulate mocap flag = $emulate_mocap, is not a boolean value.";
+			exit();
+		}
+		// Call the bash script for launching the master
+		$output = shell_exec("./bashscripts/launchRosMaster.sh $emulate_mocap");
+	}
+	//
+	// > For the AGENT
+	elseif ($scriptname == "agent")
+	{
+		// GET THE VALUES OF THE EMULATE MOCAP FLAG
+		// > This will be a string
+		$emulate_crazyradio = strtolower( $_GET['emulatecrazyradio'] );
+		// Check that the new setpoint values are numerical
+		if (in_array($emulate_crazyradio, array("true", "1", "yes"), true))
+		{
+			$emulate_crazyradio = "true";
+		}
+		elseif (in_array($emulate_crazyradio, array("false", "0", "no"), true))
+		{
+			$emulate_crazyradio = "false";
+		}
+		else
+		{
+			echo "emulate crazyradio flag = $emulate_crazyradio, is not a boolean value.";
+			exit();
+		}
+		// Call the bash script for launching the agent
+		$output = shell_exec("./bashscripts/launchRosAgent.sh $emulate_crazyradio");
+	}
+	else
+	{
+		$output = "launch name = $scriptname is not a valid option";
+	}
+
+	echo "$output";
+?>
diff --git a/web_interface/html/callBashScript_setNewSetpoint.php b/web_interface/html/callBashScript_setNewSetpoint.php
index 4d52c0c1..2e44ae0c 100644
--- a/web_interface/html/callBashScript_setNewSetpoint.php
+++ b/web_interface/html/callBashScript_setNewSetpoint.php
@@ -3,15 +3,36 @@
 	$scriptname = $_GET['scriptname'];
 
 	// GET THE (x,y,z,yaw) VALUES OF THE NEW SETPOINT
+	// > These will be strings
 	$x_new = $_GET['x'];
 	$y_new = $_GET['y'];
 	$z_new = $_GET['z'];
 	$yaw_new = $_GET['yaw'];
 
 
+	// Check that the new setpoint values are numerical
+	if ( ! (is_numeric($x_new)) )
+	{
+		echo "x = $x_new, is not a numeric value.";
+		exit();
+	}
+	if ( ! (is_numeric($y_new)) )
+	{
+		echo "y = $y_new, is not a numeric value.";
+		exit();
+	}
+	if ( ! (is_numeric($z_new)) )
+	{
+		echo "z = $z_new, is not a numeric value.";
+		exit();
+	}
+	if ( ! (is_numeric($yaw_new)) )
+	{
+		echo "yaw = $yaw_new, is not a numeric value.";
+		exit();
+	}
 
-	// ONLY EXECUTE NAMES WITH AN EXACT MATHC
-	
+	// ONLY EXECUTE "SCRIPT NAMES" WITH AN EXACT MATCH
 	// For the CONTROL tab:
 	// GET SETPOINT
 	if ($scriptname == "rosSetSetpointDefault") {
diff --git a/web_interface/html/js/callbashscript.js b/web_interface/html/js/callbashscript.js
index d4a563fd..e494f3c1 100644
--- a/web_interface/html/js/callbashscript.js
+++ b/web_interface/html/js/callbashscript.js
@@ -1,4 +1,4 @@
-function callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons(bashscript, labelID, otherLabels, otherbuttons)
+function callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons(bashscript, labelID, otherLabels, otherButtons)
 {
 	// Create a variable for sending an AJAX request
 	var xmlhttp = new XMLHttpRequest();
@@ -11,15 +11,15 @@ function callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons(bashscr
 			var base_string = "";
 			document.getElementById(labelID).innerHTML = base_string + this.responseText;
 			// Click the "other buttons" as requested
-			if (otherbuttons)
+			if (otherButtons)
 			{
-				if ( typeof(otherbuttons) == "string" )
+				if ( typeof(otherButtons) == "string" )
 				{
-					document.getElementById(otherbuttons).click();
+					document.getElementById(otherButtons).click();
 				}
-				else if (otherbuttons.constructor === Array)
+				else if (otherButtons.constructor === Array)
 				{
-					for (otherButtonID of otherbuttons)
+					for (otherButtonID of otherButtons)
 					{
 						if (otherButtonID)
 						{
@@ -51,7 +51,7 @@ function callBashScript_outputLabelID_clearOtherLabels_clickOtherButtons(bashscr
 		}
 		else if (otherLabels.constructor === Array)
 		{
-			for (otherLabelID of otherbuttons)
+			for (otherLabelID of otherLabels)
 			{
 				if (otherLabelID)
 				{
diff --git a/web_interface/html/js/roslaunch.js b/web_interface/html/js/roslaunch.js
new file mode 100644
index 00000000..0f537573
--- /dev/null
+++ b/web_interface/html/js/roslaunch.js
@@ -0,0 +1,110 @@
+function roslaunch_outputLabelID_clearOtherLabels_clickOtherButtons(launchName, labelID, otherLabels, otherButtons)
+{
+	// Convert the "controllerName" to a heading string
+	var scriptname_for_php = "";
+	if (launchName == "master")
+	{
+		scriptname_for_php = "master";
+	}
+	else if (launchName == "agent")
+	{
+		scriptname_for_php = "agent";
+	}
+	else
+	{
+		return;
+	}
+
+	// Get the booleans for emulation
+	emulated_mocap      = document.getElementById("checkboxEmulateMocap").checked;
+	emulated_crazyradio = document.getElementById("checkboxEmulateCrazyRadio").checked;
+
+	// Set the label to be sending
+	if(labelID){document.getElementById(labelID).innerHTML = "sending...";}
+
+	// Create a variable for sending an AJAX request
+	var xmlhttp = new XMLHttpRequest();
+	// Add the function to be run when the response is recieved
+	xmlhttp.onreadystatechange = function()
+	{
+		if (this.readyState == 4 && this.status == 200)
+		{
+			// Construct and display the appropriate information
+			var base_string = "";
+			if(labelID){document.getElementById(labelID).innerHTML = base_string + this.responseText;}
+			// Click the "other buttons" as requested
+			if (otherButtons)
+			{
+				if ( typeof(otherButtons) == "string" )
+				{
+					document.getElementById(otherButtons).click();
+				}
+				else if (otherButtons.constructor === Array)
+				{
+					for (otherButtonID of otherButtons)
+					{
+						if (otherButtonID)
+						{
+							if ( typeof(otherButtonID) == "string" )
+							{
+								document.getElementById(otherButtonID).click();
+							}
+						}
+					}
+				}
+			}
+
+		}
+		else
+		{
+			// Construct and display the appropriate information
+			var base_string = "";
+			var display_message = getDisplayMessageForXMLHttpRequest(this);
+			if (this.readyState == 4)
+			{
+				if(labelID){document.getElementById(labelID).innerHTML = base_string + display_message;}
+			}
+			else
+			{
+				//if(labelID){document.getElementById(labelID).innerHTML = base_string + display_message;}
+			}
+		}
+	};
+	if (launchName == "master")
+	{
+		xmlhttp.open("GET", "callBashScript_roslaunch.php?scriptname=" + scriptname_for_php + "&emulatemocap=" + emulated_mocap, true);
+		xmlhttp.send();
+	}
+	else if (launchName == "agent")
+	{
+		xmlhttp.open("GET", "callBashScript_roslaunch.php?scriptname=" + scriptname_for_php + "&emulatecrazyradio=" + emulated_crazyradio, true);
+		xmlhttp.send();
+	}
+	else
+	{
+		if(labelID){document.getElementById(labelID).innerHTML = "ERROR: launch name = " + launchName + " is not a valid option";}
+	}
+	
+
+	// Clear the other labels as requested
+	if (otherLabels)
+	{
+		if ( typeof(otherLabels) == "string" )
+		{
+			document.getElementById(otherLabels).innerHTML = "";
+		}
+		else if (otherLabels.constructor === Array)
+		{
+			for (otherLabelID of otherLabels)
+			{
+				if (otherLabelID)
+				{
+					if ( typeof(otherLabelID) == "string" )
+					{
+						document.getElementById(otherLabelID).innerHTML = "";
+					}
+				}
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/web_interface/html/js/temp.js b/web_interface/html/js/temp.js
deleted file mode 100644
index 3f1a6f4d..00000000
--- a/web_interface/html/js/temp.js
+++ /dev/null
@@ -1,156 +0,0 @@
-function checkForRosMaster()
-{
-	// Create a variable for sending an AJAX request
-	var xmlhttp = new XMLHttpRequest();
-	// Add the function to be run when the response is recieved
-	xmlhttp.onreadystatechange = function()
-	{
-		if (this.readyState == 4 && this.status == 200)
-		{
-			//var base_string = "Connection success, ";
-			var base_string = "";
-			document.getElementById("rosMasterStatus").innerHTML = base_string + "ROS Master " + this.responseText;
-		}
-		else
-		{
-			var base_string = "Connection error";
-			var error_message = getErrorMessageForXMLHttpRequest(this);
-			document.getElementById("rosMasterStatus").innerHTML = base_string + ", " + error_message;
-			//
-			// Alternatively, just put "loading..."
-			//document.getElementById("rosMasterStatus").innerHTML = "loading...";
-		}
-	};
-	xmlhttp.open("GET", "checkForRosMaster.php", true);
-	xmlhttp.send();
-}
-
-
-
-function checkForRosAgent()
-{
-	// Create a variable for sending an AJAX request
-	var xmlhttp = new XMLHttpRequest();
-	// Add the function to be run when the response is recieved
-	xmlhttp.onreadystatechange = function()
-	{
-		if (this.readyState == 4 && this.status == 200)
-		{
-			//var base_string = "Connection success, ";
-			var base_string = "";
-			document.getElementById("rosAgentStatus").innerHTML = base_string + this.responseText;
-		}
-		else
-		{
-			//var base_string = "Connection error";
-			//var error_message = getErrorMessageForXMLHttpRequest(this);
-			//document.getElementById("rosAgentStatus").innerHTML = base_string + ", " + error_message;
-			//
-			// Alternatively, just put "loading..."
-			document.getElementById("rosAgentStatus").innerHTML = "loading...";
-		}
-	};
-	xmlhttp.open("GET", "checkForRosAgent.php", true);
-	xmlhttp.send();
-}
-
-
-
-
-function launchRosAgent()
-{
-	// Create a variable for sending an AJAX request
-	var xmlhttp = new XMLHttpRequest();
-	// Add the function to be run when the response is recieved
-	xmlhttp.onreadystatechange = function()
-	{
-		if (this.readyState == 4 && this.status == 200)
-		{
-			//var base_string = "Connection success, ";
-			var base_string = "";
-			document.getElementById("rosLaunchAgentStatus").innerHTML = base_string + this.responseText;
-			// Call the function to check for and
-			// update the "ROS Agent Status" field
-			checkForRosAgent();
-		}
-		else
-		{
-			//var base_string = "Connection error";
-			//var error_message = getErrorMessageForXMLHttpRequest(this);
-			//document.getElementById("rosLaunchAgentStatus").innerHTML = base_string + ", " + error_message;
-			//
-			// Alternatively, just put "loading..."
-			document.getElementById("rosLaunchAgentStatus").innerHTML = "loading...";
-		}
-	};
-	xmlhttp.open("GET", "launchRosAgent.php", true);
-	xmlhttp.send();
-}
-
-
-
-
-
-
-function killRosAgent()
-{
-	// Create a variable for sending an AJAX request
-	var xmlhttp = new XMLHttpRequest();
-	// Add the function to be run when the response is recieved
-	xmlhttp.onreadystatechange = function()
-	{
-		if (this.readyState == 4 && this.status == 200)
-		{
-			//var base_string = "Connection success, ";
-			var base_string = "";
-			document.getElementById("killAgentStatus").innerHTML = base_string + this.responseText;
-			// Call the function to check for and
-			// update the "ROS Agent Status" field
-			checkForRosAgent();
-			// Call the function to update the list 
-			// of ROS nodes
-			rosnodeList()
-		}
-		else
-		{
-			//var base_string = "Connection error";
-			//var error_message = getErrorMessageForXMLHttpRequest(this);
-			//document.getElementById("killAgentStatus").innerHTML = base_string + ", " + error_message;
-			//
-			// Alternatively, just put "loading..."
-			document.getElementById("killAgentStatus").innerHTML = "loading...";
-		}
-	};
-	xmlhttp.open("GET", "killRosAgent.php", true);
-	xmlhttp.send();
-}
-
-
-
-
-function rosnodeList()
-{
-	// Create a variable for sending an AJAX request
-	var xmlhttp = new XMLHttpRequest();
-	// Add the function to be run when the response is recieved
-	xmlhttp.onreadystatechange = function()
-	{
-		if (this.readyState == 4 && this.status == 200)
-		{
-			//var base_string = "Connection success, ";
-			var base_string = "";
-			document.getElementById("rosnodeList").innerHTML = base_string + this.responseText;
-		}
-		else
-		{
-			//var base_string = "Connection error";
-			//var error_message = getErrorMessageForXMLHttpRequest(this);
-			//document.getElementById("rosnodeList").innerHTML = base_string + ", " + error_message;
-			//
-			// Alternatively, just put "loading..."
-			document.getElementById("rosnodeList").innerHTML = "loading...";
-		}
-	};
-	xmlhttp.open("GET", "rosnodeList.php", true);
-	xmlhttp.send();
-}
diff --git a/web_interface/html/page_header.html b/web_interface/html/page_header.html
index 9caeb038..a2377b8c 100644
--- a/web_interface/html/page_header.html
+++ b/web_interface/html/page_header.html
@@ -33,6 +33,7 @@
 	<script src="js/general.js?ver=0.1"></script>
 	<script src="js/callbashscript.js?ver=0.1"></script>
 	<script src="js/callgit.js?ver=0.1"></script>
+	<script src="js/roslaunch.js?ver=0.1"></script>
 	<script src="js/sendRosMessage.js?ver=0.1"></script>
 	<script src="js/setSetpointViaRosMessage.js?ver=0.1"></script>
 	<script src="js/getSetpointViaRosServiceCall.js?ver=0.1"></script>
diff --git a/web_interface/install_dfall_server_WIP.sh b/web_interface/install_dfall_server_WIP.sh
index 09e47cfb..6df43f81 100644
--- a/web_interface/install_dfall_server_WIP.sh
+++ b/web_interface/install_dfall_server_WIP.sh
@@ -14,6 +14,17 @@ sudo -u www-data git clone https://...
 # "git pull" commands
 www-data ALL=(www-data) /usr/bin/git pull
 
+# Add the "www-data" user to the "plugdev" group
+# NOTE: this is the group nominated in the udev
+#       rules for the CrazyRadio (see the "install"
+#       folder). This allows a CrazyRadio node
+#       that is launched by the web interface to
+#       access the USB dongle.
+sudo usermod -a -G plugdev www-data
+
+# To confirm the group allocation, view the file:
+less /etc/group
+
 
 
 
-- 
GitLab