Skip to content

Commit

Permalink
Implemented individual abort and estops
Browse files Browse the repository at this point in the history
  • Loading branch information
mickey li committed Sep 29, 2022
1 parent 6382099 commit 945a1a6
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ RUN . /opt/ros/foxy/setup.sh \
&& colcon build --packages-select starling_ui_dashly starling_allocator_msgs --cmake-force-configure \
&& rm -r build

EXPOSE 3000
ENV PORT 3000

cmd ["ros2", "launch", "starling_ui_dashly", "dashboard_gunicorn.launch.xml"]
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ ros2 launch starling_ui_dashly dashboard_gunicorn.launxch.xml

which will run a user facing server on https://0.0.0.0:3000

> The port number can be specified with environment varibale `PORT`
### Docker

The project can also be run using a dockerfile, either build the local file after cloning as follows
Expand All @@ -48,6 +50,10 @@ Or use the built version on docker hub
docker run --it -rm --network=host mickeyli789/starling_ui_dashly:latest
```

> Specify port number using `-e PORT=3002` for example.
To make things easier, a MAKEFILE has been provided. Therefore to build and run the project, run `make run` in the root of this project.

## Usage

### Mission Control Screen (`/`)
Expand Down
2 changes: 1 addition & 1 deletion starling_ui_dashly/launch/dashboard_gunicorn.launch.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<launch>

<executable cmd="gunicorn -b 0.0.0.0:3000 'starling_ui_dashly.main:server()'" output="screen"/>
<executable cmd="gunicorn -b 0.0.0.0:$(env PORT 3000) 'starling_ui_dashly.main:server()'" output="screen"/>

</launch>
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pandas as pd
import json

import dash
from dash.dependencies import Input, Output, State
from dash.dependencies import Input, Output, State, MATCH, ALL
from dash import dcc, html
import dash_bootstrap_components as dbc
# import dash_core_components as dcc
Expand Down Expand Up @@ -123,21 +124,51 @@ def __control_panel_system_status_update(self, n_clicks, n_intervals):
# Call self.dashboard_node.get_system_status()
vehic_namespace = self.dashboard_node.get_current_vehicle_namespaces()
return html.Div([
html.P(f"System status update at {get_time()}"),
html.P(f"System status update at {get_time()}, {len(vehic_namespace)} vehicles detected"),
dbc.Row([
dbc.Col([
html.H4(f"Abort {vehicle_name}"),
dbc.Button(
html.Img(src='/static/stop-button.png', style={"max-width": "75%"}),
id=f"mc_btn_mission_abort_{vehicle_name}",
size="sm",
outline=True,
color="warning"),
html.Div([
dbc.Button(
f"Abort {vehicle_name}",
id={'type': f"mc_btn_mission_abort_drone", 'index': vehicle_name},
size="lg",
block=True,
color="warning"),
dbc.Button(
f"ESTOP {vehicle_name}",
id={'type': f"mc_btn_emergency_stop_drone", 'index': vehicle_name},
size="lg",
block=True,
color="danger"),
],
className="d-grid gap-5")
])
for vehicle_name in vehic_namespace
])
]),
html.Div("", id="mc_system_status_individual_vehicle_status_msg")
])

@app_callback(
Output("mc_system_status_individual_vehicle_status_msg", "children"),
[Input({"type": "mc_btn_mission_abort_drone", "index": ALL}, 'n_clicks'),
Input({"type": "mc_btn_emergency_stop_drone", "index": ALL}, 'n_clicks')]
)
def __control_panel_individual_drone_abort_press(self, abort_n_clicks, estop_n_clicks):
ctx = dash.callback_context
if ctx.triggered:
trigger_id = ctx.triggered[0]['prop_id'].split('.')[0]
trigger_dict = json.loads(trigger_id)
vehicle_name = trigger_dict["index"]

if trigger_dict["type"] == "mc_btn_mission_abort_drone":
self.dashboard_node.call_mission_abort_drone(vehicle_name)
return f"Abort {vehicle_name} pressed at {get_time()}"
else:
self.dashboard_node.send_emergency_stop_drone(vehicle_name)
return f"ESTOP {vehicle_name} pressed at {get_time()}"

return "Waiting for button press"

def __generate_control_panel_emergency_stop(self):
return html.Div([
html.H2("EMERGENCY STOP (/emergency_stop)", id="mc_title_estop"),
Expand Down
2 changes: 1 addition & 1 deletion starling_ui_dashly/starling_ui_dashly/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
try:
from .node import Dashboard_Node
from .handler import Dashboard_Handler
except Exception:
except ModuleNotFoundError:
from node import Dashboard_Node
from handler import Dashboard_Handler

Expand Down
13 changes: 8 additions & 5 deletions starling_ui_dashly/starling_ui_dashly/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def __init__(self):
self.timer_period = 0.01 # seconds
self.emergency_stop_timer = None

self.current_vehicle_namespaces = []

self.valid_methods = [
'nearest',
'random',
Expand Down Expand Up @@ -62,7 +64,7 @@ def send_emergency_stop_drone(self, drone_namespace):
msg = Empty()
emergency_stop_publisher_ = self.create_publisher(Empty, f'/{drone_namespace}/emergency_stop', 10)
emergency_stop_publisher_.publish(msg)
self.get_logger().info('emergency_stop published')
self.get_logger().info(f'emergency_stop published to {drone_namespace}')
emergency_stop_publisher_.destroy()

def call_mission_abort_drone(self, drone_namespace):
Expand All @@ -75,16 +77,17 @@ def call_mission_abort_drone(self, drone_namespace):
def get_current_vehicle_namespaces(self):
topic_list = self.get_topic_names_and_types()
namespaces = set()
self.get_logger().info('Found the following topics:')
# self.get_logger().info('Found the following topics:')
for topic_name, _ in topic_list:
self.get_logger().info(topic_name)
if 'mavros' in topic_name:
# self.get_logger().info(topic_name)
if 'mavros/state' in topic_name:
name = topic_name.split('/')[1]
if name == 'mavros':
name = ''
namespaces.add(name)
self.get_logger().info(f'Found {len(namespaces)} namespaces: {",".join(namespaces)}')
return list(namespaces)
self.current_vehicle_namespaces = list(namespaces)
return self.current_vehicle_namespaces

def is_valid_trajectory_dict(self, trajectory_dict):
self.get_logger().info("testing validity of trajectory dictionary")
Expand Down

0 comments on commit 945a1a6

Please sign in to comment.