Skip to content

Commit

Permalink
Merge pull request #129 from tritonuas/feat/takeoff
Browse files Browse the repository at this point in the history
Input mechanism to request takeoff mode.
  • Loading branch information
Tyler-Lentz authored May 11, 2024
2 parents 01d645d + 2e06604 commit 73c1da0
Show file tree
Hide file tree
Showing 11 changed files with 238 additions and 38 deletions.
Binary file added houston/src/assets/emergency_button.webp
Binary file not shown.
2 changes: 1 addition & 1 deletion houston/src/components/MyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function MyModal({children, modalVisible, closeModal, type="default", loading=fa
<Typography id="modal-modal-title" variant="h6" component="h2" textAlign={"center"}>
{loading ? <div className="lds-dual-ring"></div> : null}
</Typography>
{children}
{loading ? null : children}
</Box>
</Modal>
)
Expand Down
58 changes: 58 additions & 0 deletions houston/src/components/TakeoffSelector.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.emergency_button_outer_div {
display: flex;
flex-direction: column;
justify-content: center;
width: auto;
border-radius: 5px;
align-items: center;
}

.emergency_button_submit_button {
background-color: initial;
background-image: linear-gradient(-180deg, #FF7E31, #E62C03);
border-radius: 6px;
box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px;
color: #FFFFFF;
cursor: pointer;
display: inline-block;
font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
height: 40px;
line-height: 40px;
outline: 0;
overflow: hidden;
padding: 0 20px;
pointer-events: auto;
position: relative;
text-align: center;
touch-action: manipulation;
user-select: none;
-webkit-user-select: none;
vertical-align: top;
white-space: nowrap;
width: 20%;
z-index: 9;
border: 0;
transition: box-shadow .2s;
margin-top: 5px;
}

.emergency_button_submit_button:hover {
box-shadow: rgba(253, 76, 0, 0.5) 0 3px 8px;
}

.emergency_button_takeoff_option {
background-color: gold;
width: 100%;
text-align: center;
font-size: 35px;
border-radius: 5px;
cursor: pointer;
box-shadow: rgba(207, 242, 11, 0.5) 0 3px 8px;
margin: 5px;
}

.emergency_button_takeoff_option.select {
background-color: rgba(255, 215, 0, 0.8);
transition: inset 0.2s ease;
box-shadow: inset rgba(0, 0, 0, 0.5) 0 3px 8px;
}
96 changes: 96 additions & 0 deletions houston/src/components/TakeoffSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { useState } from "react";
import MyModal from "./MyModal";
import { useMyModal } from "./UseMyModal";

import "./TakeoffSelector.css"


interface props{
modalVisible: boolean;
closeModal: ( ) => void;
}

/**
* A modal with two options. Manual Takeoff and Autonomous Takeoff. When selected,
* sends signal to obc in order to pick the TakeoffTick or ActiveTakeoffTick.
* @param props - The properties of TakeoffSelector
* @param props.modalVisible - A Boolean that dictates the visibility of the TakeoffSelector
* @param props.closeModal - A void function to close the PlanePicker.
* @returns A modal with two options.
*/
function TakeoffSelector({modalVisible, closeModal}:props){

const {modalVisible: fetchModalVisible, openModal:fetchOpenModal, closeModal:fetchCloseModal} = useMyModal();
const [selectedTakeoff, setSelectedTakeoff] = useState(0);
const [fetchStatus, setFetchStatus] = useState('');
const [fetchLog, setFetchLog] = useState('default');
const [isLoadingFetch, setIsLoadingFetch] = useState(true);

/**
* Helper function that sends a post request to obc
* to set the takeoff option and opens a modal to display
* the response.
* @param option - A string that specifies manual or autonomous.
*/
function submitTakeoffOption(option:string) {
setIsLoadingFetch(true);
fetchOpenModal();

fetch(`/api/takeoff/${option}`, {method: "POST"})
.then(async resp => {
if(resp.status == 200){
setFetchStatus("default")
setFetchLog(await resp.text());
setIsLoadingFetch(false);
}
else {
setFetchStatus("error")
setFetchLog(await resp.text());
setIsLoadingFetch(false);
}
})
.catch(err => {
console.error(err);
setFetchStatus("error")
setFetchLog(err);
setIsLoadingFetch(false);
})
}

return(
<MyModal modalVisible={modalVisible} closeModal={closeModal}>
<div className='emergency_button_outer_div'>
<div
onClick={() => setSelectedTakeoff(1)}
className={`emergency_button_takeoff_option ${selectedTakeoff === 1 ? 'select' : ''}`}>
Manual Takeoff
</div>
<div
onClick={() => setSelectedTakeoff(2)}
className= {`emergency_button_takeoff_option ${selectedTakeoff === 2 ? 'select' : ''}`}>
Autonomous Takeoff
</div>
<button
className='emergency_button_submit_button'
onClick={() => {
if(selectedTakeoff === 1) {
submitTakeoffOption("manual");
}
if(selectedTakeoff === 2){
submitTakeoffOption("autonomous");
}
}}
>
submit
</button>
</div>
<MyModal modalVisible={fetchModalVisible} closeModal={fetchCloseModal} type={fetchStatus} loading={isLoadingFetch}>
{fetchLog}
</MyModal>
</MyModal>
)

}

export default TakeoffSelector;

3 changes: 1 addition & 2 deletions houston/src/components/UpdateMapCenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ function UpdateMapCenter({position}:{position: [number, number]}){

useEffect(() => {
map.setView(position);
map.setZoom(16);
}, [position, map]);
}, [position]); // eslint-disable-line react-hooks/exhaustive-deps

return null;
}
Expand Down
61 changes: 30 additions & 31 deletions houston/src/pages/Control.css
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@
left: 50%;
top: 50%;
margin: -15px 0 0 -15px;
}
}

