Skip to content

Commit

Permalink
Finalized the GS GUI and added a receiver to receive the data coming …
Browse files Browse the repository at this point in the history
…from the rocket
  • Loading branch information
dyka3773 committed Jan 11, 2024
1 parent 4f88c3a commit 2e8b4bb
Show file tree
Hide file tree
Showing 15 changed files with 312 additions and 497 deletions.
97 changes: 28 additions & 69 deletions Ground Station/dropstar_gui/app.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
from flask import Flask, render_template, request, url_for

from flask import Flask, render_template, request
from functools import cache
from threading import Thread
from typing import Mapping, Tuple
import asyncio

from controllers import home_page, downlink_page, uplink_page, figures as figs, status as experiment_status
from controllers.tests import with_fluid, without_fluid
from controllers import home_page, downlink_page, figures as figs, status as experiment_status
from telecoms import rxsm_receiver


app = Flask(__name__, template_folder='templates', static_folder='static')


@app.route('/')
def index():
def index() -> str:
return home_page.render()


@app.route('/downlink/')
def downlink():
def downlink() -> str:
return downlink_page.render()


@app.route('/uplink/')
def uplink():
return uplink_page.render()


@app.route('/test-with-fluid/')
def test_with_fluid():
return with_fluid.render_page()


@app.route('/test-without-fluid/')
def test_without_fluid():
return without_fluid.render_page()


# The following routes are for special purposes and do not serve any pages

@app.route('/figures/<figure_name>')
async def figures(figure_name: str):
async def figures(figure_name: str) -> tuple[str, int] | str:
"""Sends an image to the client.
Args:
Expand All @@ -55,71 +42,36 @@ async def figures(figure_name: str):


@app.get('/status/')
async def status():
async def status() -> Tuple[Mapping[str, str | int | None], int]:
"""Gets the status of the system.
Returns:
Returns a tuple containing the status and the HTTP status code.
"""

motor_speed, sound_card_status, camera_status, heater_status = await asyncio.gather(
motor_speed, sound_card_status, camera_status, LO, SOE, SODS, error_code = await asyncio.gather(
experiment_status.get_motor_speed(),
experiment_status.get_sound_card_status(),
experiment_status.get_camera_status(),
experiment_status.get_heater_status()
experiment_status.get_LO_signal(),
experiment_status.get_SOE_signal(),
experiment_status.get_SODS_signal(),
experiment_status.get_errors()
)

status = {
'motor_speed': motor_speed,
'sound_card_status': sound_card_status,
'camera_status': camera_status,
'heater_status': heater_status,
'LO_status': LO,
'SOE_status': SOE,
'SODS_status': SODS,
'errors': error_code
}
return status, 200


@app.get('/delete_data/')
def delete_data():
"""Deletes the data from the CSV file on the rocket
Returns:
Returns a tuple containing the status and the HTTP status code.
"""
data_were_deleted = experiment_status.delete_data()
return ('OK', 200) if data_were_deleted else ('Error', 417)


@app.post('/status/check/<component>')
def check(component: str):
"""Checks the status of a component.
Args:
component (str): The component to be checked.
Returns:
Returns a tuple containing the status and the HTTP status code.
"""
if component == 'motor':
status = experiment_status.check_motor()
elif component == 'sound_card':
status = experiment_status.check_sound_card()
elif component == 'camera':
status = experiment_status.check_camera()
elif component == 'heater':
status = experiment_status.check_heater()
else:
return "Component not found", 400

app.logger.info(f"Component {component} status: {status}")

if status == True:
return "OK", 200
else:
return "Error", 417

# The following routes are for error handling


@app.errorhandler(404)
def page_not_found(error):
"""Handles 404 errors.
Expand All @@ -133,9 +85,9 @@ def page_not_found(error):
app.logger.error(f"Page not found. The requested URL was: {request.url}")
return render_template('404.j2'), 404

# The folloring route is for favicon


# The following route is for favicon
@cache
@app.route('/favicon.ico')
def favicon():
"""Sends the favicon to the client.
Expand All @@ -146,6 +98,13 @@ def favicon():
return app.send_static_file('img/favicon.png')


# Start the receiver thread
receiver_thread = Thread(
target=rxsm_receiver.receive_data,
daemon=True
)
receiver_thread.start()

if __name__ == '__main__':
app.run(
host="0.0.0.0", # This will make the app available to other computers on the network
Expand Down
47 changes: 10 additions & 37 deletions Ground Station/dropstar_gui/controllers/figures.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from cProfile import label
import io
import numpy as np
import matplotlib
Expand All @@ -6,7 +7,7 @@
from matplotlib import pyplot as plt

from .utils import base64_util
from .status import get_temperature, get_pressure
from .status import get_temperature

matplotlib.use('agg')

Expand All @@ -22,19 +23,22 @@ async def get_temp_plot(img: io.BytesIO) -> None:
"""
time_range_of_plot = 60 # This value determines the time range of the plot

sensor1, sensor2 = await asyncio.gather(
sensor1, sensor2, sensor3 = await asyncio.gather(
get_temperature("sensor1", time_range_of_plot),
get_temperature("sensor2", time_range_of_plot)
get_temperature("sensor2", time_range_of_plot),
get_temperature("sensor3", time_range_of_plot)
)

# FIXME: This can probably be done in one line for every sensor so that we don't have to repeat the code
x_1 = [i/3 for i in range(len(sensor1))]
x_2 = [i/3 for i in range(len(sensor2))]
x_3 = [i/3 for i in range(len(sensor3))]

figure, ax = plt.subplots()
# FIXME: This can probably be done in one line for every sensor so that we don't have to repeat the code
ax.plot(x_1, sensor1)
ax.plot(x_2, sensor2)

ax.plot(x_1, sensor1, label="Sensor 1")
ax.plot(x_2, sensor2, label="Sensor 2")
ax.plot(x_3, sensor3, label="Sensor 3")
ax.set_title("Temperature Plot")
ax.set_xlabel("Time (s)")
ax.set_ylabel("Temperature (C)")
Expand All @@ -43,35 +47,6 @@ async def get_temp_plot(img: io.BytesIO) -> None:
plt.close(figure)


async def get_pressure_plot(img: io.BytesIO) -> None:
"""Creates a matplotlib plot and stores it in the given io buffer.
Args:
img (io.BytesIO): A buffer to store the plot as an image.
"""
time_range_of_plot = 60 # This value determines the time range of the plot

sensor1, sensor2 = await asyncio.gather(
get_pressure("sensor1", time_range_of_plot),
get_pressure("sensor2", time_range_of_plot)
)

# FIXME: This can probably be done in one line for every sensor so that we don't have to repeat the code
x_1 = [i/3 for i in range(len(sensor1))]
x_2 = [i/3 for i in range(len(sensor2))]

figure, ax = plt.subplots()
# FIXME: This can probably be done in one line for every sensor so that we don't have to repeat the code
ax.plot(x_1, sensor1)
ax.plot(x_2, sensor2)
ax.set_title("Pressure Plot")
ax.set_xlabel("Time (s)")
ax.set_ylabel("Pressure (atm)")
ax.grid()
figure.savefig(img, format='png')
plt.close(figure)


async def get_plot_by_type(type: str) -> str:
"""Returns a base64 encoded image of a matplotlib plot.
Expand All @@ -87,8 +62,6 @@ async def get_plot_by_type(type: str) -> str:

if type == "temperature":
await get_temp_plot(img)
elif type == "pressure":
await get_pressure_plot(img)
else:
raise ValueError("Invalid figure type")

Expand Down
Loading

0 comments on commit 2e8b4bb

Please sign in to comment.