From e8972449c15f8b56554ce630295fa035b8077c10 Mon Sep 17 00:00:00 2001 From: Paul Beuchat <beuchatp@control.ee.ethz.ch> Date: Tue, 5 May 2020 15:51:51 +0200 Subject: [PATCH] Got most everything working for the web interface --- .../src/dfall_pkg/crazyradio/CrazyRadio.py | 20 + .../nodes/AgentStatusForWebInterface.h | 26 +- .../include/nodes/CrazyRadioEmulator.h | 5 + .../include/nodes/StudentControllerService.h | 2 +- .../src/dfall_pkg/param/CrazyRadioConfig.yaml | 4 + .../src/nodes/AgentStatusForWebInterface.cpp | 313 +++++++++++- .../src/nodes/CrazyRadioEmulator.cpp | 23 + .../src/nodes/StudentControllerService.cpp | 32 +- web_interface/html/agent.php | 19 +- .../html/agent_control_tab_default.html | 123 ++++- .../html/agent_control_tab_student.html | 220 ++++++-- web_interface/html/agent_tab_code.html | 11 +- web_interface/html/agent_tab_control.php | 8 +- .../bashscripts/rosGetStatusJson_forAgent.sh | 82 +++ .../html/bashscripts/rosLoadYaml_forAgent.sh | 54 ++ web_interface/html/callBashScript.php | 8 + web_interface/html/checkForRosAgent.php | 4 - web_interface/html/checkForRosMaster.php | 11 - web_interface/html/css/buttons.css | 36 -- web_interface/html/css/debug_values_table.css | 91 ++++ web_interface/html/css/layout.css | 20 + .../css/measurement_setpoint_error_table.css | 151 ++++++ web_interface/html/css/status_bar.css | 71 +++ web_interface/html/css/switches.css | 75 +++ web_interface/html/css/tabs_control.css | 9 + web_interface/html/css/tabs_main.css | 24 +- web_interface/html/img/battery_20.png | Bin 0 -> 2099 bytes web_interface/html/img/battery_40.png | Bin 0 -> 2147 bytes web_interface/html/img/battery_60.png | Bin 0 -> 2232 bytes web_interface/html/img/battery_80.png | Bin 0 -> 2252 bytes web_interface/html/img/battery_empty.png | Bin 0 -> 3379 bytes web_interface/html/img/battery_full.png | Bin 0 -> 2152 bytes .../html/img/battery_unavailable.png | Bin 0 -> 2775 bytes web_interface/html/img/battery_unknown.png | Bin 0 -> 3138 bytes .../html/img/flying_state_disabling.png | Bin 0 -> 3303 bytes .../html/img/flying_state_enabling.png | Bin 0 -> 3512 bytes .../html/img/flying_state_flying.png | Bin 0 -> 3001 bytes web_interface/html/img/flying_state_off.png | Bin 0 -> 3106 bytes .../html/img/flying_state_unavailable.png | Bin 0 -> 4797 bytes .../html/img/flying_state_unknown.png | Bin 0 -> 3868 bytes web_interface/html/img/rf_connected.png | Bin 0 -> 6410 bytes web_interface/html/img/rf_connecting.png | Bin 0 -> 5679 bytes web_interface/html/img/rf_disconnected.png | Bin 0 -> 5321 bytes web_interface/html/js/sendRosMessage.js | 9 + web_interface/html/js/sse_agentStatus.js | 468 ++++++++++++++++++ web_interface/html/killRosAgent.php | 4 - web_interface/html/launchRosAgent.php | 4 - web_interface/html/page_header.html | 4 +- web_interface/html/rosnodeList.php | 4 - web_interface/html/sse_agentStatus.php | 30 ++ web_interface/html/temp.html | 69 --- 51 files changed, 1806 insertions(+), 228 deletions(-) create mode 100755 web_interface/html/bashscripts/rosGetStatusJson_forAgent.sh create mode 100755 web_interface/html/bashscripts/rosLoadYaml_forAgent.sh delete mode 100644 web_interface/html/checkForRosAgent.php delete mode 100644 web_interface/html/checkForRosMaster.php create mode 100644 web_interface/html/css/debug_values_table.css create mode 100644 web_interface/html/css/measurement_setpoint_error_table.css create mode 100644 web_interface/html/css/status_bar.css create mode 100644 web_interface/html/img/battery_20.png create mode 100644 web_interface/html/img/battery_40.png create mode 100644 web_interface/html/img/battery_60.png create mode 100644 web_interface/html/img/battery_80.png create mode 100644 web_interface/html/img/battery_empty.png create mode 100644 web_interface/html/img/battery_full.png create mode 100644 web_interface/html/img/battery_unavailable.png create mode 100644 web_interface/html/img/battery_unknown.png create mode 100644 web_interface/html/img/flying_state_disabling.png create mode 100644 web_interface/html/img/flying_state_enabling.png create mode 100644 web_interface/html/img/flying_state_flying.png create mode 100644 web_interface/html/img/flying_state_off.png create mode 100644 web_interface/html/img/flying_state_unavailable.png create mode 100644 web_interface/html/img/flying_state_unknown.png create mode 100644 web_interface/html/img/rf_connected.png create mode 100644 web_interface/html/img/rf_connecting.png create mode 100644 web_interface/html/img/rf_disconnected.png create mode 100644 web_interface/html/js/sse_agentStatus.js delete mode 100644 web_interface/html/killRosAgent.php delete mode 100644 web_interface/html/launchRosAgent.php delete mode 100644 web_interface/html/rosnodeList.php create mode 100644 web_interface/html/sse_agentStatus.php delete mode 100644 web_interface/html/temp.html diff --git a/dfall_ws/src/dfall_pkg/crazyradio/CrazyRadio.py b/dfall_ws/src/dfall_pkg/crazyradio/CrazyRadio.py index ca3dd25a..fb0e3cf5 100755 --- a/dfall_ws/src/dfall_pkg/crazyradio/CrazyRadio.py +++ b/dfall_ws/src/dfall_pkg/crazyradio/CrazyRadio.py @@ -252,6 +252,20 @@ class CrazyRadioClient: def _data_received_stateEstimate_callback(self, timestamp, data, logconf): + # Perform safety check if required + if (isEnabled_strictSafety): + # Estimate the height at the next measurement + height_at_next_measurement = data["stateEstimateZ.z"] / 1000.0 + (cfStateEstimate_polling_period / 1000.0) * (data["stateEstimateZ.vz"] / 1000.0) + # Turn-off if too high + if (height_at_next_measurement > maxHeight_for_strictSafety_meters): + # Publish a motors OFF command + msg = IntWithHeader() + msg.shouldCheckForAgentID = False + msg.data = CMD_CRAZYFLY_MOTORS_OFF + self.flyingAgentClient_command_publisher.publish(msg) + # Inform the user + rospy.logerr("[CRAZY RADIO] Height safety check failed, measured = %f, max allowed = %f" % (height_at_next_measurement, maxHeight_for_strictSafety_meters)) + # Initialise the variable for the flying vehicle state cfStateEstimate = FlyingVehicleState() @@ -747,6 +761,12 @@ if __name__ == '__main__': global cfStateEstimate_polling_period cfStateEstimate_polling_period = rospy.get_param(ros_namespace + "CrazyRadio/CrazyRadioConfig/cfStateEstimate_polling_period") + global isEnabled_strictSafety + isEnabled_strictSafety = rospy.get_param(ros_namespace + "CrazyRadio/CrazyRadioConfig/isEnabled_strictSafety") + + global maxHeight_for_strictSafety_meters + maxHeight_for_strictSafety_meters = rospy.get_param(ros_namespace + "CrazyRadio/CrazyRadioConfig/maxHeight_for_strictSafety_meters") + # Fetch the YAML paramter "agentID" and "coordID" global m_agentID m_agentID = rospy.get_param(ros_namespace + "/FlyingAgentClient/agentID") diff --git a/dfall_ws/src/dfall_pkg/include/nodes/AgentStatusForWebInterface.h b/dfall_ws/src/dfall_pkg/include/nodes/AgentStatusForWebInterface.h index 48ea1086..c7dcef7e 100644 --- a/dfall_ws/src/dfall_pkg/include/nodes/AgentStatusForWebInterface.h +++ b/dfall_ws/src/dfall_pkg/include/nodes/AgentStatusForWebInterface.h @@ -56,6 +56,8 @@ // Include the DFALL message types #include "dfall_pkg/SetpointWithHeader.h" +#include "dfall_pkg/FlyingVehicleState.h" +#include "dfall_pkg/DebugMsg.h" // Include the DFALL service types #include "dfall_pkg/IntStringService.h" @@ -95,6 +97,10 @@ using namespace dfall_pkg; // > as received via messages int m_crazyradio_status = CRAZY_RADIO_STATE_DISCONNECTED; +// The battery level +// > as received via messages +int m_battery_level = BATTERY_LEVEL_UNAVAILABLE; + // The flying state of the agent // > as received via messages int m_agent_operating_state = STATE_UNAVAILABLE; @@ -103,10 +109,6 @@ int m_agent_operating_state = STATE_UNAVAILABLE; // > as received via messages int m_instant_controller = DEFAULT_CONTROLLER; -// The battery level -// > as received via messages -int m_battery_level = BATTERY_LEVEL_UNAVAILABLE; - // The setpoint of the default controller // > as received via messages float m_setpoint_default[4] = {0.0,0.0,0.4,0.0}; @@ -115,6 +117,14 @@ float m_setpoint_default[4] = {0.0,0.0,0.4,0.0}; // > as received via messages float m_setpoint_student[4] = {0.0,0.0,0.4,0.0}; +// The debug values of the student controller +// > as received via messages +float m_debug_values_student[10] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; + +// The current state estimate of the Crazyflie +// > as received via messages +float m_cf_state_estimate_xyz_rpy[6] = {0.0,0.0,0.0,0.0,0.0,0.0}; + @@ -137,16 +147,20 @@ float m_setpoint_student[4] = {0.0,0.0,0.4,0.0}; // Callbacks for messages with the status of things: // > For the status of the crazyradio void crazyRadioStatusCallback(const std_msgs::Int32& msg); +// > For the battery level +void newBatteryLevelCallback(const std_msgs::Int32& msg); // > For the flying state of the agent void agentOperatingStateCallback(const std_msgs::Int32& msg); // > For the instant controller void instantControllerChangedCallback(const std_msgs::Int32& msg); -// > For the battery level -void newBatteryLevelCallback(const std_msgs::Int32& msg); // > For the Default Controller Setpoint void defaultControllerSetpointChangedCallback(const SetpointWithHeader& newSetpoint); // > For the Student Controller Setpoint void studentControllerSetpointChangedCallback(const SetpointWithHeader& newSetpoint); +// > For the Student Controller Debug Values +void studentControllerDebugValuesCallback(const DebugMsg& newDebugMsg); +// > For the State Estimate from the Crazyflie +void cfStateEstimateCallback(const FlyingVehicleState & newStateEstimate); // Service callback for providing status to the Web Interface bool statusForWebInterfaceCallback(IntStringService::Request &request, IntStringService::Response &response); \ No newline at end of file diff --git a/dfall_ws/src/dfall_pkg/include/nodes/CrazyRadioEmulator.h b/dfall_ws/src/dfall_pkg/include/nodes/CrazyRadioEmulator.h index 71481271..a4af6736 100644 --- a/dfall_ws/src/dfall_pkg/include/nodes/CrazyRadioEmulator.h +++ b/dfall_ws/src/dfall_pkg/include/nodes/CrazyRadioEmulator.h @@ -153,6 +153,11 @@ ros::Publisher cfSimulationStatePublisher; // Frequency of requesting the battery voltage, in [seconds] float yaml_battery_polling_period_in_seconds = 0.2f; +// Flag for whether to use height as a trigger for +// publishing a motors-OFF command +bool yaml_isEnabled_strictSafety = true; +float yaml_maxHeight_for_strictSafety_meters = 1.2; + // // Battery thresholds while in the "motors off" state, in [Volts] // float yaml_battery_voltage_threshold_lower_while_standby = 3.30f; // float yaml_battery_voltage_threshold_upper_while_standby = 4.20f; diff --git a/dfall_ws/src/dfall_pkg/include/nodes/StudentControllerService.h b/dfall_ws/src/dfall_pkg/include/nodes/StudentControllerService.h index 67647069..bb04171a 100644 --- a/dfall_ws/src/dfall_pkg/include/nodes/StudentControllerService.h +++ b/dfall_ws/src/dfall_pkg/include/nodes/StudentControllerService.h @@ -69,7 +69,7 @@ // Include the DFALL service types #include "dfall_pkg/LoadYamlFromFilename.h" #include "dfall_pkg/GetSetpointService.h" -#include "dfall_pkg/GetDebugValuesService.h" +//#include "dfall_pkg/GetDebugValuesService.h" // Include the shared definitions #include "nodes/Constants.h" diff --git a/dfall_ws/src/dfall_pkg/param/CrazyRadioConfig.yaml b/dfall_ws/src/dfall_pkg/param/CrazyRadioConfig.yaml index 88ddd0a7..6f2892f8 100755 --- a/dfall_ws/src/dfall_pkg/param/CrazyRadioConfig.yaml +++ b/dfall_ws/src/dfall_pkg/param/CrazyRadioConfig.yaml @@ -1,6 +1,10 @@ # Frequency of requesting the onboard state estimate, in [milliseconds] cfStateEstimate_polling_period: 20 +# Flag for whether to use height as a trigger for +# publishing a motors-OFF command +isEnabled_strictSafety: true +maxHeight_for_strictSafety_meters: 1.2 # ----------------------------------------------- # diff --git a/dfall_ws/src/dfall_pkg/src/nodes/AgentStatusForWebInterface.cpp b/dfall_ws/src/dfall_pkg/src/nodes/AgentStatusForWebInterface.cpp index d7718333..612fd244 100644 --- a/dfall_ws/src/dfall_pkg/src/nodes/AgentStatusForWebInterface.cpp +++ b/dfall_ws/src/dfall_pkg/src/nodes/AgentStatusForWebInterface.cpp @@ -70,6 +70,11 @@ void crazyRadioStatusCallback(const std_msgs::Int32& msg) m_crazyradio_status = msg.data; } +// > For the battery level +void newBatteryLevelCallback(const std_msgs::Int32& msg) +{ + m_battery_level = msg.data; +} // > For the flying state of the agent void agentOperatingStateCallback(const std_msgs::Int32& msg) @@ -83,12 +88,6 @@ void instantControllerChangedCallback(const std_msgs::Int32& msg) m_instant_controller = msg.data; } -// > For the battery level -void newBatteryLevelCallback(const std_msgs::Int32& msg) -{ - m_battery_level = msg.data; -} - // > For the Default Controller Setpoint void defaultControllerSetpointChangedCallback(const SetpointWithHeader& newSetpoint) { @@ -107,6 +106,34 @@ void studentControllerSetpointChangedCallback(const SetpointWithHeader& newSetpo m_setpoint_student[3] = newSetpoint.yaw; } +// > For the Student Controller Debug Values +void studentControllerDebugValuesCallback(const DebugMsg& newDebugMsg) +{ + m_debug_values_student[0] = newDebugMsg.value_1; + m_debug_values_student[1] = newDebugMsg.value_2; + m_debug_values_student[2] = newDebugMsg.value_3; + m_debug_values_student[3] = newDebugMsg.value_4; + m_debug_values_student[4] = newDebugMsg.value_5; + m_debug_values_student[5] = newDebugMsg.value_6; + m_debug_values_student[6] = newDebugMsg.value_7; + m_debug_values_student[7] = newDebugMsg.value_8; + m_debug_values_student[8] = newDebugMsg.value_9; + m_debug_values_student[9] = newDebugMsg.value_10; +} + +// > For the State Estimate from the Crazyflie +void cfStateEstimateCallback(const FlyingVehicleState & newStateEstimate) +{ + // > For (x,y,z) positions + m_cf_state_estimate_xyz_rpy[0] = newStateEstimate.x; + m_cf_state_estimate_xyz_rpy[1] = newStateEstimate.y; + m_cf_state_estimate_xyz_rpy[2] = newStateEstimate.z; + // > For (roll,pitch,yaw) orientation + m_cf_state_estimate_xyz_rpy[3] = newStateEstimate.roll; + m_cf_state_estimate_xyz_rpy[4] = newStateEstimate.pitch; + m_cf_state_estimate_xyz_rpy[5] = newStateEstimate.yaw; +} + @@ -114,8 +141,8 @@ void studentControllerSetpointChangedCallback(const SetpointWithHeader& newSetpo // SERVICE CALLBACK FOR PROVIDING STATUS TO THE WEB INTERFACE bool statusForWebInterfaceCallback(IntStringService::Request &request, IntStringService::Response &response) { - // Get the statuses as string - str::string crazyradio_status_string; + // Get the CrazyRadio status as a string + std::string crazyradio_status_string; switch (m_crazyradio_status) { case CRAZY_RADIO_STATE_CONNECTED: @@ -140,11 +167,248 @@ bool statusForWebInterfaceCallback(IntStringService::Request &request, IntString } } + + // Get the Battery level as a string + std::string battery_level_string; + switch (m_battery_level) + { + case BATTERY_LEVEL_000: + { + battery_level_string = "000"; + break; + } + case BATTERY_LEVEL_010: + { + battery_level_string = "010"; + break; + } + case BATTERY_LEVEL_020: + { + battery_level_string = "020"; + break; + } + case BATTERY_LEVEL_030: + { + battery_level_string = "030"; + break; + } + case BATTERY_LEVEL_040: + { + battery_level_string = "040"; + break; + } + case BATTERY_LEVEL_050: + { + battery_level_string = "050"; + break; + } + case BATTERY_LEVEL_060: + { + battery_level_string = "060"; + break; + } + case BATTERY_LEVEL_070: + { + battery_level_string = "070"; + break; + } + case BATTERY_LEVEL_080: + { + battery_level_string = "080"; + break; + } + case BATTERY_LEVEL_090: + { + battery_level_string = "090"; + break; + } + case BATTERY_LEVEL_100: + { + battery_level_string = "100"; + break; + } + case BATTERY_LEVEL_UNAVAILABLE: + { + battery_level_string = "unavailable"; + break; + } + default: + { + battery_level_string = "unavailable"; + break; + } + } + + // Get the Flying status as a string + std::string flying_state_string; + switch (m_agent_operating_state) + { + case STATE_MOTORS_OFF: + { + flying_state_string = "motorsoff"; + break; + } + case STATE_TAKE_OFF: + { + flying_state_string = "takeoff"; + break; + } + case STATE_FLYING: + { + flying_state_string = "flying"; + break; + } + case STATE_LAND: + { + flying_state_string = "land"; + break; + } + case STATE_UNAVAILABLE: + { + flying_state_string = "unavailable"; + break; + } + default: + { + flying_state_string = "unavailable"; + break; + } + } + + // Get the Flying status as a string + std::string instant_controller_string; + switch (m_instant_controller) + { + case DEFAULT_CONTROLLER: + { + instant_controller_string = "default"; + break; + } + case DEMO_CONTROLLER: + { + instant_controller_string = "demo"; + break; + } + case STUDENT_CONTROLLER: + { + instant_controller_string = "student"; + break; + } + case MPC_CONTROLLER: + { + instant_controller_string = "mpc"; + break; + } + case REMOTE_CONTROLLER: + { + instant_controller_string = "remote"; + break; + } + case TUNING_CONTROLLER: + { + instant_controller_string = "tuning"; + break; + } + case PICKER_CONTROLLER: + { + instant_controller_string = "picker"; + break; + } + case TEMPLATE_CONTROLLER: + { + instant_controller_string = "template"; + break; + } + case CSONE_CONTROLLER: + { + instant_controller_string = "csone"; + break; + } + case TESTMOTORS_CONTROLLER: + { + instant_controller_string = "testmotors"; + break; + } + default: + { + instant_controller_string = "none"; + break; + } + } + // Concatenate the json together using a string stream std::stringstream ss; - ss << "{\u0022crazyradiostatus\u0022: \u0022" << crazyradio_status_string << "\u0022}"; - //ss << R"({"crazyradiostatus": ")" << m_crazyradio_status << R"("})"; - std::string s = ss.str(); + ss << "{"; + ss << "\u0022crazyradiostatus\u0022: \u0022" << crazyradio_status_string << "\u0022"; + ss << " , "; + ss << "\u0022batterylevel\u0022: \u0022" << battery_level_string << "\u0022"; + ss << " , "; + ss << "\u0022flyingstate\u0022: \u0022" << flying_state_string << "\u0022"; + ss << " , "; + ss << "\u0022instantcontroller\u0022: \u0022" << instant_controller_string << "\u0022"; + ss << " , "; + ss << "\u0022setpointdefault\u0022:"; + ss << "{"; + ss << "\u0022x\u0022: " << std::setprecision(3) << std::fixed << m_setpoint_default[0]; + ss << " , "; + ss << "\u0022y\u0022: " << std::setprecision(3) << std::fixed << m_setpoint_default[1]; + ss << " , "; + ss << "\u0022z\u0022: " << std::setprecision(3) << std::fixed << m_setpoint_default[2]; + ss << " , "; + ss << "\u0022yaw\u0022: " << std::setprecision(1) << std::fixed << m_setpoint_default[3]*RAD2DEG; + ss << "}"; + ss << " , "; + ss << "\u0022setpointstudent\u0022:"; + ss << "{"; + ss << "\u0022x\u0022: " << std::setprecision(3) << std::fixed << m_setpoint_student[0]; + ss << " , "; + ss << "\u0022y\u0022: " << std::setprecision(3) << std::fixed << m_setpoint_student[1]; + ss << " , "; + ss << "\u0022z\u0022: " << std::setprecision(3) << std::fixed << m_setpoint_student[2]; + ss << " , "; + ss << "\u0022yaw\u0022: " << std::setprecision(1) << std::fixed << m_setpoint_student[3]*RAD2DEG; + ss << "}"; + ss << " , "; + ss << "\u0022stateestimate\u0022:"; + ss << "{"; + ss << "\u0022x\u0022: " << std::setprecision(3) << std::fixed << m_cf_state_estimate_xyz_rpy[0]; + ss << " , "; + ss << "\u0022y\u0022: " << std::setprecision(3) << std::fixed << m_cf_state_estimate_xyz_rpy[1]; + ss << " , "; + ss << "\u0022z\u0022: " << std::setprecision(3) << std::fixed << m_cf_state_estimate_xyz_rpy[2]; + ss << " , "; + ss << "\u0022roll\u0022: " << std::setprecision(1) << std::fixed << m_cf_state_estimate_xyz_rpy[3]*RAD2DEG; + ss << " , "; + ss << "\u0022pitch\u0022: " << std::setprecision(1) << std::fixed << m_cf_state_estimate_xyz_rpy[4]*RAD2DEG; + ss << " , "; + ss << "\u0022yaw\u0022: " << std::setprecision(1) << std::fixed << m_cf_state_estimate_xyz_rpy[5]*RAD2DEG; + ss << "}"; + ss << " , "; + ss << "\u0022debugvaluesstudent\u0022:"; + ss << "{"; + ss << "\u0022value1\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[0]; + ss << " , "; + ss << "\u0022value2\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[1]; + ss << " , "; + ss << "\u0022value3\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[2]; + ss << " , "; + ss << "\u0022value4\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[3]; + ss << " , "; + ss << "\u0022value5\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[4]; + ss << " , "; + ss << "\u0022value6\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[5]; + ss << " , "; + ss << "\u0022value7\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[6]; + ss << " , "; + ss << "\u0022value8\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[7]; + ss << " , "; + ss << "\u0022value9\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[8]; + ss << " , "; + ss << "\u0022value10\u0022: " << std::setprecision(3) << std::fixed << m_debug_values_student[9]; + ss << "}"; + ss << "}"; + + //std::string s = ss.str(); // Put the string into the response //response.data = "test of service for web interface"; //response.data = "{\"crazyradiostatus\": \"%d\"}", m_crazyradio_status; @@ -192,6 +456,13 @@ int main(int argc, char* argv[]) // > Subscribe to the topic ros::Subscriber crazyRadioStatusSubscriber = nodeHandle_to_crazyradio.subscribe("CrazyRadioStatus", 1, crazyRadioStatusCallback); + // FOR THE BATTERY LEVEL + // > Get a node handle to the Battery Monitor + std::string namespace_to_BatteryMonitor = m_namespace + "/BatteryMonitor"; + ros::NodeHandle nodeHandle_to_BatteryMonitor(namespace_to_BatteryMonitor); + // > Subscribe to the topic + ros::Subscriber newBatteryLevelSubscriber = nodeHandle_to_BatteryMonitor.subscribe("Level", 1, newBatteryLevelCallback); + // FOR THE FLYING STATE OF THE AGENT // > Get a node handle to the Flying Agent Client std::string namespace_to_FlyingAgentClient = m_namespace + "/FlyingAgentClient"; @@ -203,13 +474,6 @@ int main(int argc, char* argv[]) // > Subscribe to the topic ros::Subscriber instantControllerSubscriber = nodeHandle_to_FlyingAgentClient.subscribe("ControllerUsed", 1, instantControllerChangedCallback); - // FOR THE BATTERY LEVEL - // > Get a node handle to the Battery Monitor - std::string namespace_to_BatteryMonitor = m_namespace + "/BatteryMonitor"; - ros::NodeHandle nodeHandle_to_BatteryMonitor(namespace_to_BatteryMonitor); - // > Subscribe to the topic - ros::Subscriber newBatteryLevelSubscriber = nodeHandle_to_BatteryMonitor.subscribe("Level", 1, newBatteryLevelCallback); - // FOR THE DEFAULT CONTROLLER SETPOINT // > Get a node handle to the Default Controller Service std::string namespace_to_DefaultController = m_namespace + "/DefaultControllerService"; @@ -222,7 +486,18 @@ int main(int argc, char* argv[]) std::string namespace_to_StudentController = m_namespace + "/StudentControllerService"; ros::NodeHandle nodeHandle_to_StudentController(namespace_to_StudentController); // > Subscribe to the topic - ros::Subscriber stuentControllerSetpointChangedSubscriber = nodeHandle_to_StudentController.subscribe("SetpointChanged", 1, studentControllerSetpointChangedCallback); + ros::Subscriber studentControllerSetpointChangedSubscriber = nodeHandle_to_StudentController.subscribe("SetpointChanged", 1, studentControllerSetpointChangedCallback); + + // FOR THE DEBUG VALUES OF THE STUDENT CONTROLLER + // > Subscribe to the topic + ros::Subscriber studentControllerDebugValuesSubscriber = nodeHandle_to_StudentController.subscribe("DebugTopic", 1, studentControllerDebugValuesCallback); + + // FOR THE STATE ESTIMATE FROM THE CRAZYFLIE + // > Get a node handle to the Crazy Radio node + std::string namespace_to_CrazyRadio = m_namespace + "/CrazyRadio"; + ros::NodeHandle nodeHandle_to_CrazyRadio(namespace_to_CrazyRadio); + // > Subscribe to the topic + ros::Subscriber cfStateEstimateSubscriber = nodeHandle_to_CrazyRadio.subscribe("CFStateEstimate", 1, cfStateEstimateCallback); diff --git a/dfall_ws/src/dfall_pkg/src/nodes/CrazyRadioEmulator.cpp b/dfall_ws/src/dfall_pkg/src/nodes/CrazyRadioEmulator.cpp index 87af0d62..6ce70b49 100644 --- a/dfall_ws/src/dfall_pkg/src/nodes/CrazyRadioEmulator.cpp +++ b/dfall_ws/src/dfall_pkg/src/nodes/CrazyRadioEmulator.cpp @@ -305,6 +305,24 @@ void timerCallback_update_cfStateEstimate(const ros::TimerEvent&) // Simulate the quadrotor for one time step m_quadrotor_sim.simulate_for_one_time_step( yaml_cfSimulation_deltaT_in_seconds ); + // Perform safety check if required + if (yaml_isEnabled_strictSafety) + { + // Estimate the height at the next measurement + float height_at_next_measurement = m_quadrotor_sim.m_position[2] + yaml_cfSimulation_deltaT_in_seconds * m_quadrotor_sim.m_velocity[2]; + // Turn-off if too high + if (height_at_next_measurement > yaml_maxHeight_for_strictSafety_meters) + { + // Send the MOTORS-OFF command to the Flying Agent Client + IntWithHeader msg; + msg.shouldCheckForAgentID = false; + msg.data = CMD_CRAZYFLY_MOTORS_OFF; + flyingAgentClientCommandPublisher.publish(msg); + // Inform the user + ROS_ERROR_STREAM("[CRAZY RADIO] Height safety check failed, measured = " << height_at_next_measurement << ", max allowed = " << yaml_maxHeight_for_strictSafety_meters ); + } + } + // Local variable for the data of this quadrotor FlyingVehicleState quadrotor_data; @@ -467,6 +485,11 @@ void fetchCrazyRadioConfigYamlParameters(ros::NodeHandle& nodeHandle) // Add the "CrazyRadioConfig" namespace to the "nodeHandle" ros::NodeHandle nodeHandle_for_paramaters(nodeHandle, "CrazyRadioConfig"); + // FLAG FOR WHETHER TO USE HEIGHT AS A TRIGGER FOR + // PUBLISHING A MOTORS-OFF COMMAND + yaml_isEnabled_strictSafety = getParameterBool(nodeHandle_for_paramaters,"isEnabled_strictSafety"); + yaml_maxHeight_for_strictSafety_meters = getParameterFloat(nodeHandle_for_paramaters,"maxHeight_for_strictSafety_meters"); + // SIMULATION FREQUENCY FOR EMULATING A CRAZYFLIE [Hertz] float yaml_cfSimulation_frequency = getParameterFloat(nodeHandle_for_paramaters,"cfSimulation_frequency"); diff --git a/dfall_ws/src/dfall_pkg/src/nodes/StudentControllerService.cpp b/dfall_ws/src/dfall_pkg/src/nodes/StudentControllerService.cpp index 424a48df..20c0131b 100644 --- a/dfall_ws/src/dfall_pkg/src/nodes/StudentControllerService.cpp +++ b/dfall_ws/src/dfall_pkg/src/nodes/StudentControllerService.cpp @@ -452,7 +452,10 @@ bool calculateControlOutput(Controller::Request &request, Controller::Response & // The "DebugMsg" type has 10 properties from "value_1" to "value_10", all of // type "float64" that you can fill in with data you would like to plot in // real-time. - // debugMsg.value_1 = thrustAdjustment; + debugMsg.value_1 = thrustAdjustment; + debugMsg.value_2 = rollRate_forResponse; + debugMsg.value_3 = pitchRate_forResponse; + debugMsg.value_4 = yawRate_forResponse; // ...................... // debugMsg.value_10 = your_variable_name; @@ -639,25 +642,6 @@ bool getCurrentSetpointCallback(GetSetpointService::Request &request, GetSetpoin return true; } -// -------------------------------------------------------------------------------- -// DDDD EEEEE BBBB U U GGGG V V A L U U EEEEE SSSS -// D D E B B U U G V V A A L U U E S -// D D EEE BBBB U U G GG V V A A L U U EEE SSS -// D D E B B U U G G V V AAAAA L U U E S -// DDDD EEEEE BBBB UUU GGG V A A LLLLL UUU EEEEE SSSS -// -// CCCC A L L BBBB A CCCC K K -// C A A L L B B A A C K K -// C A A L L BBBB A A C KKK -// C AAAAA L L B B AAAAA C K K -// CCCC A A LLLLL LLLLL BBBB A A CCCC K K -// -------------------------------------------------------------------------------- - -bool getDebugValuesCallback(GetDebugValuesService::Request &request, GetDebugValuesService::Response &response) -{ - // What should I write in this function? Or do the students fill it in depending on what they want to debug? -} - @@ -1101,14 +1085,6 @@ int main(int argc, char* argv[]) { - // Instantiate the local variable "getDebugValuesService" to be - // a "ros::ServiceServer" type variable that advertises the service - // called "GetDebugValues". This service has the input-output - // behaviour defined in the "GetDebugValuesService.srv" file (located - // in the "srv" folder). When a request is made - // of this service the "getDebugValuesCallback" function is called. - ros::ServiceServer getDebugValuesService = nodeHandle.advertiseService("GetDebugValues", getDebugValuesCallback); - // Print out some information to the user. diff --git a/web_interface/html/agent.php b/web_interface/html/agent.php index deee41d2..6b19f212 100644 --- a/web_interface/html/agent.php +++ b/web_interface/html/agent.php @@ -2,6 +2,10 @@ include("page_header.html"); ?> + +<script src="js/sse_agentStatus.js?ver=0.1"></script> + + <div class="full-window-fixed"> </div> @@ -29,8 +33,19 @@ </tr> </table> - <div class="top-bar-title padbelow"> - Agent (IP <?php echo $_SERVER['REMOTE_ADDR']; ?>) + <!-- + <div class="top-bar-title"> + Agent (IP <?php echo $_SERVER['REMOTE_ADDR']; ?>) + </div> + --> + + <div class="top-bar-status-icons padbelow"> + <div class="on-off-switch type2forstatusbar"> + <input type="checkbox" id="checkboxTopBarStatus" onchange=checkboxTopBarStatus_changed()><label for="checkboxTopBarStatus"></label> + </div> + <img id="radio-icon" class="status-icon-radio" src="img/rf_disconnected.png"> + <img id="battery-icon" class="status-icon-battery" src="img/battery_unavailable.png"> + <img id="flying-state-icon" class="status-icon-flying-state" src="img/flying_state_unavailable.png"> </div> </div> diff --git a/web_interface/html/agent_control_tab_default.html b/web_interface/html/agent_control_tab_default.html index 5f92bf87..f187738b 100644 --- a/web_interface/html/agent_control_tab_default.html +++ b/web_interface/html/agent_control_tab_default.html @@ -202,5 +202,126 @@ <hr class="hr-basic navy"> +<br> + +<table class="mse-table"> + <tr> + <td></td> + <td class="mse-column-title-cell"> + <span class="mse-column-title-span-full-text">Measured<br>Pose</span> + <span class="mse-column-title-span-short-text">Meas.<br>Pose</span> + </td> + <td class="mse-column-title-cell"> + <span class="mse-column-title-span-full-text">Error</span> + <span class="mse-column-title-span-short-text">Error</span> + </td> + <td class="mse-column-title-cell"> + <span class="mse-column-title-span-full-text">Current<br>Setpoint</span> + <span class="mse-column-title-span-short-text">Curr.<br>Setp.</span> + </td> + </tr> + <tr> + <td class="mse-row-title-cell"> + x + </td> + <td class="mse-data-cell" id="default-measurement-x">xx.xx</td> + <td class="mse-data-cell" id="default-error-x">xx.xx</td> + <td class="mse-data-cell" id="default-setpoint-x">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + y + </td> + <td class="mse-data-cell" id="default-measurement-y">xx.xx</td> + <td class="mse-data-cell" id="default-error-y">xx.xx</td> + <td class="mse-data-cell" id="default-setpoint-y">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + z + </td> + <td class="mse-data-cell" id="default-measurement-z">xx.xx</td> + <td class="mse-data-cell" id="default-error-z">xx.xx</td> + <td class="mse-data-cell" id="default-setpoint-z">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + yaw + </td> + <td class="mse-data-cell" id="default-measurement-yaw">xx.xx</td> + <td class="mse-data-cell" id="default-error-yaw">xx.xx</td> + <td class="mse-data-cell" id="default-setpoint-yaw">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + pitch + </td> + <td class="mse-data-cell" id="default-measurement-pitch">xx.xx</td> + <td></td> + <td></td> + </tr> + <tr> + <td class="mse-row-title-cell"> + roll + </td> + <td class="mse-data-cell" id="default-measurement-roll">xx.xx</td> + <td></td> + <td></td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>NOTES:</b> + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>(x,y,z)</b> in meters + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>(roll,pitch,yaw)</b> in degrees + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>error =</b> measured - setpoint + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>~2Hz</b> data refresh rate + </td> + </tr> +</table> + +<br> + +<hr class="hr-basic navy"> + +<table class="git-table"> + <tr> + <td class="git-button-cell"> + <button + class="button-push navy fullwidth" + id="buttonLoadYamlDefault" + onclick="sendRosMessage_outputLabelID('loadyamldefault', 'labelLoadYamlDefault')" + > + Load YAML Paramters for Default Controller + <div class="div-for-button-highlight-on-touchscreen navy"></div> + </button> + </td> + </tr> + <tr> + <td + style="text-align: center;" + id="labelLoadYamlDefault" + > +   + </td> + </tr> +</table> + +<hr class="hr-basic navy"> -<br><br><br><br><br> \ No newline at end of file +<br><br><br><br> \ No newline at end of file diff --git a/web_interface/html/agent_control_tab_student.html b/web_interface/html/agent_control_tab_student.html index 9265271c..57f0f48e 100644 --- a/web_interface/html/agent_control_tab_student.html +++ b/web_interface/html/agent_control_tab_student.html @@ -200,51 +200,211 @@ </tr> </table> +<hr class="hr-basic navy"> + <br> -<table class="centered-table"> +<table class="mse-table"> <tr> - <td class="centered-table-button-cell"> - <button - class="button-push navy" - id="buttonGetSetpointStudent" - onclick="getSetpointViaRosServiceCall_outputLabelID('student', 'labelGetSetpointStudent')" - > - get current - <div class="div-for-button-highlight-on-touchscreen navy"></div> - </button> + <td></td> + <td class="mse-column-title-cell"> + <span class="mse-column-title-span-full-text">Measured<br>Pose</span> + <span class="mse-column-title-span-short-text">Meas.<br>Pose</span> + </td> + <td class="mse-column-title-cell"> + <span class="mse-column-title-span-full-text">Error</span> + <span class="mse-column-title-span-short-text">Error</span> + </td> + <td class="mse-column-title-cell"> + <span class="mse-column-title-span-full-text">Current<br>Setpoint</span> + <span class="mse-column-title-span-short-text">Curr.<br>Setp.</span> + </td> + </tr> + <tr> + <td class="mse-row-title-cell"> + x + </td> + <td class="mse-data-cell" id="student-measurement-x">xx.xx</td> + <td class="mse-data-cell" id="student-error-x">xx.xx</td> + <td class="mse-data-cell" id="student-setpoint-x">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + y + </td> + <td class="mse-data-cell" id="student-measurement-y">xx.xx</td> + <td class="mse-data-cell" id="student-error-y">xx.xx</td> + <td class="mse-data-cell" id="student-setpoint-y">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + z + </td> + <td class="mse-data-cell" id="student-measurement-z">xx.xx</td> + <td class="mse-data-cell" id="student-error-z">xx.xx</td> + <td class="mse-data-cell" id="student-setpoint-z">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + yaw + </td> + <td class="mse-data-cell" id="student-measurement-yaw">xx.xx</td> + <td class="mse-data-cell" id="student-error-yaw">xx.xx</td> + <td class="mse-data-cell" id="student-setpoint-yaw">xx.xx</td> + </tr> + <tr> + <td class="mse-row-title-cell"> + pitch + </td> + <td class="mse-data-cell" id="student-measurement-pitch">xx.xx</td> + <td></td> + <td></td> + </tr> + <tr> + <td class="mse-row-title-cell"> + roll </td> - <td class="centered-table-button-cell"> + <td class="mse-data-cell" id="student-measurement-roll">xx.xx</td> + <td></td> + <td></td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>NOTES:</b> + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>(x,y,z)</b> in meters + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>(roll,pitch,yaw)</b> in degrees + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>error =</b> measured - setpoint + </td> + </tr> + <tr> + <td colspan="4" class="mse-caption-below"> + <b>~2Hz</b> data refresh rate + </td> + </tr> +</table> + +<br> + +<hr class="hr-basic navy"> + +<table class="git-table"> + <tr> + <td class="git-button-cell"> <button - class="button-push navy" - id="buttonSetDefaultSetpointStudent" - onclick="putDefaultSetpointForGivenBaseID('inputControlStudentSetpoint')" + class="button-push navy fullwidth" + id="buttonLoadYamlStudent" + onclick="sendRosMessage_outputLabelID('loadyamlstudent', 'labelLoadYamlStudent')" > - put default + Load YAML Paramters for Student Controller <div class="div-for-button-highlight-on-touchscreen navy"></div> </button> </td> </tr> <tr> - <td class="centered-table-button-cell"> - <div - style="text-align: center;" - id="labelGetSetpointStudent" - > -   - </div> + <td + style="text-align: center;" + id="labelLoadYamlStudent" + > +   </td> - <td class="centered-table-button-cell"> - <div - style="text-align: center;" - id="labelSetDeaultSetpointStudent" - > -   - </div> + </tr> +</table> + +<hr class="hr-basic navy"> + +<br> + +<h2 style="text-align: center;"> + Debug Values +</h2> + +<table class="debug-values-table"> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 1 + </td> + <td class="debug-values-data-cell" id="debug-value1">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 2 </td> + <td class="debug-values-data-cell" id="debug-value2">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 3 + </td> + <td class="debug-values-data-cell" id="debug-value3">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 4 + </td> + <td class="debug-values-data-cell" id="debug-value4">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 5 + </td> + <td class="debug-values-data-cell" id="debug-value5">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 6 + </td> + <td class="debug-values-data-cell" id="debug-value6">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 7 + </td> + <td class="debug-values-data-cell" id="debug-value7">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 8 + </td> + <td class="debug-values-data-cell" id="debug-value8">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 9 + </td> + <td class="debug-values-data-cell" id="debug-value9">xx.xx</td> + </tr> + <tr> + <td></td> + <td class="debug-values-row-title-cell"> + Value 10 + </td> + <td class="debug-values-data-cell" id="debug-value10">xx.xx</td> </tr> </table> +<br> + <hr class="hr-basic navy"> -<br><br><br><br><br> \ No newline at end of file +<br><br><br><br> \ No newline at end of file diff --git a/web_interface/html/agent_tab_code.html b/web_interface/html/agent_tab_code.html index 08f1127c..38bf15e7 100644 --- a/web_interface/html/agent_tab_code.html +++ b/web_interface/html/agent_tab_code.html @@ -8,11 +8,16 @@ Upload your code below. <br> <br> - <input class="fileupload" type="file" id="myToUpload" name="fileToUpload"> - <br> - <input type="submit" value="Upload Code" name="submit"> + <input class="fileupload" type="file" id="myToUpload" name="fileToUpload"> + <br> + <input type="submit" value="Upload Code" name="submit"> </form> + <br><br><br> + + <div id="agentStatusDebuggingDiv"></div> + + <br><br> </main> </div> </body> \ No newline at end of file diff --git a/web_interface/html/agent_tab_control.php b/web_interface/html/agent_tab_control.php index 51095d44..77f93a7a 100644 --- a/web_interface/html/agent_tab_control.php +++ b/web_interface/html/agent_tab_control.php @@ -78,7 +78,7 @@ <div class="control-tabs"> <input name="control-tabs" type="radio" id="control-tab-1" checked="checked" class="control-tab-input"/> <label for="control-tab-1" class="control-tab-label">Default</label> - <div class="control-tab-panel"> + <div class="control-tab-panel" id="control-tab-panel-default"> <?php include("agent_control_tab_default.html"); ?> @@ -86,10 +86,12 @@ <input name="control-tabs" type="radio" id="control-tab-2" class="control-tab-input"/> <label for="control-tab-2" class="control-tab-label">Student</label> - <div class="control-tab-panel"> + <div class="control-tab-panel" id="control-tab-panel-student"> <?php include("agent_control_tab_student.html"); ?> </div> -</div> \ No newline at end of file +</div> + +<br> \ No newline at end of file diff --git a/web_interface/html/bashscripts/rosGetStatusJson_forAgent.sh b/web_interface/html/bashscripts/rosGetStatusJson_forAgent.sh new file mode 100755 index 00000000..8b2d573e --- /dev/null +++ b/web_interface/html/bashscripts/rosGetStatusJson_forAgent.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# +# +# Check whether a command is supplied +# > and put it into a more redable variable +if [ "$#" -eq 0 ]; then + eventid="0" +elif [ "$#" -eq 1 ]; then + eventid=$1 +else + echo "not allowed to supply two or more arguments" + exit 1 +fi +# +# Check that the command supplied is valid +if ! [[ $eventid =~ ^[0-9]*$ ]]; then + echo "event id must be an integer, event id = $1" + exit 1 +fi +# +# 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 + # Check if the agent exists + if rosnode list | grep -q "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)"; then + # Set a flag that agent was found + agentfound="true" + # Convert the agent ID to a zero padded string + agentnamespace=$(printf "agent%03d" $DFALL_DEFAULT_AGENT_ID) + # Perform the service call for the status json + statusjsonfull="$(rosservice call /$ROS_NAMESPACE/$agentnamespace/AgentStatusForWebInterface/StatusAsJson 0)" + # Cut out the "data: " from the start, and the single " at the end + statusjsoncut1="${statusjsonfull:7:${#statusjsonfull}-8}" + # Remove all new line characters followed by 2 spaces + statusjsoncut2=$(echo $statusjsoncut1|tr -d '\n ') + # Remove all remaining new line characters + statusjsoncut3=$(echo $statusjsoncut2|tr -d '\n') + #statusjsoncut3=${statusjsoncut2//$'\n'/} + # Remove all carriage return characters + statusjsoncut4=$(echo $statusjsoncut3|tr -d '\r') + # Remove all tab characters + statusjsoncut5=$(echo $statusjsoncut4|tr -d '\t') + # Remove all remaining \ escape characters + statusjson=${statusjsoncut5//\\} + # + # Check that the status json is valid based on length + if [ ${#statusjsonfull} -le 1 ]; then + # Set a flag that agent was NOT found + agentfound="false" + # Set all other values to "not available (na)" + statusjson="\"na\"" + fi + # + else + # Set a flag that agent was NOT found + agentfound="false" + # Set all other values to "not available (na)" + statusjson="\"na\"" + fi +else + # Set a flag that agent was NOT found + agentfound="false" + # Set all other values to "not available (na)" + statusjson="\"na\"" +fi + +# Return that the values collected +echo "{"\ + "\"id\": \"$eventid\","\ + "\"agentfound\": \"$agentfound\","\ + "\"statusjson\": $statusjson"\ + "}" + +# DEBUGGING: For testing directly in terminal +# rosservice call /dfall/agent001/AgentStatusForWebInterface/StatusAsJson 0 \ No newline at end of file diff --git a/web_interface/html/bashscripts/rosLoadYaml_forAgent.sh b/web_interface/html/bashscripts/rosLoadYaml_forAgent.sh new file mode 100755 index 00000000..d2b2ecbe --- /dev/null +++ b/web_interface/html/bashscripts/rosLoadYaml_forAgent.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# +# Check that exactly one command is supplied +if [ "$#" -ne 1 ]; then + echo "failed" + exit 1 +fi +# +# Check that the command supplied is valid +if [ "$1" != "default" ] && [ "$1" != "student" ]; then + echo "failed" + exit 1 +fi +# +# Put the command into a variable for make things more readable +command=$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 +# +# 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 + # Check if the agent exists + if rosnode list | grep -q "$(printf "/dfall/agent%03d" $DFALL_DEFAULT_AGENT_ID)"; then + # Convert the agent ID to a zero padded string + agentnamespace=$(printf "agent%03d" $DFALL_DEFAULT_AGENT_ID) + # Send the message + if [ "$command" == "default" ]; then + # Publish the request + temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/ParameterService/requestLoadYamlFilename dfall_pkg/StringWithHeader "{data: DefaultController, shouldCheckForAgentID: False}")" + # Return that the message was sent + echo "sent" + # + elif [ "$command" == "student" ]; then + # Publish the request + temp="$(rostopic pub -1 /$ROS_NAMESPACE/$agentnamespace/ParameterService/requestLoadYamlFilename dfall_pkg/StringWithHeader "{data: StudentController, shouldCheckForAgentID: False}")" + # 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 "Agent $DFALL_DEFAULT_AGENT_ID not found" + fi +else + echo "ROS Master not found" +fi diff --git a/web_interface/html/callBashScript.php b/web_interface/html/callBashScript.php index 0e2b4553..c3b7401d 100644 --- a/web_interface/html/callBashScript.php +++ b/web_interface/html/callBashScript.php @@ -88,6 +88,14 @@ elseif ($scriptname == "rosGetSetpointStudent") { $output = shell_exec("./bashscripts/rosGetCurrentSetpoint_forAgent.sh student"); } + // For the CONTROL tab: + // LOAD YAML PARAMETERS + elseif ($scriptname == "rosLoadYamlDefault") { + $output = shell_exec("./bashscripts/rosLoadYaml_forAgent.sh default"); + } + elseif ($scriptname == "rosLoadYamlStudent") { + $output = shell_exec("./bashscripts/rosLoadYaml_forAgent.sh student"); + } echo "$output"; diff --git a/web_interface/html/checkForRosAgent.php b/web_interface/html/checkForRosAgent.php deleted file mode 100644 index df098596..00000000 --- a/web_interface/html/checkForRosAgent.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php - $output = shell_exec("./checkForRosAgent.sh"); - echo "$output"; -?> diff --git a/web_interface/html/checkForRosMaster.php b/web_interface/html/checkForRosMaster.php deleted file mode 100644 index db6b26f8..00000000 --- a/web_interface/html/checkForRosMaster.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - $output = shell_exec("./checkForRosMaster.sh"); - if ($output == 1) - { - echo "exists"; - } - else - { - echo "not found"; - } -?> diff --git a/web_interface/html/css/buttons.css b/web_interface/html/css/buttons.css index b64a03fa..c69685cb 100644 --- a/web_interface/html/css/buttons.css +++ b/web_interface/html/css/buttons.css @@ -1,39 +1,3 @@ -.setpoint-label-cell -{ - margin: 0; - padding-top: 2px; - padding-bottom: 2px; - font-size: 1.4em; - font-weight: bold; - text-align: center; - -} - -.setpoint-increment-cell -{ - margin: 0; - padding-top: 5px; - padding-bottom: 5px; -} - -.setpoint-input-field-cell -{ - margin: 0; - padding-top: 5px; - padding-bottom: 5px; -} - -.setpoint-input-field -{ - width:7em; - margin: 0; - padding-top: 5px; - padding-bottom: 5px; - font-size: 1.4em; - font-family: monospace; - text-align: right; -} - .button-push { position: relative; diff --git a/web_interface/html/css/debug_values_table.css b/web_interface/html/css/debug_values_table.css new file mode 100644 index 00000000..81e5fef1 --- /dev/null +++ b/web_interface/html/css/debug_values_table.css @@ -0,0 +1,91 @@ +.debug-values-table +{ + margin-left: auto; + margin-right: auto; + padding: 0; + border-collapse: collapse; + table-layout: fixed; + //border: 1px solid black; + //border-left: 2px solid black; +} + +.debug-values-column-title-cell +{ + margin: 0; + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: bold; + text-align: center; + border: 1px solid black; +} + +.debug-values-row-title-cell +{ + margin: 0; + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: bold; + text-align: center; + border: 1px solid black; +} + +.debug-values-data-cell +{ + margin: 0; + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: normal; + font-family: monospace; + text-align: right; + border: 1px solid black; +} + +.debug-values-caption-below +{ + margin: 0; + padding-left: 5px; + padding-right: 5px; + padding-top: 5px; + padding-bottom: 2px; + font-size: 1.2em; + font-weight: normal; + text-align: left; + //border: 1px solid black; +} + + + + + +/* SETTINGS FOR WHEN THE SCREEN IS SMALL */ +@media screen and (max-width: 420px) +{ + .debug-values-column-title-cell + { + font-size: 1.2em; + } + + .debug-values-row-title-cell + { + font-size: 1.2em; + } + + .debug-values-data-cell + { + font-size: 1.2em; + } + + .debug-values-caption-below + { + font-size: 1.0em; + } +} diff --git a/web_interface/html/css/layout.css b/web_interface/html/css/layout.css index b156d1eb..ec5d25d4 100644 --- a/web_interface/html/css/layout.css +++ b/web_interface/html/css/layout.css @@ -107,6 +107,26 @@ padding-bottom: 10px; } +.top-bar-status-icons +{ + position: relative; + width: 100%; + text-align: center; + font-size: 1.2em; + font-weight: 400; + padding-top: 0px; + padding-bottom: 0px; + padding-left: 0px; + padding-right: 0px; + background: none; + white-space: nowrap; + overflow-x: hidden; +} +.top-bar-status-icons.padbelow +{ + padding-bottom: 5px; +} + .ros-table { diff --git a/web_interface/html/css/measurement_setpoint_error_table.css b/web_interface/html/css/measurement_setpoint_error_table.css new file mode 100644 index 00000000..48f0e7fc --- /dev/null +++ b/web_interface/html/css/measurement_setpoint_error_table.css @@ -0,0 +1,151 @@ +.setpoint-label-cell +{ + margin: 0; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: bold; + text-align: center; +} + +.setpoint-increment-cell +{ + margin: 0; + padding-top: 5px; + padding-bottom: 5px; +} + +.setpoint-input-field-cell +{ + margin: 0; + padding-top: 5px; + padding-bottom: 5px; +} + +.setpoint-input-field +{ + width:7em; + margin: 0; + padding-top: 5px; + padding-bottom: 5px; + font-size: 1.4em; + font-family: monospace; + text-align: right; +} + + + + + +.mse-table +{ + margin-left: auto; + margin-right: auto; + padding: 0; + border-collapse: collapse; + table-layout: fixed; + //border: 1px solid black; + //border-left: 2px solid black; +} + +.mse-column-title-cell +{ + width: 28%; + margin: 0; + padding-left: 5px; + padding-right: 5px; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: bold; + text-align: center; + border: 1px solid black; +} + +.mse-column-title-span-full-text +{ + display: inline; +} + +.mse-column-title-span-short-text +{ + display: none; +} + +.mse-row-title-cell +{ + margin: 0; + padding-left: 2px; + padding-right: 2px; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: bold; + text-align: center; + border: 1px solid black; +} + +.mse-data-cell +{ + margin: 0; + padding-left: 5px; + padding-right: 5px; + padding-top: 2px; + padding-bottom: 2px; + font-size: 1.4em; + font-weight: normal; + font-family: monospace; + text-align: right; + border: 1px solid black; +} + +.mse-caption-below +{ + margin: 0; + padding-left: 5px; + padding-right: 5px; + padding-top: 5px; + padding-bottom: 2px; + font-size: 1.2em; + font-weight: normal; + text-align: left; + //border: 1px solid black; +} + + + + + +/* SETTINGS FOR WHEN THE SCREEN IS SMALL */ +@media screen and (max-width: 420px) +{ + .mse-column-title-cell + { + font-size: 1.2em; + } + + .mse-row-title-cell + { + font-size: 1.2em; + } + + .mse-column-title-span-full-text + { + display: none; + } + + .mse-column-title-span-short-text + { + display: inline; + } + + .mse-data-cell + { + font-size: 1.2em; + } + + .mse-caption-below + { + font-size: 1.0em; + } +} diff --git a/web_interface/html/css/status_bar.css b/web_interface/html/css/status_bar.css new file mode 100644 index 00000000..936fc8f6 --- /dev/null +++ b/web_interface/html/css/status_bar.css @@ -0,0 +1,71 @@ +.status-icon-radio +{ + height: 40px; + width: 55px; + object-fit: contain; + text-align: center; + margin-left: 0px; + margin-right: 0px; + margin-top: 0px; + margin-bottom: 0px; + padding-left: 10px; + padding-right: 10px; + padding-top: 0px; + padding-bottom: 0px; +} + +.status-icon-battery +{ + height: 40px; + width: 26px; + object-fit: contain; + text-align: center; + margin-left: 0px; + margin-right: 0px; + margin-top: 0px; + margin-bottom: 0px; + padding-left: 10px; + padding-right: 10px; + padding-top: 0px; + padding-bottom: 0px; +} + +.status-icon-flying-state +{ + height: 40px; + width: 51px; + object-fit: contain; + text-align: center; + margin-left: 0px; + margin-right: 0px; + margin-top: 0px; + margin-bottom: 0px; + padding-left: 10px; + padding-right: 10px; + padding-top: 0px; + padding-bottom: 0px; +} + + + +/* SETTINGS FOR WHEN THE SCREEN IS SMALL */ +@media screen and (max-width: 420px) +{ + .status-icon-radio + { + padding-left: 5px; + padding-right: 5px; + } + + .status-icon-battery + { + padding-left: 5px; + padding-right: 5px; + } + + .status-icon-flying-state + { + padding-left: 5px; + padding-right: 5px; + } +} \ No newline at end of file diff --git a/web_interface/html/css/switches.css b/web_interface/html/css/switches.css index 82e09139..2fcdce35 100644 --- a/web_interface/html/css/switches.css +++ b/web_interface/html/css/switches.css @@ -175,6 +175,81 @@ +/* =========================== */ +/* === TYPE 2 === */ +/* =========================== */ +.on-off-switch.type2forstatusbar:after +{ + position: absolute; + top: 0px; + right: 12px; + + content: ''; + + color: #ccc; + font-family: Arial; + font-size: 18px; + line-height: 40px; + font-weight: bold; +} + +.on-off-switch.type2forstatusbar label +{ + width: 70px; + height: 32px; + background: none; + border:3px solid #ccc; + border-radius: 19px; +} + +.on-off-switch.type2forstatusbar label:after +{ + top: 4px; + left: auto; + right: 4px; + overflow: hidden; + + content: 'LIVE'; + + width: 24px; + height: 24px; + + color: #fff; + font-family: Arial; + font-size: 18px; + line-height: 25px; + text-align: center; + font-weight: bold; + text-indent: 80px; + background: #ccc; + box-shadow: none; + border-radius: 12px; + + transform: translateX(-38px); + transition: all 0.4s 0.2s, width 0.2s linear, text-indent 0.4s linear; +} + +.on-off-switch.type2forstatusbar input:checked + label +{ + border-color: #269CE9; +} + +.on-off-switch.type2forstatusbar input:checked + label:after +{ + left: auto; + + width: 62px; + text-indent: 0; + background: #269CE9; + + transform: translateX(0px); + transition: all 0.4s, width 0.2s 0.4s linear, text-indent 0.3s 0.4s linear; +} + + + + + /* =========================== */ /* === TYPE 3 === */ /* =========================== */ diff --git a/web_interface/html/css/tabs_control.css b/web_interface/html/css/tabs_control.css index e02f56e9..7d0c8c4e 100644 --- a/web_interface/html/css/tabs_control.css +++ b/web_interface/html/css/tabs_control.css @@ -70,6 +70,15 @@ color:black; overflow: hidden; overflow-y: scroll; + border-left: 1px solid; + border-right: 1px solid; + border-width: 10px; + border-color: #c43c35; + /* + border-color: #c43c35; + border-color: #f44336; + border-color: #57a957; + */ } /* Make the respective panel visible when its tab-input is checked */ diff --git a/web_interface/html/css/tabs_main.css b/web_interface/html/css/tabs_main.css index 3be20ea5..0fb1582f 100644 --- a/web_interface/html/css/tabs_main.css +++ b/web_interface/html/css/tabs_main.css @@ -51,6 +51,16 @@ color: #000; } +/* The "top" specification here is critical */ +/* Use: */ +/* 189px when the top banner contains the */ +/* motors-off button and agent IP */ +/* 190px when the top banner contains the */ +/* motors-off button and status */ +/* icons */ +/* 229px when the top banner contains the */ +/* motors-off button, agent IP, and */ +/* status icons. */ .main-tab-panel { order: 99; @@ -60,7 +70,7 @@ bottom: 0; left: 0px; right: 0px; - top: 189px; + top: 190px; bottom: 0px; margin-top: 0px; margin-bottom: 0px; @@ -121,9 +131,19 @@ text-align: center; } + /* The "top" specification here is critical */ + /* Use: */ + /* 147px when the top banner contains the */ + /* motors-off button and agent IP */ + /* 162px when the top banner contains the */ + /* motors-off button and status */ + /* icons */ + /* 190px when the top banner contains the */ + /* motors-off button, agent IP, and */ + /* status icons. */ .main-tab-panel { - top: 147px; + top: 162px; } .main-tab-panel.thin-padding diff --git a/web_interface/html/img/battery_20.png b/web_interface/html/img/battery_20.png new file mode 100644 index 0000000000000000000000000000000000000000..cc7ae62ab7c662f7cf3e098e713232d2d6e0ae14 GIT binary patch literal 2099 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 C)C#=- literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_40.png b/web_interface/html/img/battery_40.png new file mode 100644 index 0000000000000000000000000000000000000000..cea5ab35b9605910112dd8a7e2eda371c430894b GIT binary patch literal 2147 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 B4M+e0 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_60.png b/web_interface/html/img/battery_60.png new file mode 100644 index 0000000000000000000000000000000000000000..1f75257c3c1f44fc10f318274741942916b47b70 GIT binary patch literal 2232 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 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_80.png b/web_interface/html/img/battery_80.png new file mode 100644 index 0000000000000000000000000000000000000000..8224e7e1e2ed8e251f37f802b14ce4ebd9027f35 GIT binary patch literal 2252 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 ZV8c+LNiLl1kbWcp$Fn%wMr*&+{{e&KMh*Y~ literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_empty.png b/web_interface/html/img/battery_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf35549aab7c7c0247cebd69a176d68ea67387c GIT binary patch literal 3379 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 JPDHLkV1lbxPcZ-h literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_full.png b/web_interface/html/img/battery_full.png new file mode 100644 index 0000000000000000000000000000000000000000..495383be5e346e624d7a92670fcf9ddb053f99ef GIT binary patch literal 2152 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 bw6(vwhAXL2?5wx<EdXpTVo*#AucUtf=a~RS literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_unavailable.png b/web_interface/html/img/battery_unavailable.png new file mode 100644 index 0000000000000000000000000000000000000000..71765cfe18b1d7a915b7f92daf06df3b6558521f GIT binary patch literal 2775 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 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/battery_unknown.png b/web_interface/html/img/battery_unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..e20348f65a39bac7c0e40a2a4c0b338ef018dc03 GIT binary patch literal 3138 zcmV-I488M-P)<h;3K|Lk000e1NJLTq003G5004yu1^@s6+raPm00001b5ch_0Itp) z=>Px#32;bRa{vGr5&!@f5&>tQ(oz5b3+G8hK~#7F?VV?A6+0GyPpAn52mwM9y)2@K zC4r?00*Ky)04;3=D;BMm{<Mf9x>g_oLi9%vqO5=)KxiU}_TV8UR0$+PhtOMilt6$$ zV9y!k?A*C?=Xx@8J>%T56lPrJ*#55Z*vH47h#x<GfagL_ph)Pm@Nysm9%w=Bzys}p z7DPZ$HeQSNKno%uC>yUu3j$qCP(A^(`S$Hwsh2NbCVu(yrF_6d79vWOD)qc##fq1T z7A=}<L5$@`9RrOMr6O0ZT>0hV#fuBSfB#;>a!m{b6fa)<ef#$9|JATz!)*r1LK+p} z)XXqFJ$>2Lt5<&sY3($^J9OyqkH(D~Z`1?}@jB8+zkdC?IYOQxq=u?Y@W);8`Sa%r zsvu#vj&vPQo;>M}HH=`jjEjqdgoFerUAi<2vV6tv2p&Iv3>Pk3fV{jsMIOBmo<D!y zv3Bj+DV8%ZDZmlX+1c5(SUF3UED61P_l6QB?C+*yV`G83#&hS+vFt#dcYdIN8%Meh zb-i#z5E95Nk80Jbv0F#4j)N{zB(y~cwS3-RFSNDfEkzilv)o5#Wo5-befl(s`o7%U z+!!8aI6+C^Nf(MVw{PG6E0+1s9I}5zMn-0pDN`o1MvWS&<;s=2&GAgYBn1Y2|Ni~K z_(os!_U+ro0j0Yz0wE{RpV^|}$SA!{n>PQhTD9sKv(Hj`)1a}{mOFX!<bNMNeAuTT z8Or(${x58M@ZiDIjT$vd##!b+%}(h}_0b<ceypC7l9Co~kkyUj#*G`}4<9~!?CaOB z(dzJE_uNx(6kPhifdjwetS(+Fl-}$#>7WTmL`42PM4L?4Ph;JdEnDiw*%65E4rS6} zRu+xFb8>Rv#fuj}lOL_CS+nM$9zA*t!20?}Y7?WM8}ykoXMV+B^Y2<2b?epz{I3L2 zQBm5cLg7cPBBZ6I!L3`j{OXgKm^c#~$x^=vod7P-`0{AAckkW{_`FY7gGw4eC5?{Q z!WV?26*zV3luyNq6)W~(`0(KkQ5E~lCql>L15PJi2|@+XRJ`#?iz>&kflLq@G-v=# zn>JN=uzyf@5}lEeF@gDwu0HVe5~8rk%*@Pz>NwI%v}@N+9TXP#*i`_wUZ1+&y?b}K zPe1@qP|)w+zpqcfoceiu5rkAfq0Y-q_t;fHty=l|Fr{ek-o5)1@(KEa3JUtehYvNi zmBrbTHYjZTdUZ!eHG%;M3L15}CAC7#4&k)V%k0-vQX!+rV08)#I+y@2o{xYBnvav^ zR32!{2>Aqfp!qmiPUV5NjF3-&2bzzQ<y0PM%Lw@dc%b<>Sx#kog~f7?CIz5s79Ko! z0H{HPSFc_HE<OOR-$D8E<@5jQ)Tskis#Gy4S!{CGfku-p*u8r<q^71SYDrSO^Ejp# zCuP@w0RsTn_IMr>mc})pagi6+tXZS{so_RPM??Mk^_3P&trpknl$K4cn_4=qz$>us z-Ma_$w|)C|m^5h;3>!8KrcRv-ty;Ab2Gb}bSAfRFUKl-kG@wnMQdVk>0|yR-{{8zy zmo8m^%*Fya)RhA+vBA1^>)_e5pZa%Pc7q)|b|`-%MvQ<B8#X}MvSkB_5EAbA%W&Sj zdC;v}Hw7{QB|n-nDBybb>}d#@2sLls9OlfK1J|!#hw<ab`$5~eb0@^d$HS#dm;B;H z1~>|O!GZ;_aN$D55V3RT&Oohm{`~nt;pzdR7v!c*n_$8O{{;`~Qs~E|AHyl<80alq zwkS}k;?zAHJ9Z3cdCaP#ejaTv{KU{ZpiiGZ%IL)}Qb2$spy_2dZQ3-YdbAd|XU`s> z)eAcu$(b=j-^QJ~B<h-+aE^eUHER}73yX<~fkTH5DdS-~D^J6_K#sZ>0xsGK2Tc<W z8Yhz@*v*<XQ&yL$g_GY7M>f|qiP3X3BH6ZWn_g5%!-Rw0zkk0nyJNLNTRymU?Ha6H zxf0s8ZL5rpS)7$?g$jYUsvJ3T#ER$wJu4hEZg~q>(3>}J!sgAJ1IC+)Fd8@0P*3!3 zG~uApK+=%ci4!Lb<9Q>EHXfmQfbh|WaL~B9(vX#1FDf(vFwBR#ERjw)Xqw3xxh74T z7)5i(q19`^v@`@1t#lF&dg#!hhI$852-)F`K;u>k(6i65Bi}$Wgo7rNs;yhM`i-5b zl`UGd2>SKw7pSb<;b^9%*fj@wfXs|VJA0;MCQD74xsDw>0`49FG!g`|<s)5(-qhKo zXeBvK=jnwP$c-I4Hc(W^!W{ukwV`qDxN+k`Qn7ve_63w%uU<V!N=gbCAF2r9T}P-Y z!sOzy6tHX8F6Q;SZrr%h^!5)wAq5C1RG`tsSovR~x95b|Oq@6ox_0ea08G{&DKwy| zPo}<~blQ4u<j9e*di83(C?R2m2K3yybN%+1FoLA8B1ICdH#+4Udl}kU;p4}T!?I<| z{6vz`I4v*?88XB#PIQ3uplLOH)TmK@djUyJN{hDI4L+g)v;3q6O&Yi{W5xiUD#Pld zTxl`(=+UE4sZu4Ma3?)d_tB)o*6NnYfQI%1x(PD<z|yv8G7%#)1GS^A&eW30=*DeM z+7`Wb?OOQs=~F&WQWW84cvnKELP`mm0265D@BkA9vRlqiV&zg_r36jdKN@W?w`9o@ zWs#Tp-FTG_G)=EptXPrHlC*$RrcBBAyY)so(9{ppNdx3)rk9+YEL@3SkRMYz&}4AK z9GT|YVMfN}h4LyLXm(}>ZIhlodv++<*eY8p&~(-)>o)rK?JL!JAyR=RgLUQx4<5{J zrMOB3THS5*>eWjMz^sr`foA4pv@fM{<;tuMQe341%{CEr>eNXJxavYm1DaH#Y&PiT zskR!f`d%8)Z{EBCvTY_O+MG)btf2SOfX1^y^XusLU9$NFRozGfns(GO=QgE2E0@t# z8qn%WIJugwE{B<WDM6d5pj*;P30lVE;G_YqhHJDx#VsvZTMlVJ)0Q@JbS^5p*H*^O zerZ<0t5>fMr%#^-vOc0ULfM=&pb4yuQ*YKFr0F_Xfn~jVpauPM9%v7=AOeE2@mjP8 zS`Yz2*+|i%X(K+ZdXsvT_9T)8|K-b<1tGF08`py-)7<XeyDLVFw3PMe(Idcb!T^3C z4A!q-Zx00lQd|w1PIDv!M!mB&XcyMx$&-OC*a~1}DGOJFUb=LtVdZFPYx(l!hH2aq z=~~caHOg}+AnlZu!MPT+4Dig9(Y2uIJPn=$TwAwpZKl4CrFAW6A~JL4OhYhqY|!-S z(+$(OCDPTP=@3{l%~h|iFmQB`%BoeXphb%oEXdVYSA!;Uix)3e&Ml>LMakZdc3+Vt zX?l9Pa$13_>8c7Rh2o!%esJpe2Nf$zx2|u|mTKpshdt1Oehd$^2U-vTLD_gM+5;_! zfS_!=7VUu+L_knBL0dFVf4>VVi?rGBFbWD9KREvL8%Zh*>H|qs!C`T$S9d)|S{Ok= zqlxTYe8{I&vrjE)g@uD(TX*~#(rYxj{3BT1gMua@xZUhGwIDj0G9x2H9TXP#ckbNr zJF*^+ecQ`Z`JkXl2p%`QNh^%BXS5|PTpTs7vuDrxRTtSL`vvL*aDm37fOp5m#igmA zr&VY=|CyaOppGkS_w?NXIx1QFT>$+2-wxz*o~OPMdQ16w58}sY8X?#;%q&J5PlhdY z(m8!8LQhGrO)rC=xa8#I0DWbHeKS#AZanfWbI_nciBVBe|3BPGoj`NX4Vs?A^D?^O zxpD{d^77;}xz|+OiWgsif8Y`Monm8Sukp$;1)7ASwa{M>b_#B=jN{FuFr~xK`Q5@L zw!V1%d9V{_<xGPnxj2@LJAeNCfAC8{e?xBsXI@e#!&UiQ{I1tZT$x{ti^9eqdC{q9 zfj~1k_$IE0L!J@Xhb3iaXV=o}zgb!tuQ;^L!p%wdah)<12L!utV&Z$+tcgll0&OCn zuusZ0ybF6uHURKI+W?VAga?|3k_~A*&^AEi5#fR6p=3iE53~&sc|>@ic_`VC#sh5w cL>>|U1A{WS@`N`;j{pDw07*qoM6N<$f>IsmaR2}S literal 0 HcmV?d00001 diff --git a/web_interface/html/img/flying_state_disabling.png b/web_interface/html/img/flying_state_disabling.png new file mode 100644 index 0000000000000000000000000000000000000000..667c8027f2b902f08f8e81d3120599a5ed151d48 GIT binary patch literal 3303 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 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/flying_state_enabling.png b/web_interface/html/img/flying_state_enabling.png new file mode 100644 index 0000000000000000000000000000000000000000..ef2dc5bffa6d27edbb6c6ff1171cd77a5af5c20a GIT binary patch literal 3512 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 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/flying_state_flying.png b/web_interface/html/img/flying_state_flying.png new file mode 100644 index 0000000000000000000000000000000000000000..b0b51f3d42edbfad9f875c1faa413391c4f21de9 GIT binary patch literal 3001 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 O1B?xCTrJl}Mg9v7cDHr_ literal 0 HcmV?d00001 diff --git a/web_interface/html/img/flying_state_off.png b/web_interface/html/img/flying_state_off.png new file mode 100644 index 0000000000000000000000000000000000000000..70669664f07c12ef27a41f5b5fb918aff93da688 GIT binary patch literal 3106 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 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/flying_state_unavailable.png b/web_interface/html/img/flying_state_unavailable.png new file mode 100644 index 0000000000000000000000000000000000000000..60f6878b603abc0ba5dd375765ff06ad36a397ab GIT binary patch literal 4797 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 Xor1sWxHYe500000NkvXXu0mjfcNGNh literal 0 HcmV?d00001 diff --git a/web_interface/html/img/flying_state_unknown.png b/web_interface/html/img/flying_state_unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..5c0c7337a103e5726af53def62e37ad22209c972 GIT binary patch literal 3868 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 QwZf$_F*G-*)pw2mACB2W-2eap literal 0 HcmV?d00001 diff --git a/web_interface/html/img/rf_connected.png b/web_interface/html/img/rf_connected.png new file mode 100644 index 0000000000000000000000000000000000000000..f9f3580113053f71fe86344e6f34a74e98c17b70 GIT binary patch literal 6410 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 Y1MT~g8Gu|h>;M1&07*qoM6N<$f{u_*s{jB1 literal 0 HcmV?d00001 diff --git a/web_interface/html/img/rf_connecting.png b/web_interface/html/img/rf_connecting.png new file mode 100644 index 0000000000000000000000000000000000000000..5f1d4ca1b48496ecf1372e1a0f727058bbc24d95 GIT binary patch literal 5679 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 VL=C_Dk2wGU002ovPDHLkV1kGw)=~ff literal 0 HcmV?d00001 diff --git a/web_interface/html/img/rf_disconnected.png b/web_interface/html/img/rf_disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..0e99e624009059328820b317c908d337caf55ddc GIT binary patch literal 5321 zcmV;)6gKOLP)<h;3K|Lk000e1NJLTq006cC004#v1^@s6aEfrL00001b5ch_0Itp) z=>Px#32;bRa{vGr5&!@f5&>tQ(oz5b6mCgGK~#7F?VSm@O;sDm&oz@NMMR-8H7LW^ z%~*!dSUw)ylFASb9z7*ZzBH*+nyD{Jq^p!iBr=r3EmQa;Au?nvN|`BXegFUcI_`1Y zyVl<4>~qc@-u*oLInzFCul2sav(DaYz3cy`DWyWPi3R*-fqZ^zkhDiEKnn=i!htU> zAYl8_t<op~TNv=A1q5tgx>Xt_U<(7jw19x^OSej+3Wy2fw*ck>(heRxSbX2UeWkeX z-MhE+(W6I;96x?MKli`>`YT`fu}G03M~fCMdW3tiV#N;gv1G}T`_DMzj6M1C<@+oC z+DHksfPih_fKYDPvZeBO-+fn;k2`kksJMOm_DbQ$AAkI@!v6jHOY%5FQQD6QHQ_E- zuH5eMUb%ARE$5wg-lp(=?z!h~Em*MNF>OKeVYUU*Wacr41OmHk*|Pd8R;;K)kh!m4 zzrH$q-<+dT_bh+@{Ku<Qsq(|6mtMNEcJ12VgdeL`t-3iaElsA7BU}?$un!+TT<ps) zzihmC@!~6%E?xQ$0{zoZKb4QLjc1;gC{f}-dV2bjMvWRR;C{swS1jgxo@qb=DjFED zfB*gW!iyFyYA|>1+-9GD{(18yOO~V`J9eyK0&JS{BH6REN5AsQE9bXu+jeHtrcJYW zZJ+XrC9wj&27CAJ-Q}iDn|96AsZ;+oXU?3KM~)mRnnc^W=2W~vZPu*W+%|36%xc%J z-Hh|kKYycZDwm*&_zvt%n>L*{Y0{)FgqfEa@!r(sWp`ZDhALF35W+QY2-mnF-0+3a zE1t9isFPmvb6l^7FlrL|?YG~uA9H*D>#x6taB~_$-SawqT<`PRJR>7xBKHd}xL`wE zg}L<0z5p8_l$khjVn$|W=8X#%E^O$OtqT<@6uRV+OF|fx$c8rzF+s+NMK-ifoh!N= z{JVtKty>pbyLRnK`}f~}ANu2uKcWwh`E}j8b^lAiufP8Kch5QJoNX}|<gzb%7Hp0S zPoF;h>T%=7_4xSXkK4o9h2pg>d)<Z&8=lzf*RLNcT)1$	=^KmYtQgny-M*uVbz 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 b<XGT;)oaV+3*z)L00000NkvXXu0mjf@PT8C literal 0 HcmV?d00001 diff --git a/web_interface/html/js/sendRosMessage.js b/web_interface/html/js/sendRosMessage.js index cabbe8cd..91bbd954 100644 --- a/web_interface/html/js/sendRosMessage.js +++ b/web_interface/html/js/sendRosMessage.js @@ -30,6 +30,15 @@ function sendRosMessage_outputLabelID(rosMessage, labelID) { scriptname_for_php = scriptname_for_php + "rosEnablestudent"; } + else if (rosMessage == "loadyamldefault") + { + scriptname_for_php = scriptname_for_php + "rosLoadYamlDefault"; + } + else if (rosMessage == "loadyamlstudent") + { + scriptname_for_php = scriptname_for_php + "rosLoadYamlStudent"; + } + // Set the label to be sending diff --git a/web_interface/html/js/sse_agentStatus.js b/web_interface/html/js/sse_agentStatus.js new file mode 100644 index 00000000..34c1172d --- /dev/null +++ b/web_interface/html/js/sse_agentStatus.js @@ -0,0 +1,468 @@ +let agentStatusEventSource; + +window.onload = function() { + document.getElementById("checkboxTopBarStatus").checked = false; +}; + +function checkboxTopBarStatus_changed() +{ + if ( document.getElementById("checkboxTopBarStatus").checked ) + { + severSentAgentStatus_start(); + } + else + { + severSentAgentStatus_stop(); + } +} + + +function severSentAgentStatus_start() +{ + // Check is server sent events are supported + if (typeof(EventSource) == "undefined") + { + // Server-sent events are NOT supported + // Display this to the user + document.getElementById("agentStatusDebuggingDiv").innerHTML = "Server Sent Events are not supported by this browser."; + return; + } + + // If the code makes it to here, then + // server sent events are supported + + // Create the "EventSource" variable + agentStatusEventSource = new EventSource("sse_agentStatus.php"); + + // Handle the "onopen" event + agentStatusEventSource.onopen = function(eventSource) + { + // DEBUGGING: print out + //document.getElementById("agentStatusDebuggingDiv").innerHTML += "Event: on open <br>"; + }; + + // Handle the "onerror" event + agentStatusEventSource.onerror = function(eventSource) + { + // DEBUGGING: print out + document.getElementById("agentStatusDebuggingDiv").innerHTML += "Event: on error <br>"; + + // Check is the event is still connecting + if (this.readyState == EventSource.CONNECTING) + { + // DEBUGGING: print out + document.getElementById("agentStatusDebuggingDiv").innerHTML += "Reconnecting (readyState = " + this.readyState + ")... <br>"; + } + else + { + // DEBUGGING: print out + document.getElementById("agentStatusDebuggingDiv").innerHTML += "Error has occured. <br>"; + } + }; + + // Handle the "message" event + //agentStatusEventSource.onmessage = function(eventSource) + agentStatusEventSource.addEventListener("message", function(eventSource) + { + // DEBUGGING: print out the whole message + //document.getElementById("agentStatusDebuggingDiv").innerHTML += "Event: message, data: " + eventSource.data + " <br>"; + }); + + // Handle the "ping" event + agentStatusEventSource.addEventListener("agentstatus", function(eventSource) + { + // Extract "agent found" entry in the json + agent_found = JSON.parse(eventSource.data).agentfound; + + // If the agent is found + if ( agent_found === "true" ) + { + // Parse the status dictionary from the JSON + status_dict = JSON.parse(eventSource.data).statusjson; + + // Call the fuctions to update the status icons + // > For the Radio status: + updateStatusIconRadio( status_dict.crazyradiostatus ); + // > For the Battery level: + updateStatusIconBattery( status_dict.batterylevel ); + // > For the Flying state: + updateStatusIconFlyingState( status_dict.flyingstate ); + // > For the Instant controller: + updateStatusForInstantController( status_dict.instantcontroller ); + + // Call the function to update the "measurement, + // setpoint, error" tables + // > For the Default controller: + updateDefaultMseTable( status_dict.stateestimate , status_dict.setpointdefault ); + // > For the Student controller: + updateStudentMseTable( status_dict.stateestimate , status_dict.setpointstudent ); + + // Call the function to update the "debug values" table + updateStudentDebugValuesTable( status_dict.debugvaluesstudent ); + + // DEBUGGING: print out the whole message + //document.getElementById("agentStatusDebuggingDiv").innerHTML += "Event: agent status, data: " + eventSource.data + " <br>"; + } + // Otherwise, the agent is NOT found + else + { + // So set all the icons accordingly + // > For the Radio status: + updateStatusIconRadio( "disconnected" ); + // > For the Battery level: + updateStatusIconBattery( "unavailable" ); + // > For the Flying state: + updateStatusIconFlyingState( "unavailable" ); + // > For the Instant controller: + updateStatusForInstantController( "none" ); + + // Call the function to clear the "measurement, + // setpoint, error" tables + // > For the Default controller: + clearDefaultMseTable(); + // > For the Student controller: + clearStudentMseTable(); + + // Call the function to clear the "debug values" table + clearStudentDebugValuesTable(); + + // DEBUGGING: print out the whole message + //document.getElementById("agentStatusDebuggingDiv").innerHTML += "Event: agent status, data: " + eventSource.data + " <br>"; + } + }); + +} // END OF: function severSentAgentStatus_start() + + + + + +function severSentAgentStatus_stop() +{ + // Check is server sent events are supported + if (typeof(EventSource) == "undefined") + { + // Server-sent events are NOT supported + // Display this to the user + document.getElementById("agentStatusDebuggingDiv").innerHTML = "Server Sent Events are not supported by this browser."; + return; + } + + // If the code makes it to here, then + // server sent events are supported + + // Close the "EventSource" variable + agentStatusEventSource.close(); + + // DEBUGGING: print out + //document.getElementById("agentStatusDebuggingDiv").innerHTML += "Closed. <br>"; + + // So set all the icons accordingly + // > For the Radio status: + updateStatusIconRadio( "disconnected" ); + // > For the Battery level: + updateStatusIconBattery( "unavailable" ); + // > For the Flying state: + updateStatusIconFlyingState( "unavailable" ); + // > For the Instant controller: + updateStatusForInstantController( "none" ); + + // Call the function to clear the "measurement, + // setpoint, error" tables + // > For the Default controller: + clearDefaultMseTable(); + // > For the Student controller: + clearStudentMseTable(); + + // Call the function to clear the "debug values" table + clearStudentDebugValuesTable(); + +} // END OF: function severSentAgentStatus_stop() + + + +function updateStatusIconRadio( status_string ) +{ + if ( typeof status_string !== "string" ) + { + // Default to disconnected + status_string = "disconnected"; + } + + switch ( status_string.toLowerCase() ) + { + case "connected": + document.getElementById("radio-icon").src = "img/rf_connected.png"; + break; + case "connecting": + document.getElementById("radio-icon").src = "img/rf_connecting.png"; + break; + case "disconnected": + document.getElementById("radio-icon").src = "img/rf_disconnected.png"; + break; + default: + document.getElementById("radio-icon").src = "img/rf_disconnected.png"; + } + +} // END OF: "function updateStatusIconRadio( status_string )" + + + +function updateStatusIconBattery( level_string ) +{ + if ( typeof level_string !== "string" ) + { + // Default to unavailable + level_string = "unavailable"; + } + + switch ( level_string.toLowerCase() ) + { + case "000": + document.getElementById("battery-icon").src = "img/battery_empty.png"; + break; + case "010": + document.getElementById("battery-icon").src = "img/battery_20.png"; + break; + case "020": + document.getElementById("battery-icon").src = "img/battery_20.png"; + break; + case "030": + document.getElementById("battery-icon").src = "img/battery_40.png"; + break; + case "040": + document.getElementById("battery-icon").src = "img/battery_40.png"; + break; + case "050": + document.getElementById("battery-icon").src = "img/battery_60.png"; + break; + case "060": + document.getElementById("battery-icon").src = "img/battery_60.png"; + break; + case "070": + document.getElementById("battery-icon").src = "img/battery_80.png"; + break; + case "080": + document.getElementById("battery-icon").src = "img/battery_80.png"; + break; + case "090": + document.getElementById("battery-icon").src = "img/battery_full.png"; + break; + case "100": + document.getElementById("battery-icon").src = "img/battery_full.png"; + break; + case "unknown": + document.getElementById("battery-icon").src = "img/battery_unknown.png"; + break; + case "unavailable": + document.getElementById("battery-icon").src = "img/battery_unavailable.png"; + break; + default: + document.getElementById("battery-icon").src = "img/battery_unavailable.png"; + } + +} // END OF: "function updateStatusIconBattery( level_string )" + + + +function updateStatusIconFlyingState( state_string ) +{ + if ( typeof state_string !== "string" ) + { + // Default to unavailable + state_string = "unavailable"; + } + + switch ( state_string.toLowerCase() ) + { + case "motorsoff": + document.getElementById("flying-state-icon").src = "img/flying_state_off.png"; + break; + case "takeoff": + document.getElementById("flying-state-icon").src = "img/flying_state_enabling.png"; + break; + case "flying": + document.getElementById("flying-state-icon").src = "img/flying_state_flying.png"; + break; + case "land": + document.getElementById("flying-state-icon").src = "img/flying_state_disabling.png"; + break; + case "unavailable": + document.getElementById("flying-state-icon").src = "img/flying_state_unavailable.png"; + break; + default: + document.getElementById("flying-state-icon").src = "img/flying_state_unavailable.png"; + } + +} // END OF: "function updateStatusIconRadio( status_string )" + + + +function updateStatusForInstantController( controller_string ) +{ + if ( typeof controller_string !== "string" ) + { + // Default to unavailable + controller_string = "none"; + } + + // Colours: + // #c43c35 Dark Red + // #f44336 Light Red + // #57a957 Green + + // Set all border colours to red + document.getElementById("control-tab-panel-default").style.borderColor = "#c43c35"; + document.getElementById("control-tab-panel-student").style.borderColor = "#c43c35"; + + switch ( controller_string.toLowerCase() ) + { + case "default": + document.getElementById("control-tab-panel-default").style.borderColor = "#57a957"; + break; + case "student": + document.getElementById("control-tab-panel-student").style.borderColor = "#57a957"; + break; + } + +} // END OF: "function updateStatusIconRadio( status_string )" + + +function updateDefaultMseTable( measurement_dict , setpoint_dict ) +{ + if ( (typeof measurement_dict !== "object") || (typeof setpoint_dict !== "object") ) + { + // Call function to set fields to blank + clearDefaultMseTable(); + return; + } + + document.getElementById("default-measurement-x").innerHTML = measurement_dict.x.toFixed(3); + document.getElementById("default-measurement-y").innerHTML = measurement_dict.y.toFixed(3); + document.getElementById("default-measurement-z").innerHTML = measurement_dict.z.toFixed(3); + document.getElementById("default-measurement-yaw").innerHTML = measurement_dict.yaw.toFixed(3); + document.getElementById("default-measurement-pitch").innerHTML = measurement_dict.pitch.toFixed(1); + document.getElementById("default-measurement-roll").innerHTML = measurement_dict.roll.toFixed(1); + + document.getElementById("default-setpoint-x").innerHTML = setpoint_dict.x.toFixed(3); + document.getElementById("default-setpoint-y").innerHTML = setpoint_dict.y.toFixed(3); + document.getElementById("default-setpoint-z").innerHTML = setpoint_dict.z.toFixed(3); + document.getElementById("default-setpoint-yaw").innerHTML = setpoint_dict.yaw.toFixed(1); + + document.getElementById("default-error-x").innerHTML = (measurement_dict.x -setpoint_dict.x ).toFixed(3); + document.getElementById("default-error-y").innerHTML = (measurement_dict.y -setpoint_dict.y ).toFixed(3); + document.getElementById("default-error-z").innerHTML = (measurement_dict.z -setpoint_dict.z ).toFixed(3); + document.getElementById("default-error-yaw").innerHTML = (measurement_dict.yaw-setpoint_dict.yaw).toFixed(1); + +} // END OF: "function updateDefaultMseTable( measurement_dict , setpoint_dict )" + +function clearDefaultMseTable() +{ + document.getElementById("default-measurement-x").innerHTML = "xx.xx"; + document.getElementById("default-measurement-y").innerHTML = "xx.xx"; + document.getElementById("default-measurement-z").innerHTML = "xx.xx"; + document.getElementById("default-measurement-yaw").innerHTML = "xx.xx"; + document.getElementById("default-measurement-pitch").innerHTML = "xx.xx"; + document.getElementById("default-measurement-roll").innerHTML = "xx.xx"; + + document.getElementById("default-setpoint-x").innerHTML = "xx.xx"; + document.getElementById("default-setpoint-y").innerHTML = "xx.xx"; + document.getElementById("default-setpoint-z").innerHTML = "xx.xx"; + document.getElementById("default-setpoint-yaw").innerHTML = "xx.xx"; + + document.getElementById("default-error-x").innerHTML = "xx.xx"; + document.getElementById("default-error-y").innerHTML = "xx.xx"; + document.getElementById("default-error-z").innerHTML = "xx.xx"; + document.getElementById("default-error-yaw").innerHTML = "xx.xx"; + +} // END OF: "function clearDefaultMseTable()" + + + +function updateStudentMseTable( measurement_dict , setpoint_dict ) +{ + if ( (typeof measurement_dict !== "object") || (typeof setpoint_dict !== "object") ) + { + // Call function to set fields to blank + clearStudentMseTable(); + return; + } + + document.getElementById("student-measurement-x").innerHTML = measurement_dict.x.toFixed(3); + document.getElementById("student-measurement-y").innerHTML = measurement_dict.y.toFixed(3); + document.getElementById("student-measurement-z").innerHTML = measurement_dict.z.toFixed(3); + document.getElementById("student-measurement-yaw").innerHTML = measurement_dict.yaw.toFixed(3); + document.getElementById("student-measurement-pitch").innerHTML = measurement_dict.pitch.toFixed(1); + document.getElementById("student-measurement-roll").innerHTML = measurement_dict.roll.toFixed(1); + + document.getElementById("student-setpoint-x").innerHTML = setpoint_dict.x.toFixed(3); + document.getElementById("student-setpoint-y").innerHTML = setpoint_dict.y.toFixed(3); + document.getElementById("student-setpoint-z").innerHTML = setpoint_dict.z.toFixed(3); + document.getElementById("student-setpoint-yaw").innerHTML = setpoint_dict.yaw.toFixed(1); + + document.getElementById("student-error-x").innerHTML = (measurement_dict.x -setpoint_dict.x ).toFixed(3); + document.getElementById("student-error-y").innerHTML = (measurement_dict.y -setpoint_dict.y ).toFixed(3); + document.getElementById("student-error-z").innerHTML = (measurement_dict.z -setpoint_dict.z ).toFixed(3); + document.getElementById("student-error-yaw").innerHTML = (measurement_dict.yaw-setpoint_dict.yaw).toFixed(1); + +} // END OF: "function updateStudentMseTable( measurement_dict , setpoint_dict )" + +function clearStudentMseTable() +{ + document.getElementById("student-measurement-x").innerHTML = "xx.xx"; + document.getElementById("student-measurement-y").innerHTML = "xx.xx"; + document.getElementById("student-measurement-z").innerHTML = "xx.xx"; + document.getElementById("student-measurement-yaw").innerHTML = "xx.xx"; + document.getElementById("student-measurement-pitch").innerHTML = "xx.xx"; + document.getElementById("student-measurement-roll").innerHTML = "xx.xx"; + + document.getElementById("student-setpoint-x").innerHTML = "xx.xx"; + document.getElementById("student-setpoint-y").innerHTML = "xx.xx"; + document.getElementById("student-setpoint-z").innerHTML = "xx.xx"; + document.getElementById("student-setpoint-yaw").innerHTML = "xx.xx"; + + document.getElementById("student-error-x").innerHTML = "xx.xx"; + document.getElementById("student-error-y").innerHTML = "xx.xx"; + document.getElementById("student-error-z").innerHTML = "xx.xx"; + document.getElementById("student-error-yaw").innerHTML = "xx.xx"; + +} // END OF: "function clearStudentMseTable()" + + + + +function updateStudentDebugValuesTable( values_dict ) +{ + if (typeof values_dict !== "object") + { + // Call function to set fields to blank + clearStudentDebugValuesTable(); + return; + } + + document.getElementById("debug-value1").innerHTML = values_dict.value1.toFixed(3); + document.getElementById("debug-value2").innerHTML = values_dict.value2.toFixed(3); + document.getElementById("debug-value3").innerHTML = values_dict.value3.toFixed(3); + document.getElementById("debug-value4").innerHTML = values_dict.value4.toFixed(3); + document.getElementById("debug-value5").innerHTML = values_dict.value5.toFixed(3); + document.getElementById("debug-value6").innerHTML = values_dict.value6.toFixed(3); + document.getElementById("debug-value7").innerHTML = values_dict.value7.toFixed(3); + document.getElementById("debug-value8").innerHTML = values_dict.value8.toFixed(3); + document.getElementById("debug-value9").innerHTML = values_dict.value9.toFixed(3); + document.getElementById("debug-value10").innerHTML = values_dict.value10.toFixed(3); + +} // END OF: "function updateStudentDebugValuesTable( values_dict )" + +function clearStudentDebugValuesTable() +{ + document.getElementById("debug-value1").innerHTML = "xx.xx"; + document.getElementById("debug-value2").innerHTML = "xx.xx"; + document.getElementById("debug-value3").innerHTML = "xx.xx"; + document.getElementById("debug-value4").innerHTML = "xx.xx"; + document.getElementById("debug-value5").innerHTML = "xx.xx"; + document.getElementById("debug-value6").innerHTML = "xx.xx"; + document.getElementById("debug-value7").innerHTML = "xx.xx"; + document.getElementById("debug-value8").innerHTML = "xx.xx"; + document.getElementById("debug-value9").innerHTML = "xx.xx"; + document.getElementById("debug-value10").innerHTML = "xx.xx"; +} // END OF: "function clearStudentDebugValuesTable()" \ No newline at end of file diff --git a/web_interface/html/killRosAgent.php b/web_interface/html/killRosAgent.php deleted file mode 100644 index 4d727404..00000000 --- a/web_interface/html/killRosAgent.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php - $output = shell_exec("./killRosAgent.sh"); - echo "$output"; -?> diff --git a/web_interface/html/launchRosAgent.php b/web_interface/html/launchRosAgent.php deleted file mode 100644 index 8b39f485..00000000 --- a/web_interface/html/launchRosAgent.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php - $output = shell_exec("./launchRosAgent.sh"); - echo "<pre>$output</pre>"; -?> diff --git a/web_interface/html/page_header.html b/web_interface/html/page_header.html index a2377b8c..92c815de 100644 --- a/web_interface/html/page_header.html +++ b/web_interface/html/page_header.html @@ -27,7 +27,9 @@ <link rel="stylesheet" type="text/css" href="css/tabs_main.css?ver=0.1" /> <link rel="stylesheet" type="text/css" href="css/tabs_home.css?ver=0.1" /> <link rel="stylesheet" type="text/css" href="css/tabs_control.css?ver=0.1" /> - + <link rel="stylesheet" type="text/css" href="css/status_bar.css?ver=0.1" /> + <link rel="stylesheet" type="text/css" href="css/measurement_setpoint_error_table.css?ver=0.1" /> + <link rel="stylesheet" type="text/css" href="css/debug_values_table.css?ver=0.1" /> <!-- Link the JavaScript files --> <script src="js/general.js?ver=0.1"></script> diff --git a/web_interface/html/rosnodeList.php b/web_interface/html/rosnodeList.php deleted file mode 100644 index c0e0db44..00000000 --- a/web_interface/html/rosnodeList.php +++ /dev/null @@ -1,4 +0,0 @@ -<?php - $output = shell_exec("./rosnodeList.sh"); - echo "<pre>$output</pre>"; -?> diff --git a/web_interface/html/sse_agentStatus.php b/web_interface/html/sse_agentStatus.php new file mode 100644 index 00000000..60eeed05 --- /dev/null +++ b/web_interface/html/sse_agentStatus.php @@ -0,0 +1,30 @@ + <?php + //date_default_timezone_set("America/New_York"); + header("Cache-Control: no-cache"); + header("Content-Type: text/event-stream"); + //header('Connection: keep-alive'); + + $event_id_counter = 0; + + while (true) { + // Every second, send a "ping" event. + + echo "event: agentstatus\n"; + + $output = shell_exec("./bashscripts/rosGetStatusJson_forAgent.sh " . $event_id_counter); + + //$curDate = date(DATE_ISO8601); + //echo 'data: {"time": "' . $curDate . '"}'; + + echo "data: $output"; + echo "\n\n"; + + // Send a simple message at random intervals. + + $event_id_counter++; + + ob_end_flush(); + flush(); + usleep(250000); + } +?> \ No newline at end of file diff --git a/web_interface/html/temp.html b/web_interface/html/temp.html deleted file mode 100644 index 35a76519..00000000 --- a/web_interface/html/temp.html +++ /dev/null @@ -1,69 +0,0 @@ - <?php echo "hello world"; ?> - - <?php echo '<br>'; ?> - - <?php echo date('Y-m-d H:i:s'); ?> - - <?php echo '<br>'; ?> - - <!-- <?php phpinfo(); ?> --> - - - <?php - // outputs the username that owns the running php/httpd process - // (on a system with the "whoami" executable in the path) - echo exec('whoami'); - ?> - - <?php echo '<br>'; ?> - - - - <?php - $output = shell_exec("ls -lart"); - echo "<pre>$output</pre>"; - ?> - - <?php echo '<br>'; ?> - - <!-- - <?php - $output = shell_exec("./rosnode_list.sh"); - echo "<pre>$output</pre>"; - ?> - --> - - <?php echo '<br>'; ?> - - <!--$output = shell_exec("./roscore.sh");--> - <!--$output = shell_exec("nohup ./roscore.sh > /dev/null &");--> - - <?php echo '<br>'; ?> - - <!-- - <?php - $output = shell_exec("./rosnode_list.sh"); - echo "<pre>$output</pre>"; - ?> - --> - - <?php echo '<br>'; ?> - - <!-- - <?php - $output = shell_exec("source /opt/ros/melodic/setup.bash"); - echo "<pre>$output</pre>"; - $outputtwo = shell_exec("rosnode list"); - echo "<pre>$outputtwo</pre>"; - ?> - --> - - <?php echo '<br>'; ?> - - <!-- - <?php - exec("dir", $output, $return); - echo "Dir returned $return, and output:\n"; - var_dump($output); - ?> - --> -- GitLab