From d4aab440b5a6c4e1b2b2baa0aabc737fc122ffbb Mon Sep 17 00:00:00 2001 From: klemie Date: Thu, 12 Oct 2023 17:27:28 -0700 Subject: [PATCH 1/4] dev guide initial draft --- client/src/components/Components.d.ts | 61 +++++++ client/src/components/DataUpload.tsx | 73 +++++++++ .../assets/entity-design-pattern.png | Bin 0 -> 23795 bytes documentation/developer-guide.md | 154 +++++++++++++++++- 4 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 client/src/components/Components.d.ts create mode 100644 client/src/components/DataUpload.tsx create mode 100644 documentation/assets/entity-design-pattern.png diff --git a/client/src/components/Components.d.ts b/client/src/components/Components.d.ts new file mode 100644 index 000000000..a2fe9e389 --- /dev/null +++ b/client/src/components/Components.d.ts @@ -0,0 +1,61 @@ +import * as React from 'react'; +import * as Entities from '../utils/entities'; +import { ComponentCard } from './ComponentCard'; +import { DataUpload } from './DataUpload'; + +interface TypeMap { + props: {}; + Component: React.FC; +}; + +/** + * ComponentCard + */ + +interface IComponentCardProps { + /** + * The id of the component. + */ + componentId: string; + /** + * The id of the rocket or the rocket object. + */ + rocket: Entities.IRocketPopulated | Entities.IRocketPopulated; + /** + * event handler for when the component delete button is clicked. + */ + onDelete: () => void; + /** + * event handler for when the component edit button is clicked. + */ + updateComponent: () => void; + /** + * event handler for when the data config button is clicked. + */ + onDataConfigClick: (id: string) => void; +} + +export type ComponentCardTypeMap = TypeMap & { + props: IComponentCardProps; + Component: ComponentCard; +}; + +/** + * DataUpload + */ + +interface IDataUploadProps { + /** + * Controls whether the dialog is open or not. + */ + isOpen: boolean; + /** + * event handler for when the dialog is closed. + */ + onClose: () => void; +} + +export type DataUploadTypeMap = TypeMap & { + props: IDataUploadProps; + Component: DataUpload; +}; \ No newline at end of file diff --git a/client/src/components/DataUpload.tsx b/client/src/components/DataUpload.tsx new file mode 100644 index 000000000..6d0828522 --- /dev/null +++ b/client/src/components/DataUpload.tsx @@ -0,0 +1,73 @@ +import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Slide, Stack, Tooltip, Typography } from "@mui/material"; +import { useActiveMission } from "../utils/ActiveMissionContext"; +import React, { useState, forwardRef, useEffect } from 'react'; +import { TransitionProps } from "@mui/material/transitions"; +import FileUpload from "react-material-file-upload"; + + + +interface IDataUploadProps { + isOpen: boolean; + onClose: () => void; +} + + +const DataUpload: React.FC = (props: IDataUploadProps) => { + const { isOpen, onClose } = props; + const [files, setFiles] = useState([]); + + const handleSave = () => { + // TODO: mateos rust library + props.onClose(); + }; + + const handleClose = () => { + props.onClose(); + }; + + useEffect(() => { + console.log(files); + }, [files]); + + const context = useActiveMission(); + + return( + + + Upload Flight Data + + + + Upload two files: config.json and data.bin or data.cvs. + + Supported File Type: + + + + + + + +
+ +
+
+ +
+
+ + + + +
+ ); +} + +export default DataUpload; \ No newline at end of file diff --git a/documentation/assets/entity-design-pattern.png b/documentation/assets/entity-design-pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..5472adbca745103d94ddf22c62dbfe92c0d59a7c GIT binary patch literal 23795 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 literal 0 HcmV?d00001 diff --git a/documentation/developer-guide.md b/documentation/developer-guide.md index 8ab28e5f6..3d459bf77 100644 --- a/documentation/developer-guide.md +++ b/documentation/developer-guide.md @@ -86,17 +86,167 @@ const Component: React.FC = (props: IProps) => { export default Component; ``` There should be an accompanying documentation file in the `documentation/components` folder with the same name as the component. This file should contain a description of the component and how to use it. Use this template: [Example Component Document](./components/exampleComponentDocumentation.md). + +Once your component is finished add it to the `index.d.ts` file in the `components` folder. This will allow you to import the component from the `components` folder without having to specify the file name. For example: + +```tsx +import { ExampleComponent, IExampleProps } from '../components'; +``` +rather than +```tsx +import { ExampleComponent } from '../components/exampleComponent'; +``` + + ### How to Make a View -### How to access the API +Create a new file in the `views` folder with the name of the view in kebab-case. The file is named in `kebab-case` and the extension should be `.tsx` as we are using TypeScript. Use the following template to create a view: +```tsx +import React, { useEffect } from 'react'; +import { Grid } from '@material-ui/core'; +import { Header } from '../../components'; +interface IProps { + // props +} + +const View: React.FC = (props: IProps) => { + const { } = props; + // state + const [data, setData] = useState(null); + // or + const useSocketContext = () => useContext(Context); + + // render + return ( + + + +

+ + + // View content goes here + + + ); +}; +``` + +### How to access the API Frontend + +The helper file api.ts is give typesafety and security to ground supports api from the frontend. The file is located in `src/services/api.ts`. To learn more about the API please see the [API Documentation](./api.md). + +```tsx +// Import the entity + verb you specifically want to access +import { ... } from "../utils/api.ts"; +// If you GETTing a value from the api you also most likely need the populated version of the entity +import { ...Populated } from "../utils/api.ts"; +``` + +`Example` +getting a rocket from the API updating a value in the rocket and then sending it back to the API. + +```tsx +import { IRocketPopulated } from "../utils/"; +import { getRocket, updateRocket } from "../services/api.ts"; +import { useState, useEffect } from "react"; + +const [rocketData, setRocketData] = useState(null); + +async retrieveRocket = () => { + const rocketResponse = await getRocket(1); + const rocketData = rocketResponse.data as IRocketPopulated; + return rocketData; +} + +// Retrieve the rocket data called when the component is mounted +useEffect(() => { + const rocketData = await retrieveRocket(); + setRocketData(rocketData); +}, []); + +// Update the rocket data +setRocketData({ + ...rocketData, + name: "New Rocket Name" +}); + +const success = await updateRocket(1, rocketData).error.status === 200; +``` + + +### How to access context + +We have two contexts that are currently being used. The first is the `SocketContext` which is used to access the socket. The second is the `activeMissionContext` which is used to access the rocket and mission data of an active flight. To access the context use the following template: + +```tsx + +``` +### How to access the Socket ## Server Backend +For the server there are three main things that need to be created. The first is an **entity** which is the object that is stored in the database. The second is a **controller** which is the logic that is used to interact with the entity. The third is a **route** which is the endpoint that is used to access the controller. + +

+ +

+ ### How to create an Entity ```tsx +import mongoose, { Document, Schema, Types } from "mongoose"; + +export interface IEntity { + // entity properties + Name: string; +}; + +export interface IEntityModel extends IEntity, Document { }; + +export const EntitySchema = new Schema( + { + // entity properties + Name: { + type: String, + required: true, + validator: (value: string) => { + // validation logic + } + }, + }, + { + versionKey: false, + timestamps: true + } +); + +export const Entity = mongoose.model("Entity", EntitySchema); +``` + +### How to create a Controller +For basic CRUD operations we will just be using the Generic controller. This controller is located in `src/controllers/Generic.ts`. +### How to create a Route +```tsx +import express from "express"; +import controller from "../controllers/Generic"; +import Entity from "../models/Entity"; + +const router = express.Router(); + +router.get("/", controller.getAll(Entity)); +router.get("/:id", controller.getById(Entity)); +router.post("/", controller.create(Entity)); +router.patch("/:id", controller.update(Entity)); +router.delete("/:id", controller.remove(Entity)); +export default router; ``` -## Telemetry Backend \ No newline at end of file +## Telemetry Backend From 6d264b690ec928b014ba94e9c54648a0eb5d2f66 Mon Sep 17 00:00:00 2001 From: klemie Date: Thu, 12 Oct 2023 17:35:55 -0700 Subject: [PATCH 2/4] intro presentation link --- documentation/developer-guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/developer-guide.md b/documentation/developer-guide.md index 3d459bf77..e17d29e31 100644 --- a/documentation/developer-guide.md +++ b/documentation/developer-guide.md @@ -6,6 +6,7 @@ Please see the [installation guide](installation.md#developer-installation) for ## Resources - [Git Tutorial](https://www.figma.com/proto/LAxam3HVit5yoHmCxu2xa2/Git?page-id=0%3A1&type=design&node-id=1-796&viewport=554%2C653%2C0.24&t=YB0SXZsT8TWyvT93-1&scaling=min-zoom&starting-point-node-id=1%3A19&mode=design) - UVic Rocketry Git reference guide - [Software Process](https://docs.google.com/presentation/d/1gkJjfWnc6jsr0PQ29cYPVdIOYFZum4SubFt4X8ovL-o/edit#slide=id.g146cc2337ed_0_4) - UVic Rocketry Software Process guidelines +- [Ground Support](https://www.figma.com/proto/FmNk8xeKIUDAEmA377DV3l/Rocketry-Software?page-id=0%3A1&type=design&node-id=2085-5509&viewport=-1610%2C-611%2C0.11&t=cZkbvCD7xCJrTlDc-1&scaling=min-zoom&starting-point-node-id=2085%3A5509&show-proto-sidebar=1&mode=design) - Presentation on high level overview ## Standards ### Code Style From d41c6a7d9ef0ff7ce4e273952597f70255148892 Mon Sep 17 00:00:00 2001 From: klemie Date: Fri, 20 Oct 2023 14:46:09 -0700 Subject: [PATCH 3/4] skill dev --- client/src/components/DataUpload.tsx | 2 +- client/src/components/RocketSimDailog.tsx | 121 +++++++++++++ client/src/services/api.ts | 166 +++++++++++++++++- client/src/utils/entities.ts | 49 +++++- services/server/src/models/RocketSimModel.ts | 175 +++++++++++++++++++ services/server/src/routes/RocketSimRoute.ts | 22 +++ services/server/src/server.ts | 2 + 7 files changed, 532 insertions(+), 5 deletions(-) create mode 100644 client/src/components/RocketSimDailog.tsx create mode 100644 services/server/src/models/RocketSimModel.ts create mode 100644 services/server/src/routes/RocketSimRoute.ts diff --git a/client/src/components/DataUpload.tsx b/client/src/components/DataUpload.tsx index a85b4afbb..3a956df1c 100644 --- a/client/src/components/DataUpload.tsx +++ b/client/src/components/DataUpload.tsx @@ -1,6 +1,6 @@ +import React, { useState, forwardRef, useEffect } from 'react'; import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Slide, Stack, Tooltip, Typography } from "@mui/material"; import { useActiveMission } from "../utils/ActiveMissionContext"; -import React, { useState, forwardRef, useEffect } from 'react'; import { TransitionProps } from "@mui/material/transitions"; import FileUpload from "react-material-file-upload"; diff --git a/client/src/components/RocketSimDailog.tsx b/client/src/components/RocketSimDailog.tsx new file mode 100644 index 000000000..6e32962fb --- /dev/null +++ b/client/src/components/RocketSimDailog.tsx @@ -0,0 +1,121 @@ +import React, { useState, useEffect } from 'react'; +import { IRocketSimModel } from '../utils/entities'; +import api from '../services/api'; +import { Dialog, DialogContent, DialogTitle, Stack, TextField, Typography } from '@mui/material'; +import { LoadingButton } from '@mui/lab'; + +interface IRocketSimModalProps { + isOpen: boolean; + onClose: () => void; + id?: string; +} + +const RocketSimDialog: React.FC = (props: IRocketSimModalProps) => { + const { isOpen, onClose, id } = props; + + const [loading, setLoading] = useState(false); + // RocketSimModel Properties + const [dryMass, setDryMass] = useState(null); + const [wetMass, setWetMass] = useState(null); + const [name, setName] = useState(null); + const [centerOfGravity, setCenterOfGravity] = useState(null); + const [centerOfPressure, setCenterOfPressure] = useState(null); + const [rocketLength, setRocketLength] = useState(null); + const [rocketDiameter, setRocketDiameter] = useState(null); + const [fuselageDiameter, setFuselageDiameter] = useState(null); + const [fuselageLength, setFuselageLength] = useState(null); + + const rocketSimProperties = [ + { label: "Name", value: name, setter: setName }, + { label: "Dry Mass", value: dryMass, setter: setDryMass }, + { label: "Wet Mass", value: wetMass, setter: setWetMass }, + { label: "Center of Gravity", value: centerOfGravity, setter: setCenterOfGravity }, + { label: "Center of Pressure", value: centerOfPressure, setter: setCenterOfPressure }, + { label: "Rocket Length", value: rocketLength, setter: setRocketLength }, + { label: "Rocket Diameter", value: rocketDiameter, setter: setRocketDiameter }, + { label: "Fuselage Diameter", value: fuselageDiameter, setter: setFuselageDiameter }, + { label: "Fuselage Length", value: fuselageLength, setter: setFuselageLength }, + ]; + + const handleSave = async () => { + const payload: IRocketSimModel = { + Name: name!, + DryMass: dryMass!, + WetMass: wetMass!, + CenterOfGravity: centerOfGravity!, + CenterOfPressure: centerOfPressure!, + RocketLength: rocketLength!, + RocketDiameter: rocketDiameter!, + FuselageDiameter: fuselageDiameter!, + FuselageLength: fuselageLength!, + }; + + let success: boolean = false; + setLoading(!success); + if (id) { + // update rocketSim + success = (await api.updateRocketSim(id, payload)).error.error; + } else { + // create rocketSim + success = (await api.createRocketSim(payload)).error.error; + } + setLoading(!success); + + onClose(); + + }; + + const handleClose = async () => { + await handleSave(); + onClose(); + }; + + useEffect(() => { + async function fetchRocketSim() { + if (id) { + // fetch rocketSim + const response = await api.getRocketSim(id); + const rocketSim = response.data as IRocketSimModel; + + setName(rocketSim.Name); + setDryMass(rocketSim.DryMass); + setWetMass(rocketSim.WetMass); + setCenterOfGravity(rocketSim.CenterOfGravity); + setCenterOfPressure(rocketSim.CenterOfPressure); + setRocketLength(rocketSim.RocketLength); + setRocketDiameter(rocketSim.RocketDiameter); + setFuselageDiameter(rocketSim.FuselageDiameter); + setFuselageLength(rocketSim.FuselageLength); + } + } + fetchRocketSim(); + }, [id]); + + + + return ( + + + Rocket Simulation + + + + {rocketSimProperties.map((property, index) => ( + property.setter(Number(e.target.value))} + variant="outlined" + type="number" + /> + )) + + } + + + + ); +} + +export default RocketSimDialog; \ No newline at end of file diff --git a/client/src/services/api.ts b/client/src/services/api.ts index d088b44a3..96ea398df 100644 --- a/client/src/services/api.ts +++ b/client/src/services/api.ts @@ -6,7 +6,8 @@ import { IRocket, IComponentPopulated, IComponent, - IMission + IMission, + IRocketSimModel } from '../utils/entities'; const api = axios.create({ @@ -34,7 +35,9 @@ interface IApiResponse { IMission | IMission[] | IMissionPopulated[] | - IMissionPopulated; + IMissionPopulated | + IRocketSimModel | + IRocketSimModel[]; error: IError; } @@ -757,6 +760,158 @@ async function deleteDataConfig(id: string, componentId: string): Promise { + let response: AxiosResponse; + let data: IApiResponse = { + data: [] as IRocketPopulated[], + error: {} as IError + } as IApiResponse; + + try { + response = await api.get('/rocketSim'); + data = { + data: response.data.results + ? response.data.results + : response.data.result as IRocketPopulated[], + error: handleError(response) + }; + } catch (e) { + const err = e as AxiosError; + data.error.error = true; + data.error.message = `Error getting all rockets. Full error: \n${err.message}`; + } + + return data; +} + +/** + * @param id Id of a rocketSim + * @returns The rocketSim + */ +async function getRocketSim(id: string): Promise { + let response: AxiosResponse; + let data: IApiResponse = { + data: {} as IRocketSimModel, + error: {} as IError + } as IApiResponse; + + try { + response = await api.get(`/rocketSim/${id}`); + data = { + data: response.data.result + ? response.data.result as IRocketSimModel + : response.data.results as IRocketSimModel, + error: handleError(response) + }; + } catch (e) { + const err = e as AxiosError; + data.error.error = true; + data.error.message = `Error getting rocketSim by id with ${id}. Full error: \n${err.message}`; + } + + return data; +} + +/** + * @param payload The rocket to create type IRocketSimModel + * @returns The created rocketSim + * + * @description Creates a rocketSim + */ +async function createRocketSim(payload: IRocketSimModel): Promise { + let response: AxiosResponse; + let data: IApiResponse = { + data: {} as IRocket, + error: {} as IError + } as IApiResponse; + + try { + response = await api.post('/rocketSim', payload); + data = { + data: response.data.result + ? response.data.result + : response.data.results as IRocketSimModel, + error: handleError(response) + }; + } catch (e) { + const err = e as AxiosError; + data.error.error = true; + data.error.message = `Error creating new rocketSim. Full error:\n${err.message}`; + } + + return data; +} + +/** + * @param id Id of a rocket + * @param payload The rocket to update type IRocket + * @returns The updated rocket + */ +async function updateRocketSim(id: string, payload: IRocketSimModel): Promise { + let response: AxiosResponse; + let data: IApiResponse = { + data: {} as IRocketSimModel, + error: {} as IError + } as IApiResponse; + + try { + response = await api.patch(`/rocketSim/${id}`, payload); + data = { + data: response.data.result + ? response.data.result as IRocketSimModel + : response.data.results as IRocketSimModel, + error: handleError(response) + }; + } catch(e) { + const err = e as AxiosError; + data.error.error = true; + data.error.message = `Error updating the rocketSim with the id ${id}. Full error:\n${err.message}`; + } + + return data; +} +/** + * @param id Id of a rocket + * @returns The deleted rocket + * + * @description Deletes a rocket + */ +async function deleteRocketSim(id: string): Promise { + let response: AxiosResponse; + let data: IApiResponse = { + data: {} as IRocketSimModel, + error: {} as IError + } as IApiResponse; + + try { + response = await api.delete(`/rocketSim/${id}`); + data = { + data: response.data.result + ? response.data.result + : response.data.results as IRocketSimModel, + error: handleError(response) + }; + } catch(e) { + const err = e as AxiosError; + data.error.error = true; + data.error.message = `Error deleting rocketSim with the id ${id}. Full error:\n${err.message}`; + } + + return data; +} + // eslint-disable-next-line import/no-anonymous-default-export export default { getRockets, @@ -778,5 +933,10 @@ export default { getDataConfig, createDataConfig, updateDataConfig, - deleteDataConfig + deleteDataConfig, + getRocketSims, + getRocketSim, + createRocketSim, + updateRocketSim, + deleteRocketSim }; \ No newline at end of file diff --git a/client/src/utils/entities.ts b/client/src/utils/entities.ts index 859d71843..7b6bf579c 100644 --- a/client/src/utils/entities.ts +++ b/client/src/utils/entities.ts @@ -112,4 +112,51 @@ export interface IFieldData { ParentFieldGroupName: string; TelemetryId: Buffer; Data: IDataPoint[]; -} \ No newline at end of file +} + +/* ------- Skilldev ------ */ + +enum NoseConeShape { + Elliptical = "Elliptical", + Conical = "Conical", + Parabolic = "Parabolic", + PowerSeries = "PowerSeries" +}; + +interface IAeroDimensions { + NoseCone: { + Length: number; + Diameter: number; + TipRadius: number; + Shape: NoseConeShape; + }, + BoatTail: { + Length: number; + ForeDiameter: number; + AftDiameter: number; + }, + Fins: { + Count: number; + Span: number; + RootChord: number; + TipChord: number; + Sweep: number; + Thickness: number; + } +} + +export interface IRocketSimModel { + _id?: string; + Name: string; + DryMass: number; + WetMass: number; + RocketDiameter: number; + CenterOfGravity: number; + CenterOfPressure: number; + RocketLength: number; + FuselageLength: number; + FuselageDiameter: number; + AeroDimensions?: IAeroDimensions; +}; + +/* ------- Skilldev ------ */ \ No newline at end of file diff --git a/services/server/src/models/RocketSimModel.ts b/services/server/src/models/RocketSimModel.ts new file mode 100644 index 000000000..e4af37fb6 --- /dev/null +++ b/services/server/src/models/RocketSimModel.ts @@ -0,0 +1,175 @@ +import mongoose, { Document, Schema, Types } from "mongoose"; + +export interface IRocketSimModel { + Name: string; + DryMass: number; + WetMass: number; + CenterOfGravity: number; + CenterOfPressure: number; + RocketLength: number; + RocketDiameter: number; + FuselageLength: number; + FuselageDiameter: number; + AeroDimensions?: IAeroDimensions; +}; + +enum NoseConeShape { + Elliptical = "Elliptical", + Conical = "Conical", + Parabolic = "Parabolic", + PowerSeries = "PowerSeries" +}; + +interface IAeroDimensions { + NoseCone: { + Length: number; + Diameter: number; + TipRadius: number; + Shape: NoseConeShape; + }, + BoatTail: { + Length: number; + ForeDiameter: number; + AftDiameter: number; + }, + Fins: { + Count: number; + Span: number; + RootChord: number; + TipChord: number; + Sweep: number; + Thickness: number; + } +} + +const AeroDimensions: Schema = new Schema( + { + NoseCone: { + Length: { + type: Number, + required: true + }, + Diameter: { + type: Number, + required: true + }, + TipRadius: { + type: Number, + required: true + }, + Shape: { + type: String, + enum: Object.values(NoseConeShape), + required: true + } + }, + BoatTail: { + Length: { + type: Number, + required: true + }, + ForeDiameter: { + type: Number, + required: true + }, + AftDiameter: { + type: Number, + required: true + } + }, + Fins: { + Count: { + type: Number, + required: true + }, + Span: { + type: Number, + required: true + }, + RootChord: { + type: Number, + required: true + }, + TipChord: { + type: Number, + required: true + }, + Sweep: { + type: Number, + required: true + }, + Thickness: { + type: Number, + required: true + } + } + }, + { + versionKey: false, + timestamps: true + } +); + +export interface IRocketSimModelModel extends IRocketSimModel, Document { }; + +export const RocketSimModelSchema = new Schema( + { + Name: { + type: String, + required: true + }, + DryMass: { + type: Number, + required: true, + validator: (mass: number) => { + return mass > 0 && !isNaN(mass); + } + }, + WetMass: { + type: Number, + required: true, + validator: (mass: number) => { + return mass > 0 && !isNaN(mass); + } + }, + FuselageLength: { + type: Number, + required: true, + validator: (length: number) => { + return length > 0 && !isNaN(length); + } + }, + FuselageDiameter: { + type: Number, + required: true, + validator: (diameter: number) => { + return diameter > 0 && !isNaN(diameter); + } + }, + RocketLength: { + type: Number, + required: true, + validator: (length: number) => { + return length > 0 && !isNaN(length); + } + }, + CenterOfGravity: { + type: Number, + required: true + }, + CenterOfPressure: { + type: Number, + required: true + }, + AeroDimensions: { + type: AeroDimensions, + required: false + } + }, + { + versionKey: false, + timestamps: true + } +); + +export const RocketSimModel = mongoose.model("RocketSimModel", RocketSimModelSchema); \ No newline at end of file diff --git a/services/server/src/routes/RocketSimRoute.ts b/services/server/src/routes/RocketSimRoute.ts new file mode 100644 index 000000000..cb5fe5e19 --- /dev/null +++ b/services/server/src/routes/RocketSimRoute.ts @@ -0,0 +1,22 @@ +import express from 'express'; +import controller from '../controllers/Generic'; +import { RocketSimModel } from '../models/RocketSimModel'; + +const router = express.Router(); + +// GET all data config +router.get('/', controller.getAll(RocketSimModel)); + +// GET single data config +router.get('/:id', controller.get(RocketSimModel)); + +// POST new data config +router.post('/', controller.create(RocketSimModel)); + +// UPDATE a data config +router.patch('/:id', controller.update(RocketSimModel)); + +// DELETE a data config +router.delete('/:id', controller.deleteOne(RocketSimModel)); + +export = router; \ No newline at end of file diff --git a/services/server/src/server.ts b/services/server/src/server.ts index 33282953c..a85ca27d8 100644 --- a/services/server/src/server.ts +++ b/services/server/src/server.ts @@ -8,6 +8,7 @@ import RocketRoute from './routes/RocketRoute'; import ComponentRoute from './routes/ComponentRoute'; import MissionRoute from './routes/MissionRoute'; import DataConfigRoute from './routes/DataConfigRoute'; +import RocketSimRoute from './routes/RocketSimRoute'; const router = express(); @@ -58,6 +59,7 @@ const StartServer = () => { router.use('/component', ComponentRoute); router.use('/dataConfig', DataConfigRoute); router.use('/mission', MissionRoute); + router.use('/rocketSim', RocketSimRoute); /** Healthcheck */ router.get('/ping', (req, res, next) => res.status(200).json({ ping: 'pong' })); From 8af83fee497c1720587e4ede5e4ca3843ccddfc1 Mon Sep 17 00:00:00 2001 From: klemie Date: Sun, 22 Oct 2023 15:05:58 -0700 Subject: [PATCH 4/4] finish up skilldev --- client/src/components/RocketSimDailog.tsx | 94 ++++++++++++-------- client/src/utils/entities.ts | 1 - client/src/views/rocket-details-view.tsx | 20 +++++ client/src/views/tabs/simulation-tab.tsx | 33 +++++++ services/server/src/models/RocketSimModel.ts | 1 - 5 files changed, 112 insertions(+), 37 deletions(-) create mode 100644 client/src/views/tabs/simulation-tab.tsx diff --git a/client/src/components/RocketSimDailog.tsx b/client/src/components/RocketSimDailog.tsx index 6e32962fb..f45cc55dc 100644 --- a/client/src/components/RocketSimDailog.tsx +++ b/client/src/components/RocketSimDailog.tsx @@ -1,8 +1,8 @@ import React, { useState, useEffect } from 'react'; import { IRocketSimModel } from '../utils/entities'; import api from '../services/api'; -import { Dialog, DialogContent, DialogTitle, Stack, TextField, Typography } from '@mui/material'; -import { LoadingButton } from '@mui/lab'; +import { Alert, Button, Collapse, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, InputAdornment, Stack, TextField, Typography } from '@mui/material'; +import { Close } from '@mui/icons-material'; interface IRocketSimModalProps { isOpen: boolean; @@ -13,7 +13,9 @@ interface IRocketSimModalProps { const RocketSimDialog: React.FC = (props: IRocketSimModalProps) => { const { isOpen, onClose, id } = props; - const [loading, setLoading] = useState(false); + const [alert, setAlert] = useState(false); + const [alertMessage, setAlertMessage] = useState(''); + const [success, setSuccess] = useState(false); // RocketSimModel Properties const [dryMass, setDryMass] = useState(null); const [wetMass, setWetMass] = useState(null); @@ -21,20 +23,18 @@ const RocketSimDialog: React.FC = (props: IRocketSimModalP const [centerOfGravity, setCenterOfGravity] = useState(null); const [centerOfPressure, setCenterOfPressure] = useState(null); const [rocketLength, setRocketLength] = useState(null); - const [rocketDiameter, setRocketDiameter] = useState(null); const [fuselageDiameter, setFuselageDiameter] = useState(null); const [fuselageLength, setFuselageLength] = useState(null); const rocketSimProperties = [ - { label: "Name", value: name, setter: setName }, - { label: "Dry Mass", value: dryMass, setter: setDryMass }, - { label: "Wet Mass", value: wetMass, setter: setWetMass }, - { label: "Center of Gravity", value: centerOfGravity, setter: setCenterOfGravity }, - { label: "Center of Pressure", value: centerOfPressure, setter: setCenterOfPressure }, - { label: "Rocket Length", value: rocketLength, setter: setRocketLength }, - { label: "Rocket Diameter", value: rocketDiameter, setter: setRocketDiameter }, - { label: "Fuselage Diameter", value: fuselageDiameter, setter: setFuselageDiameter }, - { label: "Fuselage Length", value: fuselageLength, setter: setFuselageLength }, + { label: "Name", value: name, setter: setName, type: 'none' }, + { label: "Dry Mass", value: dryMass, setter: setDryMass, type: 'numeric', units: 'kg' }, + { label: "Wet Mass", value: wetMass, setter: setWetMass, type: 'numeric', units: 'kg' }, + { label: "Center of Gravity", value: centerOfGravity, setter: setCenterOfGravity, type: 'numeric', units: 'ft' }, + { label: "Center of Pressure", value: centerOfPressure, setter: setCenterOfPressure, type: 'numeric', units: 'ft' }, + { label: "Rocket Length", value: rocketLength, setter: setRocketLength, type: 'numeric', units: 'ft' }, + { label: "Fuselage Length", value: fuselageLength, setter: setFuselageLength, type: 'numeric', units: 'ft' }, + { label: "Fuselage Diameter", value: fuselageDiameter, setter: setFuselageDiameter, type: 'numeric', units: 'Inches' }, ]; const handleSave = async () => { @@ -45,30 +45,32 @@ const RocketSimDialog: React.FC = (props: IRocketSimModalP CenterOfGravity: centerOfGravity!, CenterOfPressure: centerOfPressure!, RocketLength: rocketLength!, - RocketDiameter: rocketDiameter!, FuselageDiameter: fuselageDiameter!, FuselageLength: fuselageLength!, }; - let success: boolean = false; - setLoading(!success); if (id) { // update rocketSim - success = (await api.updateRocketSim(id, payload)).error.error; + setSuccess(!(await api.updateRocketSim(id, payload)).error.error); } else { // create rocketSim - success = (await api.createRocketSim(payload)).error.error; + setSuccess(!(await api.createRocketSim(payload)).error.error); + } + console.log(success) + setAlertMessage(success ? 'RocketSim updated successfully' : 'RocketSim failed to update. Fill in all fields'); + setAlert(!success); + if (success) { + onClose(); } - setLoading(!success); - - onClose(); - }; - const handleClose = async () => { - await handleSave(); + const handleClose = () => { onClose(); }; + + const handleChange = (e: any, setState: Function) => { + setState(e.target.value as string); + }; useEffect(() => { async function fetchRocketSim() { @@ -83,7 +85,6 @@ const RocketSimDialog: React.FC = (props: IRocketSimModalP setCenterOfGravity(rocketSim.CenterOfGravity); setCenterOfPressure(rocketSim.CenterOfPressure); setRocketLength(rocketSim.RocketLength); - setRocketDiameter(rocketSim.RocketDiameter); setFuselageDiameter(rocketSim.FuselageDiameter); setFuselageLength(rocketSim.FuselageLength); } @@ -91,29 +92,52 @@ const RocketSimDialog: React.FC = (props: IRocketSimModalP fetchRocketSim(); }, [id]); - - return ( - + - Rocket Simulation + Rocket Sim Input - + + { + setAlert(false); + }} + > + + + } + sx={{ mb: 2 }} + > + {alertMessage} + + + {rocketSimProperties.map((property, index) => ( property.setter(Number(e.target.value))} + onChange={(e) => handleChange(e, property.setter)} + required variant="outlined" - type="number" + InputProps={{ + endAdornment: {property.units} + }} /> - )) - - } + ))} + + + + ); } diff --git a/client/src/utils/entities.ts b/client/src/utils/entities.ts index 7b6bf579c..62ee765a8 100644 --- a/client/src/utils/entities.ts +++ b/client/src/utils/entities.ts @@ -150,7 +150,6 @@ export interface IRocketSimModel { Name: string; DryMass: number; WetMass: number; - RocketDiameter: number; CenterOfGravity: number; CenterOfPressure: number; RocketLength: number; diff --git a/client/src/views/rocket-details-view.tsx b/client/src/views/rocket-details-view.tsx index 4e6932b48..068641509 100644 --- a/client/src/views/rocket-details-view.tsx +++ b/client/src/views/rocket-details-view.tsx @@ -22,6 +22,8 @@ import MissionConfig from '../components/MissionConfig'; import api from '../services/api'; import _ from 'lodash'; import { IRocketPopulated } from '../utils/entities'; +import RocketSimulationTab from './tabs/simulation-tab'; +import RocketSimDialog from '../components/RocketSimDailog'; interface TabPanelProps { children?: React.ReactNode; @@ -76,12 +78,14 @@ export default function RocketDetailsView(props: RocketDetailsProps) { // Popup state const [componentModalOpen, setComponentModalOpen] = useState(false); + const [isSimulationDialogOpen, setIsSimulationDialogOpen] = useState(false); const [isRocketPopUpOpen, setIsRocketPopUpOpen] = useState(false); const [isMissionConfigOpen, setIsMissionConfigOpen] = useState(false); const [rocketData, setRocketData] = useState({} as IRocketPopulated); const [selectedMission, setSelectedMission] = useState(''); const [isMissionActive, setIsMissionActive] = useState(false); + const [updateSims, setUpdateSims] = useState(false); const handleSelectedMission = (mission: string) => { setSelectedMission(mission); @@ -173,6 +177,7 @@ export default function RocketDetailsView(props: RocketDetailsProps) { + {value===1 && ( @@ -190,6 +195,11 @@ export default function RocketDetailsView(props: RocketDetailsProps) { Mission )} + {value===3 && ( + + )} + + + @@ -243,6 +256,13 @@ export default function RocketDetailsView(props: RocketDetailsProps) { onSave={()=> refresh} onClose={() => setComponentModalOpen(false)} /> + { + setIsSimulationDialogOpen(false); + setUpdateSims(true); + }} + />
diff --git a/client/src/views/tabs/simulation-tab.tsx b/client/src/views/tabs/simulation-tab.tsx new file mode 100644 index 000000000..ecbd7d37f --- /dev/null +++ b/client/src/views/tabs/simulation-tab.tsx @@ -0,0 +1,33 @@ +import React, { useEffect, useState } from 'react'; +import LoadingButton from '@mui/material/Button'; +import { Button } from '@mui/material'; +import api from '../../services/api' +import { IRocketSimModel } from '../../utils/entities'; + + + +const RocketSimulationTab: React.FC = () => { + // state + const [data, setData] = useState([{Name: 'None'}]); + useEffect(() => { + const fetchData = async () => { + const result = await api.getRocketSims(); + const d = result.data as IRocketSimModel[]; + setData(d); + } + fetchData(); + + }, []); + + return ( + <> + {data.map((sim: IRocketSimModel) => () => ( + <> + {sim.Name} + + ))} + + ); +}; + +export default RocketSimulationTab; \ No newline at end of file diff --git a/services/server/src/models/RocketSimModel.ts b/services/server/src/models/RocketSimModel.ts index e4af37fb6..e6da34d1f 100644 --- a/services/server/src/models/RocketSimModel.ts +++ b/services/server/src/models/RocketSimModel.ts @@ -7,7 +7,6 @@ export interface IRocketSimModel { CenterOfGravity: number; CenterOfPressure: number; RocketLength: number; - RocketDiameter: number; FuselageLength: number; FuselageDiameter: number; AeroDimensions?: IAeroDimensions;