.controls-page .marker-pin::after {
.controls-page .marker-pin::after {
content: attr(data-content);
text-align: center;
font-family: 'Times New Roman', Times, serif;
Expand All @@ -186,20 +186,19 @@
position: absolute;
border-radius: 50%;
transform: rotate(45deg);

}
}

.controls-page .custom-div-icon i {
.controls-page .custom-div-icon i {
position: absolute;
width: 22px;
font-size: 22px;
left: 0;
right: 0;
margin: 10px auto;
text-align: center;
}
}

.checkbox-wrapper .control {
.checkbox-wrapper .control {
display: block;
position: relative;
padding-left: 30px;
Expand All @@ -209,55 +208,55 @@
background-color: #128edb;
border-radius: 6px;
width: 60px;
}
.checkbox-wrapper .control input {
}
.checkbox-wrapper .control input {
position: absolute;
z-index: -1;
opacity: 0;
}
.checkbox-wrapper .control__indicator {
}
.checkbox-wrapper .control__indicator {
position: absolute;
top: 4px;
left: 7px;
height: 15px;
width: 15px;
background: #fbf8f8;
border-radius: 5px;
}
.checkbox-wrapper .control:hover input ~ .control__indicator,
.checkbox-wrapper .control input:focus ~ .control__indicator {
}

.checkbox-wrapper .control:hover input ~ .control__indicator,
.checkbox-wrapper .control input:focus ~ .control__indicator {
background: #ccc;
}
.checkbox-wrapper .control input:checked ~ .control__indicator {
}
.checkbox-wrapper .control input:checked ~ .control__indicator {
background: #0bb14b;
}
.checkbox-wrapper .control:hover input:not([disabled]):checked ~ .control__indicator,
.checkbox-wrapper .control input:checked:focus ~ .control__indicator {
}
.checkbox-wrapper .control:hover input:not([disabled]):checked ~ .control__indicator,
.checkbox-wrapper .control input:checked:focus ~ .control__indicator {
background: #057217;
}
.checkbox-wrapper .control input:disabled ~ .control__indicator {
}
.checkbox-wrapper .control input:disabled ~ .control__indicator {
background: #e6e6e6;
opacity: 0.6;
pointer-events: none;
}
.checkbox-wrapper .control__indicator:after {
}
.checkbox-wrapper .control__indicator:after {
content: '';
position: absolute;
display: none;
}
.checkbox-wrapper .control input:checked ~ .control__indicator:after {
}
.checkbox-wrapper .control input:checked ~ .control__indicator:after {
display: block;
}
.checkbox-wrapper .control--checkbox .control__indicator:after {
}
.checkbox-wrapper .control--checkbox .control__indicator:after {
left: 5.6px;
top: 2px;
width: 3px;
height: 8px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.checkbox-wrapper .control--checkbox input:disabled ~ .control__indicator:after {
}
.checkbox-wrapper .control--checkbox input:disabled ~ .control__indicator:after {
border-color: #7b7b7b;
}

}
14 changes: 13 additions & 1 deletion houston/src/pages/Control.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect, Dispatch, SetStateAction } from 'react';
import TuasMap from '../components/TuasMap.tsx'
import duck from '../assets/duck.png';
import emergency_button from '../assets/emergency_button.webp'
import "./Control.css"
import { pullTelemetry } from '../utilities/pull_telemetry.ts';
import NOOOO from "../assets/noooo.gif"
Expand All @@ -12,6 +13,8 @@ import UpdateMapCenter from '../components/UpdateMapCenter.tsx';
import CustomControl from 'react-leaflet-custom-control'
import { Marker, Polygon, Polyline } from 'react-leaflet';
import L, { LatLng } from 'leaflet';
import { useMyModal } from '../components/UseMyModal.tsx';
import TakeoffSelector from '../components/TakeoffSelector.tsx';

