Skip to content

Commit

Permalink
Merge pull request #132 from tritonuas/feat/targetMatch
Browse files Browse the repository at this point in the history
Manual Target Match
  • Loading branch information
Tyler-Lentz authored Jun 12, 2024
2 parents 8fdf0a1 + 23e8eed commit 798e1ee
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 3 deletions.
4 changes: 3 additions & 1 deletion houston/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import Layout from './pages/Layout';
import Report from './pages/Report';
import NoPage from './pages/NoPage';
import Settings from './pages/Settings'

import TargetMatch from "./pages/TargetMatch";

import { ConnectionType, ConnectionStatus } from "./utilities/temp";
import { SettingsConfig, loadSettings } from "./utilities/settings";
import { LatLng } from "leaflet";
import Drop from "./pages/Drop";


/**
* Main React function
* @returns App
Expand Down Expand Up @@ -106,6 +107,7 @@ function App() {
<Route path="report" element={<Report />} />
<Route path="settings" element={<Settings settings={config} setSettings={setConfig}/>} />
<Route path="drop" element={<Drop/>}/>
<Route path="targetmatch" element={<TargetMatch/>} />

<Route path="*" element={<NoPage />} />
</Route>
Expand Down
2 changes: 1 addition & 1 deletion houston/src/pages/Layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ li:has(a.active) {
transition: background-color var(--std-transition);
}

nav.topbar li:nth-child(6) {
nav.topbar li:nth-child(7) {
margin-right: auto;
}

Expand Down
3 changes: 3 additions & 0 deletions houston/src/pages/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ function Layout({statuses}:{statuses:ConnectionStatus[]}) {
<li>
<NavLink to="/drop" className={checkForActive}>Drop</NavLink>
</li>
<li>
<NavLink to="/targetmatch" className={checkForActive}>Target</NavLink>
</li>
<Button onClick={handleBottleConnectionStatusModal}>
<img
src={bottleIcon}
Expand Down
77 changes: 77 additions & 0 deletions houston/src/pages/TargetMatch.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.flex-box {
display: flex;
flex:19;
justify-content: center;
justify-items: center;
align-content: center;
align-items: center;
width: 100%;
height: 100%;
}

.form-container {
background-color: goldenrod;
border-radius: 20px;
padding: 20px;
text-align: center;
}

.bottle {
display: flex;
align-items: center;
gap: 30px;
}

.input-field {
margin: 30px 0;
font-size: 60px;
padding: 10px;
width: 350px;
border: 1px solid rgb(253, 76, 0, 0.5);
border-top: none;
border-left: none;
border-right: none;
background: rgba(20, 20, 20, .2);
color: white;
outline: none;
caret-color: black;
}

.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;
font-size: x-large;
height: 60px;
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: 25%;
z-index: 9;
border: 0;
transition: box-shadow .2s;
margin-top: 5px;
}

.submit-button:hover {
box-shadow: rgba(253, 76, 0, 0.5) 0 3px 25px;
}

.image-gallery {
width: 90%;
height: auto;
}
168 changes: 168 additions & 0 deletions houston/src/pages/TargetMatch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import "./TargetMatch.css"

import { useState } from "react";
import { AirdropTarget, GPSCoord} from '../protos/obc.pb';
import { useMyModal } from "../components/UseMyModal";

import MyModal from "../components/MyModal";

/**
* @returns Returns manual target matching page.
*/
function TargetMatch() {

const {modalVisible, openModal, closeModal} = useMyModal();
const [modalType, setModalType] = useState('default');
const [modalMessage, setModalMessage] = useState('');
const [modalLoading, setModalLoading] = useState(true);
/**
* The bottles that exist.
*/
const bottle_list = ['A', 'B', 'C', 'D', 'E'];

/**
* Define the initial state for lat_lng
*/
const lat_lng_template = [
{ lat: '', lng: '', setLat: (value: number|string) => handleUpdate(0, 'lat', value), setLng: (value: number|string) => handleUpdate(0, 'lng', value) },
{ lat: '', lng: '', setLat: (value: number|string) => handleUpdate(1, 'lat', value), setLng: (value: number|string) => handleUpdate(1, 'lng', value) },
{ lat: '', lng: '', setLat: (value: number|string) => handleUpdate(2, 'lat', value), setLng: (value: number|string) => handleUpdate(2, 'lng', value) },
{ lat: '', lng: '', setLat: (value: number|string) => handleUpdate(3, 'lat', value), setLng: (value: number|string) => handleUpdate(3, 'lng', value) },
{ lat: '', lng: '', setLat: (value: number|string) => handleUpdate(4, 'lat', value), setLng: (value: number|string) => handleUpdate(4, 'lng', value) }
];

/**
* A state variable that is an array where each element is a json object that
* contains the latitude, longitude, latitude setter, and longitude setter.
* It has 5 json because there are five bottles.
*/
const [lat_lng, set_lat_lng] = useState(lat_lng_template);

/**
* This function handels the set_lat_lng state setter. Since lat_lng
* is an array, to change a specific value, we use this function to index
* into it.
* @param index The index that help you grab the specific json.
* @param key This either 'lat' or 'lng'.
* @param value The number representing the latitude or longitude.
*/
const handleUpdate = (index:number, key:string, value:number|string) => {
set_lat_lng(pre_lat_lng => {
const new_lat_lng = [...pre_lat_lng];
new_lat_lng[index] = { ...new_lat_lng[index], [key]: value };
return new_lat_lng;
});
};


/**
* This functions puts all the values in the lat_lng variable into an array
* consisting of the struct AirdropTarget from obc.protos.
* @param event A form tag event.
*
* [
* {
* Index: BottleDropIndex,
* Coordiante: GPSCoord,
* },
* ...
* ]
*/
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setModalLoading(true);
const airdrop_target_list: AirdropTarget[] = [];
bottle_list.forEach((_, index: number) => {
if(lat_lng[index].lat != '' && lat_lng[index].lng != '') {
const coordiante: GPSCoord = {
Latitude: parseInt(lat_lng[index].lat),
Longitude: parseInt(lat_lng[index].lng),
Altitude: 0,
}
const airdrop_target: AirdropTarget = {
Index: index+1,
Coordinate: coordiante,
}
airdrop_target_list.push(airdrop_target);
}

});
set_lat_lng(lat_lng_template);

if(airdrop_target_list.length > 0) {
fetch("/api/airdrop", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(airdrop_target_list)
})
.then(response => {
if (response.status == 200) {
return response.text()
} else {
throw response.text();
}
})
.then(data => {
setModalType('default');
setModalMessage(data)
})
.catch(err => {
console.log("ERROR: " + err);
setModalType('error');
setModalMessage(err.toString());
})
} else {
setModalType('warning');
setModalMessage('Form is empty or imcomplete');
}
setModalLoading(false);
};


return (
<div className="flex-box">
<div className="form-container">
<form onSubmit={(e) => handleSubmit(e)} >
{
bottle_list.map((bottle, index) => {
return (
<div className="bottle" key={index}>
<div style={{fontSize:'50px', fontWeight:'bold'}}>Bottle {bottle}</div>
<input className="input-field" type="number" placeholder="lat" value={lat_lng[index].lat} onChange={(e) => {
const value = e.target.value;
if (value.trim() === '') {
lat_lng[index].setLat('');
} else {
const numericValue = parseInt(value);
if (!isNaN(numericValue)) {
lat_lng[index].setLat(numericValue);
}
}
}} />
<input className="input-field" type="number" placeholder="lng" value={lat_lng[index].lng} onChange={(e) => {
const value = e.target.value;
if (value.trim() === '') {
lat_lng[index].setLng('');
} else {
const numericValue = parseInt(value);
if (!isNaN(numericValue)) {
lat_lng[index].setLng(numericValue);
}
}
}} />
</div>
)
})
}
<input className="submit-button" type="submit" value="Submit" onClick={openModal}></input>
</form>
</div>
<MyModal modalVisible={modalVisible} closeModal={closeModal} type={modalType} loading={modalLoading}>
{modalMessage}
</MyModal>
</div>
)
}
export default TargetMatch;
9 changes: 8 additions & 1 deletion internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,14 @@ func (server *Server) postMission() gin.HandlerFunc {

func (server *Server) postAirdropTargets() gin.HandlerFunc {
return func(c *gin.Context) {
c.String(http.StatusNotImplemented, "Not Implemented")
airdropTarget := []protos.AirdropTarget{}
err := c.BindJSON(&airdropTarget)
if err != nil {
c.String(http.StatusBadRequest, err.Error())
}

respBody, status := server.obcClient.PostAirdropTargets(&airdropTarget)
c.String(status, "text/plain", respBody)
}
}

Expand Down

0 comments on commit 798e1ee

Please sign in to comment.