type Unit = 'knots' | 'm/s' | 'feet' | 'meters' | 'V' | 'V/c' | '°F' | '°C' | '';
export type Threshold = [number, number, number, number];
Expand Down Expand Up @@ -275,6 +278,7 @@ function Control({settings, planeCoordinates}:{settings: SettingsConfig, planeCo
iconAnchor: [32, 25]
});
const [centerMap, setCenterMap] = useState(true);
const {modalVisible, openModal, closeModal} = useMyModal();

const [superSecret, setSuperSecret] = useState(false);

Expand Down Expand Up @@ -311,11 +315,15 @@ function Control({settings, planeCoordinates}:{settings: SettingsConfig, planeCo
data ? setIcon(data) : setIcon(duck);
};

const handleEmergencyButton = () => {
openModal();
}

useEffect(() => {
window.addEventListener("storage", () => {handleStorageChange()})
window.dispatchEvent(new Event("storage"))
return () => {window.removeEventListener("storage", () => {handleStorageChange()})}
});
}, []);

useEffect(() => {
pullFlightBounds(setFlightBound, setSearchBound, setWayPoint);
Expand All @@ -341,6 +349,10 @@ function Control({settings, planeCoordinates}:{settings: SettingsConfig, planeCo
</label>
</div>
</CustomControl>
<CustomControl prepend position='bottomright'>
<img src={emergency_button} alt='emergency button' width={'72px'} height={'72px'} style={{cursor: 'pointer'}} onClick={handleEmergencyButton}/>
<TakeoffSelector modalVisible={modalVisible} closeModal={closeModal}/>
</CustomControl>
{centerMap ? <UpdateMapCenter position={planeLatLng}/> : null}
<Marker position={planeLatLng} icon={markerIcon}/>
<Polyline color='lime' positions={planeCoordinates}/>
Expand Down
4 changes: 2 additions & 2 deletions houston/src/pages/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function Layout({statuses}:{statuses:ConnectionStatus[]}) {
window.addEventListener("storage", () => {handleStorageChange()})
window.dispatchEvent(new Event("storage"))
return () => {window.removeEventListener("storage", () => {handleStorageChange()})}
});
}, []);

const handleInflux = () => {
setLoading(true);
Expand Down Expand Up @@ -97,7 +97,7 @@ function Layout({statuses}:{statuses:ConnectionStatus[]}) {
</Button>
<MyModal modalVisible={modalVisible} closeModal={closeModal} loading={influxLoading}>
<Typography id="modal-modal-title" variant="h6" component="h2" textAlign={"center"}>
{influxLoading ? null : influxReturnValue}
{influxReturnValue}
</Typography>
</MyModal>
{/* If another page is added, need to adjust the nth child rule in the css
Expand Down
16 changes: 16 additions & 0 deletions internal/obc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ func NewClient(urlBase string, timeout int) *Client {
return client
}

/*
Send a request to the obc to set the status of WaitForTakeoffTick to be autonomous.
*/
func (client *Client) DoAutonomousTakeoff() ([]byte, int) {
body, httpErr := client.httpClient.Post("/takeoff/autonomous", nil)
return body, httpErr.Status
}

/*
Send a request to the obc to set the status of WaitForTakeoffTick to be manual.
*/
func (client *Client) DoManualTakeoff() ([]byte, int) {
body, httpErr := client.httpClient.Post("/takeoff/manual", nil)
return body, httpErr.Status
}

/*
Requests the obc connection info from the OBC via GET request
Expand Down
Loading

0 comments on commit 73c1da0

Please sign in to comment.