diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index ff9d51f..d276559 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -21,4 +21,4 @@ jobs: pip install -r requirements.txt - name: Analysing the code with pylint run: | - pylint -d C0200,C0301,C0114,R0903,C0115,W0246,R0914,C0209,E1121,C0103,C2801,R0801,E1101,R0911,C0116 $(git ls-files '*.py') + pylint -d C0200,C0301,C0114,R0903,C0115,W0246,R0914,C0209,E1121,C0103,C2801,R0801,E1101,E0401,E0611,R0911,C0116,W0212,W0719,W0601,W1203,W0123 $(git ls-files '*.py') diff --git a/README.md b/README.md index 1517f5c..c4774c2 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ - Stores simulation input data in mongo-db ## Docker -- touch .env -- docker-compose up --build -d +- set MONGODB_CONNECTION_STRING: `touch .env && echo MONGODB_CONNECTION_STRING="$ConnectionString" > .env` +- run docker compose: `docker-compose up --build -d` ## Setup - [Install python3](https://www.python.org/downloads/) 3.11.5 or above @@ -34,19 +34,25 @@ │   │   ├── motor.py │   │   └── rocket.py │   │   -│   ├── models -│   │   ├── aerosurfaces.py +│   ├── routes │   │   ├── environment.py │   │   ├── flight.py │   │   ├── motor.py -│   │   ├── parachute.py │   │   └── rocket.py │   │   │   ├── repositories +│   │   ├── repo.py │   │   ├── environment.py │   │   ├── flight.py │   │   ├── motor.py -│   │   ├── repo.py +│   │   └── rocket.py +│   │   +│   ├── models +│   │   ├── aerosurfaces.py +│   │   ├── environment.py +│   │   ├── flight.py +│   │   ├── motor.py +│   │   ├── parachute.py │   │   └── rocket.py │   │   │   └── views diff --git a/compose.yaml b/compose.yaml index 7293a1d..b1340e9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,11 +1,12 @@ -version: '1' +version: '2' services: - fastapi-app: + api: build: context: . dockerfile: Dockerfile image: infinity-api:latest + container_name: infinity-api ports: - "3000:3000" env_file: diff --git a/lib/api.py b/lib/api.py index 595a131..a0e91cc 100644 --- a/lib/api.py +++ b/lib/api.py @@ -2,7 +2,6 @@ This is the main API file for the RocketPy API. """ import logging -from typing import Any, Dict from fastapi import FastAPI, Request, status from fastapi.exceptions import RequestValidationError @@ -10,20 +9,14 @@ from fastapi.openapi.utils import get_openapi from fastapi.responses import RedirectResponse, JSONResponse -from lib.views.flight import FlightSummary, FlightCreated, FlightUpdated, FlightDeleted, FlightPickle -from lib.views.environment import EnvSummary, EnvCreated, EnvUpdated, EnvDeleted, EnvPickle -from lib.views.rocket import RocketSummary, RocketCreated, RocketUpdated, RocketDeleted, RocketPickle -from lib.views.motor import MotorSummary, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle -from lib.models.environment import Env -from lib.models.flight import Flight -from lib.models.rocket import Rocket, RocketOptions -from lib.models.motor import Motor, MotorKinds, MotorEngines -from lib.controllers.flight import FlightController -from lib.controllers.environment import EnvController -from lib.controllers.rocket import RocketController -from lib.controllers.motor import MotorController +from lib.routes import flight, environment, motor, rocket -app = FastAPI(swagger_ui_parameters={"defaultModelsExpandDepth": 0, "syntaxHighlight.theme": "obsidian"}) +app = FastAPI( + swagger_ui_parameters={ + "defaultModelsExpandDepth": 0, + "syntaxHighlight.theme": "obsidian", + } +) app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -31,37 +24,44 @@ allow_methods=["*"], allow_headers=["*"], ) +app.include_router(flight.router) +app.include_router(environment.router) +app.include_router(motor.router) +app.include_router(rocket.router) + def custom_openapi(): if app.openapi_schema: return app.openapi_schema openapi_schema = get_openapi( title="RocketPy Infinity-API", - version="1.0.0 ALPHA", + version="1.0.0 BETA", description=( - "

RocketPy Infinity-API is a RESTful API for RocketPy, a rocket flight simulator.

" - "
" - "" - "
" - "" - "

Create, manage, and simulate rocket flights, environments, rockets, and motors.

" - "

Currently, the API only supports SolidMotor (calisto as power_off/on_drag and Cesaroni as thrust_source) and TrapezoidalFins. We apologize for the limitation, but we are actively working to expand its capabilities soon.

" - "

Please report any bugs at GitHub Issues

" + "

RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.

" + "
" + "" + "
" + "" + "

Create, manage, and simulate rocket flights, environments, rockets, and motors.

" + "

Currently, the API only supports TrapezoidalFins. We apologize for the limitation, but we are actively working to expand its capabilities soon.

" + "

Please report any bugs at GitHub Issues

" ), routes=app.routes, ) openapi_schema["info"]["x-logo"] = { "url": "https://drive.google.com/uc?id=1xKt6u5mI8x8ZuA5IZvIFDolg2_0iQUf-" } - x_swagger={"visible": False} # Hide the summary in Swagger UI app.openapi_schema = openapi_schema return app.openapi_schema + + app.openapi = custom_openapi + # Main @app.get("/", include_in_schema=False) async def main_page(): @@ -70,560 +70,19 @@ async def main_page(): """ return RedirectResponse(url="/redoc") -# Flight routes -@app.post("/flights/", tags=["FLIGHT"]) -async def create_flight(flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightCreated": - """ - Creates a new flight - - ## Args - - ``` Flight object as JSON ``` - - ## Returns - - HTTP 200 { "message": "Flight created successfully.", id: flight_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse flight data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController(flight, rocket_option, motor_kind).create_flight() - -@app.get("/flights/{flight_id}", tags=["FLIGHT"]) -async def read_flight(flight_id: int) -> "Flight": - """ - Reads a flight - - ## Args - - ``` flight_id: Flight ID hash ``` - - ## Returns - - ``` Flight object as JSON ``` - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.get_flight(flight_id) - -@app.get("/flights/rocketpy/{flight_id}", tags=["FLIGHT"]) -async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": - """ - Reads a rocketpy flight object - - ## Args - - ``` flight_id: Flight ID hash. ``` - - ## Returns - - ``` RocketPy flight object as jsonpickle string ``` - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.get_rocketpy_flight(flight_id) - -@app.put("/flights/{flight_id}/env", tags=["FLIGHT"]) -async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": - """ - Updates flight environment - - ## Args - - ``` - flight_id: Flight ID hash - env: env object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController.update_env(flight_id, env) - -@app.put("/flights/{flight_id}/rocket", tags=["FLIGHT"]) -async def update_flight_rocket(flight_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightUpdated": - """ - Updates flight rocket. - - ## Args - - ``` - flight_id: Flight ID hash. - rocket: Rocket object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController.update_rocket(flight_id, rocket, rocket_option, motor_kind) - -@app.put("/flights/{flight_id}", tags=["FLIGHT"]) -async def update_flight(flight_id: int, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightUpdated": - """ - Updates Flight object - - ## Args - ``` - flight_id: Flight ID hash. - flight: Flight object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse flight data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController(flight, rocket_option, motor_kind).update_flight(flight_id) - -@app.delete("/flights/{flight_id}", tags=["FLIGHT"]) -async def delete_flight(flight_id: int) -> "FlightDeleted": - """ - Deletes a flight - - ## Args - - ``` flight_id: Flight ID hash ``` - - ## Returns - - HTTP 200 { "message": "Flight deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.delete_flight(flight_id) - -@app.get("/flights/{flight_id}/simulate", tags=["FLIGHT"]) -async def simulate_flight(flight_id: int) -> "FlightSummary": - """ - Simulates a flight - - ## Args - - ``` flight_id: Flight ID hash ``` - - ## Returns - - ``` Flight summary as JSON ``` - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.simulate(flight_id) - -# Environment routes -@app.post("/environments/", tags=["ENVIRONMENT"]) -async def create_env(env: Env) -> "EnvCreated": - """ - Creates a new environment - - ## Args - - ``` Env object as a JSON ``` - - ## Returns - - HTTP 200 { "message": "Environment created successfully.", id: env_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create env in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await EnvController(env).create_env() - -@app.get("/environments/{env_id}", tags=["ENVIRONMENT"]) -async def read_env(env_id: int) -> "Env": - """ - Reads an environment - - ## Args - - ``` env_id: Environment ID hash ``` - - ## Returns - - ``` Env object as JSON. ``` - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.get_env(env_id) - -@app.put("/environments/{env_id}", tags=["ENVIRONMENT"]) -async def update_env(env_id: int, env: Env) -> "EnvUpdated": - """ - Updates an environment - - ## Args - - ``` - env_id: Environment ID hash - env: Env object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Environment updated successfully.", new_env_id: new_env_id_hash } - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update env in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await EnvController(env).update_env(env_id) - -@app.delete("/environments/{env_id}", tags=["ENVIRONMENT"]) -async def delete_env(env_id: int) -> "EnvDeleted": - """ - Deletes an environment - - ## Args - - ``` env_id: Environment ID hash ``` - - ## Returns - - HTTP 200 { "message": "Environment deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.delete_env(env_id) - -@app.get("/environments/rocketpy/{env_id}", tags=["ENVIRONMENT"]) -async def read_rocketpy_env(env_id: int) -> "EnvPickle": - """ - Reads a rocketpy environment - - ## Args - - ``` env_id: Environment ID hash ``` - - ## Returns - - ``` Rocketpy Environment object as JSONPickle string. ``` - - ## Raises - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.get_rocketpy_env(env_id) - -@app.get("/environments/{env_id}/simulate", tags=["ENVIRONMENT"]) -async def simulate_env(env_id: int) -> "EnvSummary": - """ - Simulates an environment - - ## Args - - ``` env_id: Env ID hash ``` - - ## Returns - - ``` Env summary object containig simulation numbers and plots as JSON ``` - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.simulate(env_id) - -# Motor routes -@app.post("/motors/", tags=["MOTOR"]) -async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": - """ - Creates a new motor - - ## Args - - ``` Motor object as a JSON ``` - - ## Returns - - HTTP 200 { "message": "Motor created successfully.", id: motor_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse motor data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create motor in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await MotorController(motor, motor_kind).create_motor() - -@app.get("/motors/{motor_id}", tags=["MOTOR"]) -async def read_motor(motor_id: int) -> "Motor": - """ - Reads a motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - Motor object as JSON. - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.get_motor(motor_id) - -@app.put("/motors/{motor_id}", tags=["MOTOR"]) -async def update_motor(motor_id: int, motor: Motor, motor_kind: MotorKinds) -> "MotorUpdated": - """ - Updates a motor - - ## Args - - ``` - motor_id: Motor ID hash - motor: Motor object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Motor updated successfully.", new_motor_id: new_motor_id_hash } - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse motor data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update motor in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await MotorController(motor, motor_kind).update_motor(motor_id) - -@app.delete("/motors/{motor_id}", tags=["MOTOR"]) -async def delete_motor(motor_id: int) -> "MotorDeleted": - """ - Deletes a motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - HTTP 200 { "message": "Motor deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.delete_motor(motor_id) - -@app.get("/motors/rocketpy/{motor_id}", tags=["MOTOR"]) -async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": - """ - Reads a rocketpy motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - ``` Rocketpy Motor object as JSONPickle string ``` - - ## Raises - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.get_rocketpy_motor(motor_id) - -@app.get("/motors/{motor_id}/simulate", tags=["MOTOR"]) -async def simulate_motor(motor_id: int) -> "MotorSummary": - """ - Simulates a motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - ``` Motor summary as JSON ``` - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.simulate(motor_id) - -# Rocket routes -@app.post("/rockets/", tags=["ROCKET"]) -async def create_rocket(rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "RocketCreated": - """ - Creates a new rocket - - ## Args - - ``` Rocket object as a JSON ``` - - ## Returns - - HTTP 200 { "message": "Rocket created successfully.", id: rocket_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create rocket in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await RocketController(rocket, rocket_option, motor_kind).create_rocket() - -@app.get("/rockets/{rocket_id}", tags=["ROCKET"]) -async def read_rocket(rocket_id: int) -> Rocket: - """ - Reads a rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - ``` Rocket object as JSON ``` - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.get_rocket(rocket_id) - -@app.put("/rockets/{rocket_id}", tags=["ROCKET"]) -async def update_rocket(rocket_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "RocketUpdated": - """ - Updates a rocket - - ## Args - - ``` - rocket_id: Rocket ID hash - rocket: Rocket object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Rocket updated successfully.", new_rocket_id: new_rocket_id_hash } - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update rocket in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await RocketController(rocket, rocket_option, motor_kind).update_rocket(rocket_id) - -@app.delete("/rockets/{rocket_id}", tags=["ROCKET"]) -async def delete_rocket(rocket_id: int) -> "RocketDeleted": - """ - Deletes a rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - HTTP 200 { "message": "Rocket deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.delete_rocket(rocket_id) - -@app.get("/rockets/rocketpy/{rocket_id}", tags=["ROCKET"]) -async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": - """ - Reads a rocketpy rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - ``` Rocketpy Rocket object as JSONPickle string ``` - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.get_rocketpy_rocket(rocket_id) - -@app.get("/rockets/{rocket_id}/simulate", tags=["ROCKET"]) -async def simulate_rocket(rocket_id: int) -> "RocketSummary": - """ - Simulates a rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - HTTP 200 pydantic rocket summary object containig simulation numbers and plots as JSON. - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.simulate(rocket_id) # Additional routes @app.get("/health", status_code=status.HTTP_200_OK, include_in_schema=False) async def __perform_healthcheck(): - return {'health': 'Everything OK!'} + return {"health": "Everything OK!"} + -# Errors +# Errors @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): - exc_str = f'{exc}'.replace('\n', ' ').replace(' ', ' ') - logging.error(f"{request}: {exc_str}") - content = {'status_code': 10422, 'message': exc_str, 'data': None} - return JSONResponse(content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) + exc_str = f"{exc}".replace("\n", " ").replace(" ", " ") + logging.error(f"{request}: {exc_str}") + content = {"status_code": 10422, "message": exc_str, "data": None} + return JSONResponse( + content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY + ) diff --git a/lib/controllers/environment.py b/lib/controllers/environment.py index 46fc5ac..90c4c6f 100644 --- a/lib/controllers/environment.py +++ b/lib/controllers/environment.py @@ -1,15 +1,24 @@ -from typing import Dict, Any, Union +from typing import Union import jsonpickle from rocketpy.environment.environment import Environment -from fastapi import Response, status +from fastapi import HTTPException, status from lib.models.environment import Env from lib.repositories.environment import EnvRepository -from lib.views.environment import EnvSummary, EnvData, EnvPlots, EnvCreated, EnvDeleted, EnvUpdated, EnvPickle - -class EnvController(): - """ +from lib.views.environment import ( + EnvSummary, + EnvData, + EnvPlots, + EnvCreated, + EnvDeleted, + EnvUpdated, + EnvPickle, +) + + +class EnvController: + """ Controller for the Environment model. Init Attributes: @@ -18,35 +27,39 @@ class EnvController(): Enables: - Create a rocketpy.Environment object from an Env model object. """ + def __init__(self, env: Env): rocketpy_env = Environment( - latitude=env.latitude, - longitude=env.longitude, - elevation=env.elevation, - date=env.date - ) + latitude=env.latitude, + longitude=env.longitude, + elevation=env.elevation, + date=env.date, + ) rocketpy_env.set_atmospheric_model( - type=env.atmospheric_model_type, - file=env.atmospheric_model_file - ) + type=env.atmospheric_model_type, file=env.atmospheric_model_file + ) self.rocketpy_env = rocketpy_env self.env = env - async def create_env(self) -> "Union[EnvCreated, Response]": + async def create_env(self) -> "Union[EnvCreated, HTTPException]": """ Create a env in the database. Returns: - Dict[str, str]: Environment id. + EnvCreated: Environment id. """ env = EnvRepository(environment=self.env) successfully_created_env = await env.create_env() - if successfully_created_env: - return EnvCreated(env_id=str(env.env_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + if not successfully_created_env: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create environment", + ) + + return EnvCreated(env_id=str(env.env_id)) @staticmethod - async def get_env(env_id: int) -> "Union[Env, Response]": + async def get_env(env_id: int) -> "Union[Env, HTTPException]": """ Get a env from the database. @@ -59,14 +72,16 @@ async def get_env(env_id: int) -> "Union[Env, Response]": Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) + return successfully_read_env @staticmethod - async def get_rocketpy_env(env_id: int) -> "Union[EnvPickle, Response]": + async def get_rocketpy_env(env_id: int) -> "Union[EnvPickle, HTTPException]": """ Get a rocketpy env object encoded as jsonpickle string from the database. @@ -79,17 +94,21 @@ async def get_rocketpy_env(env_id: int) -> "Union[EnvPickle, Response]": Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) - successfully_read_rocketpy_env = \ - EnvController( successfully_read_env ).rocketpy_env + successfully_read_rocketpy_env = EnvController( + successfully_read_env + ).rocketpy_env - return EnvPickle(jsonpickle_rocketpy_env=jsonpickle.encode(successfully_read_rocketpy_env)) + return EnvPickle( + jsonpickle_rocketpy_env=jsonpickle.encode(successfully_read_rocketpy_env) + ) - async def update_env(self, env_id: int) -> "Union[EnvUpdated, Response]": + async def update_env(self, env_id: int) -> "Union[EnvUpdated, HTTPException]": """ Update a env in the database. @@ -97,25 +116,30 @@ async def update_env(self, env_id: int) -> "Union[EnvUpdated, Response]": env_id (int): env id. Returns: - Dict[str, Any]: env id and message. + EnvUpdated: env id and message. Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) - successfully_updated_env = \ - await EnvRepository(environment=self.env, env_id=env_id).update_env() + successfully_updated_env = await EnvRepository( + environment=self.env, env_id=env_id + ).update_env() + if not successfully_updated_env: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update environment", + ) - if successfully_updated_env: - return EnvUpdated(new_env_id=str(successfully_updated_env)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return EnvUpdated(new_env_id=str(successfully_updated_env)) @staticmethod - async def delete_env(env_id: int) -> "Union[EnvDeleted, Response]": + async def delete_env(env_id: int) -> "Union[EnvDeleted, HTTPException]": """ Delete a env from the database. @@ -123,24 +147,28 @@ async def delete_env(env_id: int) -> "Union[EnvDeleted, Response]": env_id (int): Environment id. Returns: - Dict[str, str]: Environment id and message. + EnvDeleted: Environment id and message. Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) + + successfully_deleted_env = await EnvRepository(env_id=env_id).delete_env() + if not successfully_deleted_env: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete environment", + ) - successfully_deleted_env = \ - await EnvRepository(env_id=env_id).delete_env() - if successfully_deleted_env: - return EnvDeleted(deleted_env_id = str(env_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return EnvDeleted(deleted_env_id=str(env_id)) @staticmethod - async def simulate(env_id: int) -> "Union[EnvSummary, Response]": + async def simulate(env_id: int) -> "Union[EnvSummary, HTTPException]": """ Simulate a rocket environment. @@ -153,14 +181,22 @@ async def simulate(env_id: int) -> "Union[EnvSummary, Response]": Raises: HTTP 404 Not Found: If the env does not exist in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - env = EnvController(successfully_read_env).rocketpy_env - env_simulation_numbers = EnvData.parse_obj(env.all_info_returned()) - env_simulation_plots = EnvPlots.parse_obj(env.all_plot_info_returned()) - - env_summary = EnvSummary( env_data=env_simulation_numbers, env_plots=env_simulation_plots ) - return env_summary + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) + + try: + env = EnvController(successfully_read_env).rocketpy_env + env_simulation_numbers = EnvData.parse_obj(env.all_info_returned()) + env_simulation_plots = EnvPlots.parse_obj(env.all_plot_info_returned()) + env_summary = EnvSummary( + env_data=env_simulation_numbers, env_plots=env_simulation_plots + ) + return env_summary + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to simulate environment: {e}", + ) from e diff --git a/lib/controllers/flight.py b/lib/controllers/flight.py index 5c83a5d..0fd6eba 100644 --- a/lib/controllers/flight.py +++ b/lib/controllers/flight.py @@ -1,25 +1,38 @@ -from typing import Dict, Any, Union -from fastapi import Response, status +from typing import Union +from fastapi import HTTPException, status from rocketpy.simulation.flight import Flight as RocketpyFlight import jsonpickle from lib.models.rocket import Rocket, RocketOptions -from lib.models.motor import Motor, MotorEngines, MotorKinds +from lib.models.motor import MotorKinds from lib.models.flight import Flight from lib.models.environment import Env -from lib.views.flight import ( - FlightSummary, SurfaceWindConditions, OutOfRailConditions, BurnoutConditions, ApogeeConditions, MaximumValues, - InitialConditions, NumericalIntegrationSettings, ImpactConditions, EventsRegistered, LaunchRailConditions, FlightData, - FlightCreated, FlightUpdated, FlightDeleted, FlightPickle +from lib.views.flight import ( + FlightSummary, + SurfaceWindConditions, + OutOfRailConditions, + BurnoutConditions, + ApogeeConditions, + MaximumValues, + InitialConditions, + NumericalIntegrationSettings, + ImpactConditions, + EventsRegistered, + LaunchRailConditions, + FlightData, + FlightCreated, + FlightUpdated, + FlightDeleted, + FlightPickle, ) from lib.repositories.flight import FlightRepository from lib.controllers.environment import EnvController from lib.controllers.rocket import RocketController -class FlightController(): +class FlightController: """ Controller for the Flight model. @@ -36,8 +49,13 @@ class FlightController(): - Read a RocketpyFlight object from the database. """ - def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds): - rocketpy_rocket = RocketController(flight.rocket, rocket_option=rocket_option, motor_kind=motor_kind).rocketpy_rocket + + def __init__( + self, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds + ): + rocketpy_rocket = RocketController( + flight.rocket, rocket_option=rocket_option, motor_kind=motor_kind + ).rocketpy_rocket rocketpy_env = EnvController(flight.environment).rocketpy_env rocketpy_flight = RocketpyFlight( @@ -45,7 +63,7 @@ def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: Mot inclination=flight.inclination, heading=flight.heading, environment=rocketpy_env, - rail_length=flight.rail_length + rail_length=flight.rail_length, ) self.rocket_option = rocket_option @@ -53,26 +71,28 @@ def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: Mot self.rocketpy_flight = rocketpy_flight self.flight = flight - async def create_flight(self) -> "Union[FlightCreated, Response]": + async def create_flight(self) -> "Union[FlightCreated, HTTPException]": """ Create a flight in the database. Returns: - Dict[str, str]: Flight id. + FlightCreated: Flight id. """ flight = FlightRepository(flight=self.flight) - successfully_created_flight = \ - await flight.create_flight( - motor_kind = self.motor_kind, - rocket_option = self.rocket_option - ) + successfully_created_flight = await flight.create_flight( + motor_kind=self.motor_kind, rocket_option=self.rocket_option + ) - if successfully_created_flight: - return FlightCreated(flight_id=str(flight.flight_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + if not successfully_created_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create flight.", + ) + + return FlightCreated(flight_id=str(flight.flight_id)) @staticmethod - async def get_flight(flight_id: int) -> "Union[Flight, Response]": + async def get_flight(flight_id: int) -> "Union[Flight, HTTPException]": """ Get a flight from the database. @@ -85,14 +105,20 @@ async def get_flight(flight_id: int) -> "Union[Flight, Response]": Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + return successfully_read_flight @staticmethod - async def get_rocketpy_flight(flight_id: int) -> "Union[FlightPickle, Response]": + async def get_rocketpy_flight( + flight_id: int, + ) -> "Union[FlightPickle, HTTPException]": """ Get a rocketpy flight object encoded as jsonpickle string from the database. @@ -105,19 +131,29 @@ async def get_rocketpy_flight(flight_id: int) -> "Union[FlightPickle, Response]" Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) - successfully_read_rocketpy_flight = \ - FlightController(flight = successfully_read_flight, - rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight + successfully_read_rocketpy_flight = FlightController( + flight=successfully_read_flight, + rocket_option=RocketOptions(successfully_read_flight.rocket._rocket_option), + motor_kind=MotorKinds(successfully_read_flight.rocket.motor._motor_kind), + ).rocketpy_flight - return FlightPickle(jsonpickle_rocketpy_flight=jsonpickle.encode(successfully_read_rocketpy_flight)) + return FlightPickle( + jsonpickle_rocketpy_flight=jsonpickle.encode( + successfully_read_rocketpy_flight + ) + ) - async def update_flight(self, flight_id: int) -> "Union[FlightUpdated, Response]": + async def update_flight( + self, flight_id: int + ) -> "Union[FlightUpdated, HTTPException]": """ Update a flight in the database. @@ -125,28 +161,34 @@ async def update_flight(self, flight_id: int) -> "Union[FlightUpdated, Response] flight_id (int): Flight id. Returns: - Dict[str, Any]: Flight id and message. + FlightUpdated: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) - successfully_updated_flight = \ - await FlightRepository(flight=self.flight, flight_id=flight_id).update_flight( - rocket_option = self.rocket_option, - motor_kind = self.motor_kind - ) + successfully_updated_flight = await FlightRepository( + flight=self.flight, flight_id=flight_id + ).update_flight(rocket_option=self.rocket_option, motor_kind=self.motor_kind) + if not successfully_updated_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update flight.", + ) - if successfully_updated_flight: - return FlightUpdated(new_flight_id=str(successfully_updated_flight)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @staticmethod - async def update_env(flight_id: int, env: Env) -> "Union[FlightUpdated, Response]": + async def update_env( + flight_id: int, env: Env + ) -> "Union[FlightUpdated, HTTPException]": """ Update the environment of a flight in the database. @@ -155,28 +197,37 @@ async def update_env(flight_id: int, env: Env) -> "Union[FlightUpdated, Response env (models.Env): Environment model object. Returns: - Dict[str, Any]: Flight id and message. + FlightUpdated: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) - flight = successfully_read_flight.dict() + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + flight = successfully_read_flight.dict() flight["environment"] = env flight = Flight(**flight) - successfully_updated_flight = \ - await FlightRepository(flight=flight, flight_id=flight_id).update_flight() + successfully_updated_flight = await FlightRepository( + flight=flight, flight_id=flight_id + ).update_flight() + if not successfully_updated_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update flight.", + ) - if successfully_updated_flight: - return FlightUpdated(new_flight_id=str(successfully_updated_flight)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @staticmethod - async def update_rocket(flight_id: int, rocket: Rocket, rocket_option, motor_kind) -> "Union[FlightUpdated, Response]": + async def update_rocket( + flight_id: int, rocket: Rocket, rocket_option, motor_kind + ) -> "Union[FlightUpdated, HTTPException]": """ Update the rocket of a flight in the database. @@ -185,32 +236,38 @@ async def update_rocket(flight_id: int, rocket: Rocket, rocket_option, motor_kin rocket (models.Rocket): Rocket model object. Returns: - Dict[str, Any]: Flight id and message. + FlightUpdated: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) - flight = successfully_read_flight.dict() + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + flight = successfully_read_flight.dict() updated_rocket = rocket.dict() updated_rocket["rocket_option"] = rocket_option updated_rocket["motor"]["motor_kind"] = motor_kind flight["rocket"] = Rocket(**updated_rocket) - flight = Flight(**flight) - successfully_updated_flight = \ - await FlightRepository(flight=flight, flight_id=flight_id).update_flight() + successfully_updated_flight = await FlightRepository( + flight=flight, flight_id=flight_id + ).update_flight() + if not successfully_updated_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update flight.", + ) - if successfully_updated_flight: - return FlightUpdated(new_flight_id=str(successfully_updated_flight)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @staticmethod - async def delete_flight(flight_id: int) -> "Union[FlightDeleted, Response]": + async def delete_flight(flight_id: int) -> "Union[FlightDeleted, HTTPException]": """ Delete a flight from the database. @@ -218,24 +275,32 @@ async def delete_flight(flight_id: int) -> "Union[FlightDeleted, Response]": flight_id (int): Flight id. Returns: - Dict[str, str]: Flight id and message. + FlightDeleted: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + + successfully_deleted_flight = await FlightRepository( + flight_id=flight_id + ).delete_flight() + if not successfully_deleted_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete flight.", + ) - successfully_deleted_flight = \ - await FlightRepository(flight_id=flight_id).delete_flight() - if successfully_deleted_flight: - return FlightDeleted(deleted_flight_id=str(flight_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightDeleted(deleted_flight_id=str(flight_id)) @staticmethod - async def simulate(flight_id: int) -> "Union[FlightSummary, Response]": + async def simulate(flight_id: int) -> "Union[FlightSummary, HTTPException]": """ Simulate a rocket flight. @@ -248,160 +313,247 @@ async def simulate(flight_id: int) -> "Union[FlightSummary, Response]": Raises: HTTP 404 Not Found: If the flight does not exist in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - flight = FlightController(flight = successfully_read_flight, - rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight - - _initial_conditions = InitialConditions( - initial_altitude = "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format(flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0)), - initial_velocity = "Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format(flight.vx(0), flight.vy(0), flight.vz(0)), - initial_position = "Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format(flight.x(0), flight.y(0), flight.z(0)), - initial_angular_position = "Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format(flight.phi(0), flight.theta(0), flight.psi(0)), - initial_angular_velocity = "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format(flight.w1(0), flight.w2(0), flight.w3(0)) - ) - - _numerical_integration_settings = NumericalIntegrationSettings( - max_time = "Maximum Allowed Flight Time: {:f} s".format(flight.max_time), - max_time_step = "Maximum Allowed Time Step: {:f} s".format(flight.max_time_step), - min_time_step = "Minimum Allowed Time Step: {:e} s".format(flight.min_time_step), - relative_error_tolerance = f"Relative Error Tolerance: {flight.rtol}", - absolute_error_tolerance = f"Absolute Error Tolerance: {flight.atol}", - time_overshoot = f"Allow Event Overshoot: {flight.time_overshoot}", - terminate_on_apogee = f"Terminate Simulation on Apogee: {flight.terminate_on_apogee}", - number_of_time_steps = f"Number of Time Steps Used: {len(flight.time_steps)}", - function_evaluations_per_time_step = f"Number of Derivative Functions Evaluation: {sum(flight.function_evaluations_per_time_step)}", - avg_function_evaluations_per_time_step = "Average Function Evaluations per Time Step: {:3f}".format(sum(flight.function_evaluations_per_time_step) / len(flight.time_steps)) - ) - - _launch_rail_conditions = LaunchRailConditions( - rail_length = "Launch Rail Length: {:.2f} m".format(flight.rail_length), - flight_inclination = "Launch Rail Inclination: {:.2f}°".format(flight.inclination), - flight_heading = "Launch Rail Heading: {:.2f}°".format(flight.heading) - ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) - _surface_wind_conditions = SurfaceWindConditions( - frontal_surface_wind_speed = "Frontal Surface Wind Speed: {:.2f} m/s".format(flight.frontal_surface_wind), - lateral_surface_wind_speed = "Lateral Surface Wind Speed: {:.2f} m/s".format(flight.lateral_surface_wind) - ) + try: + flight = FlightController( + flight=successfully_read_flight, + rocket_option=RocketOptions( + successfully_read_flight.rocket._rocket_option + ), + motor_kind=MotorKinds( + successfully_read_flight.rocket.motor._motor_kind + ), + ).rocketpy_flight + + _initial_conditions = InitialConditions( + initial_altitude="Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format( + flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0) + ), + initial_velocity="Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format( + flight.vx(0), flight.vy(0), flight.vz(0) + ), + initial_position="Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format( + flight.x(0), flight.y(0), flight.z(0) + ), + initial_angular_position="Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format( + flight.phi(0), flight.theta(0), flight.psi(0) + ), + initial_angular_velocity="Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format( + flight.w1(0), flight.w2(0), flight.w3(0) + ), + ) - _out_of_rail_conditions = OutOfRailConditions( - out_of_rail_time = "Rail Departure Time: {:.3f} s".format(flight.out_of_rail_time), - out_of_rail_velocity = "Rail Departure Velocity: {:.3f} m/s".format(flight.out_of_rail_velocity), - out_of_rail_static_margin = "Rail Departure Static Margin: {:.3f} c".format(flight.rocket.static_margin(flight.out_of_rail_time)), - out_of_rail_angle_of_attack = "Rail Departure Angle of Attack: {:.3f}°".format(flight.angle_of_attack(flight.out_of_rail_time)), - out_of_rail_thrust_weight_ratio = "Rail Departure Thrust-Weight Ratio: {:.3f}".format(flight.rocket.thrust_to_weight(flight.out_of_rail_time)), - out_of_rail_reynolds_number = "Rail Departure Reynolds Number: {:.3e}".format(flight.reynolds_number(flight.out_of_rail_time)) - ) + _numerical_integration_settings = NumericalIntegrationSettings( + max_time="Maximum Allowed Flight Time: {:f} s".format(flight.max_time), + max_time_step="Maximum Allowed Time Step: {:f} s".format( + flight.max_time_step + ), + min_time_step="Minimum Allowed Time Step: {:e} s".format( + flight.min_time_step + ), + relative_error_tolerance=f"Relative Error Tolerance: {flight.rtol}", + absolute_error_tolerance=f"Absolute Error Tolerance: {flight.atol}", + time_overshoot=f"Allow Event Overshoot: {flight.time_overshoot}", + terminate_on_apogee=f"Terminate Simulation on Apogee: {flight.terminate_on_apogee}", + number_of_time_steps=f"Number of Time Steps Used: {len(flight.time_steps)}", + function_evaluations_per_time_step=f"Number of Derivative Functions Evaluation: {sum(flight.function_evaluations_per_time_step)}", + avg_function_evaluations_per_time_step="Average Function Evaluations per Time Step: {:3f}".format( + sum(flight.function_evaluations_per_time_step) + / len(flight.time_steps) + ), + ) - _burnout_conditions = BurnoutConditions( - burnout_time = "Burn out time: {:.3f} s".format(flight.rocket.motor.burn_out_time), - burnout_altitude = "Altitude at burn out: {:.3f} m (AGL)".format( - flight.z(flight.rocket.motor.burn_out_time) - - flight.env.elevation - ), - burnout_rocket_velocity = "Rocket velocity at burn out: {:.3f} m/s".format( - flight.speed(flight.rocket.motor.burn_out_time) - ), - burnout_freestream_velocity = "Freestream velocity at burn out: {:.3f} m/s".format(( - flight.stream_velocity_x(flight.rocket.motor.burn_out_time) ** 2 - + flight.stream_velocity_y(flight.rocket.motor.burn_out_time) ** 2 - + flight.stream_velocity_z(flight.rocket.motor.burn_out_time) ** 2 + _launch_rail_conditions = LaunchRailConditions( + rail_length="Launch Rail Length: {:.2f} m".format(flight.rail_length), + flight_inclination="Launch Rail Inclination: {:.2f}°".format( + flight.inclination + ), + flight_heading="Launch Rail Heading: {:.2f}°".format(flight.heading), ) - ** 0.5), - burnout_mach_number = "Mach Number at burn out: {:.3f}".format(flight.mach_number(flight.rocket.motor.burn_out_time)), - burnout_kinetic_energy = "Kinetic energy at burn out: {:.3e}".format(flight.kinetic_energy(flight.rocket.motor.burn_out_time)) - ) - _apogee_conditions = ApogeeConditions( - apogee_altitude = "Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format(flight.apogee, flight.apogee - flight.env.elevation), - apogee_time = "Apogee Time: {:.3f} s".format(flight.apogee_time), - apogee_freestream_speed = "Apogee Freestream Speed: {:.3f} m/s".format(flight.apogee_freestream_speed) - ) + _surface_wind_conditions = SurfaceWindConditions( + frontal_surface_wind_speed="Frontal Surface Wind Speed: {:.2f} m/s".format( + flight.frontal_surface_wind + ), + lateral_surface_wind_speed="Lateral Surface Wind Speed: {:.2f} m/s".format( + flight.lateral_surface_wind + ), + ) - _maximum_values = MaximumValues( - maximum_speed = "Maximum Speed: {:.3f} m/s at {:.2f} s".format(flight.max_speed, flight.max_speed_time), - maximum_mach_number = "Maximum Mach Number: {:.3f} Mach at {:.2f} s".format(flight.max_mach_number, flight.max_mach_number_time), - maximum_reynolds_number = "Maximum Reynolds Number: {:.3e} at {:.2f} s".format(flight.max_reynolds_number, flight.max_reynolds_number_time), - maximum_dynamic_pressure = "Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format(flight.max_dynamic_pressure, flight.max_dynamic_pressure_time), - maximum_acceleration_during_motor_burn = "Maximum Acceleration During Motor Burn: {:.3f} m/s² at {:.2f} s".format(flight.max_acceleration, flight.max_acceleration_time), - maximum_gs_during_motor_burn = "Maximum Gs During Motor Burn: {:.3f} g at {:.2f} s".format(flight.max_acceleration / flight.env.gravity(flight.z(flight.max_acceleration_time)), flight.max_acceleration_time), - maximum_acceleration_after_motor_burn = "Maximum Acceleration After Motor Burn: {:.3f} m/s² at {:.2f} s".format( - flight.max_acceleration_power_off, - flight.max_acceleration_power_off_time, - ), - maximum_gs_after_motor_burn = "Maximum Gs After Motor Burn: {:.3f} g at {:.2f} s".format( - flight.max_acceleration_power_off / flight.env.standard_g, - flight.max_acceleration_power_off_time, - ), - maximum_upper_rail_button_normal_force = "Maximum Upper Rail Button Normal Force: {:.3f} N".format(flight.max_rail_button1_normal_force), - maximum_upper_rail_button_shear_force = "Maximum Upper Rail Button Shear Force: {:.3f} N".format(flight.max_rail_button1_shear_force), - maximum_lower_rail_button_normal_force = "Maximum Lower Rail Button Normal Force: {:.3f} N".format(flight.max_rail_button2_normal_force), - maximum_lower_rail_button_shear_force = "Maximum Lower Rail Button Shear Force: {:.3f} N".format(flight.max_rail_button2_shear_force) - ) + _out_of_rail_conditions = OutOfRailConditions( + out_of_rail_time="Rail Departure Time: {:.3f} s".format( + flight.out_of_rail_time + ), + out_of_rail_velocity="Rail Departure Velocity: {:.3f} m/s".format( + flight.out_of_rail_velocity + ), + out_of_rail_static_margin="Rail Departure Static Margin: {:.3f} c".format( + flight.rocket.static_margin(flight.out_of_rail_time) + ), + out_of_rail_angle_of_attack="Rail Departure Angle of Attack: {:.3f}°".format( + flight.angle_of_attack(flight.out_of_rail_time) + ), + out_of_rail_thrust_weight_ratio="Rail Departure Thrust-Weight Ratio: {:.3f}".format( + flight.rocket.thrust_to_weight(flight.out_of_rail_time) + ), + out_of_rail_reynolds_number="Rail Departure Reynolds Number: {:.3e}".format( + flight.reynolds_number(flight.out_of_rail_time) + ), + ) - if len(flight.impact_state) != 0: - _impact_conditions = ImpactConditions( - x_impact_position = "X Impact: {:.3f} m".format(flight.x_impact), - y_impact_position = "Y Impact: {:.3f} m".format(flight.y_impact), - time_of_impact = "Time of Impact: {:.3f} s".format(flight.t_final), - impact_velocity = "Velocity at Impact: {:.3f} m/s".format(flight.impact_velocity) + _burnout_conditions = BurnoutConditions( + burnout_time="Burn out time: {:.3f} s".format( + flight.rocket.motor.burn_out_time + ), + burnout_altitude="Altitude at burn out: {:.3f} m (AGL)".format( + flight.z(flight.rocket.motor.burn_out_time) - flight.env.elevation + ), + burnout_rocket_velocity="Rocket velocity at burn out: {:.3f} m/s".format( + flight.speed(flight.rocket.motor.burn_out_time) + ), + burnout_freestream_velocity="Freestream velocity at burn out: {:.3f} m/s".format( + ( + flight.stream_velocity_x(flight.rocket.motor.burn_out_time) ** 2 + + flight.stream_velocity_y(flight.rocket.motor.burn_out_time) + ** 2 + + flight.stream_velocity_z(flight.rocket.motor.burn_out_time) + ** 2 + ) + ** 0.5 + ), + burnout_mach_number="Mach Number at burn out: {:.3f}".format( + flight.mach_number(flight.rocket.motor.burn_out_time) + ), + burnout_kinetic_energy="Kinetic energy at burn out: {:.3e}".format( + flight.kinetic_energy(flight.rocket.motor.burn_out_time) + ), ) - elif flight.terminate_on_apogee is False: - _impact_conditions = ImpactConditions( - time = "Time: {:.3f} s".format(flight.solution[-1][0]), - altitude = "Altitude: {:.3f} m".format(flight.solution[-1][3]) + + _apogee_conditions = ApogeeConditions( + apogee_altitude="Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format( + flight.apogee, flight.apogee - flight.env.elevation + ), + apogee_time="Apogee Time: {:.3f} s".format(flight.apogee_time), + apogee_freestream_speed="Apogee Freestream Speed: {:.3f} m/s".format( + flight.apogee_freestream_speed + ), ) - if len(flight.parachute_events) == 0: - _events_registered = EventsRegistered( - events_trace = "No parachute event registered" + _maximum_values = MaximumValues( + maximum_speed="Maximum Speed: {:.3f} m/s at {:.2f} s".format( + flight.max_speed, flight.max_speed_time + ), + maximum_mach_number="Maximum Mach Number: {:.3f} Mach at {:.2f} s".format( + flight.max_mach_number, flight.max_mach_number_time + ), + maximum_reynolds_number="Maximum Reynolds Number: {:.3e} at {:.2f} s".format( + flight.max_reynolds_number, flight.max_reynolds_number_time + ), + maximum_dynamic_pressure="Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format( + flight.max_dynamic_pressure, flight.max_dynamic_pressure_time + ), + maximum_acceleration_during_motor_burn="Maximum Acceleration During Motor Burn: {:.3f} m/s² at {:.2f} s".format( + flight.max_acceleration, flight.max_acceleration_time + ), + maximum_gs_during_motor_burn="Maximum Gs During Motor Burn: {:.3f} g at {:.2f} s".format( + flight.max_acceleration + / flight.env.gravity(flight.z(flight.max_acceleration_time)), + flight.max_acceleration_time, + ), + maximum_acceleration_after_motor_burn="Maximum Acceleration After Motor Burn: {:.3f} m/s² at {:.2f} s".format( + flight.max_acceleration_power_off, + flight.max_acceleration_power_off_time, + ), + maximum_gs_after_motor_burn="Maximum Gs After Motor Burn: {:.3f} g at {:.2f} s".format( + flight.max_acceleration_power_off / flight.env.standard_g, + flight.max_acceleration_power_off_time, + ), + maximum_upper_rail_button_normal_force="Maximum Upper Rail Button Normal Force: {:.3f} N".format( + flight.max_rail_button1_normal_force + ), + maximum_upper_rail_button_shear_force="Maximum Upper Rail Button Shear Force: {:.3f} N".format( + flight.max_rail_button1_shear_force + ), + maximum_lower_rail_button_normal_force="Maximum Lower Rail Button Normal Force: {:.3f} N".format( + flight.max_rail_button2_normal_force + ), + maximum_lower_rail_button_shear_force="Maximum Lower Rail Button Shear Force: {:.3f} N".format( + flight.max_rail_button2_shear_force + ), ) - else: - events = { } - for event in flight.parachute_events: - trigger_time = event[0] - parachute = event[1] - open_time = trigger_time + parachute.lag - velocity = flight.free_stream_speed(open_time) - altitude = flight.z(open_time) - name = parachute.name.title() - events[name] = [] - events[name].append(name + " Ejection Triggered at: {:.3f} s".format(trigger_time)) - events[name].append(name + " Parachute Inflated at: {:.3f} s".format(open_time)) - events[name].append( - name - + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( - velocity - ) + + if len(flight.impact_state) != 0: + _impact_conditions = ImpactConditions( + x_impact_position="X Impact: {:.3f} m".format(flight.x_impact), + y_impact_position="Y Impact: {:.3f} m".format(flight.y_impact), + time_of_impact="Time of Impact: {:.3f} s".format(flight.t_final), + impact_velocity="Velocity at Impact: {:.3f} m/s".format( + flight.impact_velocity + ), ) - events[name].append( - name - + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( - altitude - flight.env.elevation - ) + elif flight.terminate_on_apogee is False: + _impact_conditions = ImpactConditions( + time="Time: {:.3f} s".format(flight.solution[-1][0]), + altitude="Altitude: {:.3f} m".format(flight.solution[-1][3]), ) - _events_registered = EventsRegistered( - events_trace = events - ) - - _flight_data = FlightData( - initial_conditions = _initial_conditions, - numerical_integration_settings = _numerical_integration_settings, - surface_wind_conditions = _surface_wind_conditions, - launch_rail_conditions = _launch_rail_conditions, - out_of_rail_conditions= _out_of_rail_conditions, - burnout_conditions = _burnout_conditions, - apogee_conditions = _apogee_conditions, - maximum_values = _maximum_values, - impact_conditions = _impact_conditions, - events_registered = _events_registered - ) - flight_summary = FlightSummary( flight_data = _flight_data ) + if len(flight.parachute_events) == 0: + _events_registered = EventsRegistered( + events_trace="No parachute event registered" + ) + else: + events = {} + for event in flight.parachute_events: + trigger_time = event[0] + parachute = event[1] + open_time = trigger_time + parachute.lag + velocity = flight.free_stream_speed(open_time) + altitude = flight.z(open_time) + name = parachute.name.title() + events[name] = [] + events[name].append( + name + " Ejection Triggered at: {:.3f} s".format(trigger_time) + ) + events[name].append( + name + " Parachute Inflated at: {:.3f} s".format(open_time) + ) + events[name].append( + name + + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( + velocity + ) + ) + events[name].append( + name + + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( + altitude - flight.env.elevation + ) + ) + _events_registered = EventsRegistered(events_trace=events) + + _flight_data = FlightData( + initial_conditions=_initial_conditions, + numerical_integration_settings=_numerical_integration_settings, + surface_wind_conditions=_surface_wind_conditions, + launch_rail_conditions=_launch_rail_conditions, + out_of_rail_conditions=_out_of_rail_conditions, + burnout_conditions=_burnout_conditions, + apogee_conditions=_apogee_conditions, + maximum_values=_maximum_values, + impact_conditions=_impact_conditions, + events_registered=_events_registered, + ) - return flight_summary + flight_summary = FlightSummary(flight_data=_flight_data) + return flight_summary + except Exception as e: + raise HTTPException( + status_code=500, detail=f"Failed to simulate flight: {e}" + ) from e diff --git a/lib/controllers/motor.py b/lib/controllers/motor.py index 24393f9..a01eafb 100644 --- a/lib/controllers/motor.py +++ b/lib/controllers/motor.py @@ -1,16 +1,23 @@ -from fastapi import Response, status -from typing import Any, Dict, Union +from typing import Union +from fastapi import HTTPException, status from rocketpy.motors.solid_motor import SolidMotor from rocketpy.motors.liquid_motor import LiquidMotor from rocketpy.motors.hybrid_motor import HybridMotor -from rocketpy import TankGeometry, Fluid import jsonpickle from lib.models.motor import Motor, MotorKinds from lib.repositories.motor import MotorRepository -from lib.views.motor import MotorSummary, MotorData, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle +from lib.views.motor import ( + MotorSummary, + MotorData, + MotorCreated, + MotorUpdated, + MotorDeleted, + MotorPickle, +) -class MotorController(): + +class MotorController: """ Controller for the motor model. @@ -20,6 +27,7 @@ class MotorController(): Enables: - Create a rocketpy.Motor object from a Motor model object. """ + def __init__(self, motor: Motor, motor_kind): self.guard(motor, motor_kind) motor_core = { @@ -28,14 +36,15 @@ def __init__(self, motor: Motor, motor_kind): "nozzle_radius": motor.nozzle_radius, "dry_mass": motor.dry_mass, "dry_inertia": motor.dry_inertia, - "center_of_dry_mass_position": motor.center_of_dry_mass_position + "center_of_dry_mass_position": motor.center_of_dry_mass_position, } match motor_kind: case MotorKinds.liquid: rocketpy_motor = LiquidMotor(**motor_core) case MotorKinds.hybrid: - rocketpy_motor = HybridMotor(**motor_core, + rocketpy_motor = HybridMotor( + **motor_core, throat_radius=motor.throat_radius, grain_number=motor.grain_number, grain_density=motor.grain_density, @@ -43,10 +52,11 @@ def __init__(self, motor: Motor, motor_kind): grain_initial_inner_radius=motor.grain_initial_inner_radius, grain_initial_height=motor.grain_initial_height, grain_separation=motor.grain_separation, - grains_center_of_mass_position=motor.grains_center_of_mass_position + grains_center_of_mass_position=motor.grains_center_of_mass_position, ) case _: - rocketpy_motor = SolidMotor(**motor_core, + rocketpy_motor = SolidMotor( + **motor_core, grain_number=motor.grain_number, grain_density=motor.grain_density, grain_outer_radius=motor.grain_outer_radius, @@ -55,36 +65,45 @@ def __init__(self, motor: Motor, motor_kind): grains_center_of_mass_position=motor.grains_center_of_mass_position, grain_separation=motor.grain_separation, throat_radius=motor.throat_radius, - interpolation_method=motor.interpolation_method + interpolation_method=motor.interpolation_method, ) if motor_kind != MotorKinds.solid: for tank in motor.tanks: rocketpy_motor.add_tank(tank.tank, tank.position) - - self.motor_kind = motor_kind #tracks motor kind state + + self.motor_kind = motor_kind # tracks motor kind state self.rocketpy_motor = rocketpy_motor self.motor = motor - + def guard(self, motor: Motor, motor_kind): if motor_kind != MotorKinds.solid and motor.tanks is None: - return Response(status_code=status.HTTP_400_BAD_REQUEST, content="Tanks must be provided for liquid and hybrid motors.") + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="Tanks must be provided for liquid and hybrid motors.", + ) - async def create_motor(self) -> "Union[MotorCreated, Response]": + async def create_motor(self) -> "Union[MotorCreated, HTTPException]": """ Create a motor in the database. Returns: - Dict[str, str]: motor id. + MotorCreated: motor id. """ motor = MotorRepository(motor=self.motor) - successfully_created_motor = await motor.create_motor(motor_kind = self.motor_kind) - if successfully_created_motor: - return MotorCreated(motor_id=str(motor.motor_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + successfully_created_motor = await motor.create_motor( + motor_kind=self.motor_kind + ) + if not successfully_created_motor: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create motor.", + ) + + return MotorCreated(motor_id=str(motor.motor_id)) @staticmethod - async def get_motor(motor_id: int) -> "Union[Motor, Response]": + async def get_motor(motor_id: int) -> "Union[Motor, HTTPException]": """ Get a motor from the database. @@ -97,14 +116,16 @@ async def get_motor(motor_id: int) -> "Union[Motor, Response]": Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) + return successfully_read_motor @staticmethod - async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, Response]": + async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, HTTPException]": """ Get a rocketpy motor object encoded as jsonpickle string from the database. @@ -117,18 +138,24 @@ async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, Response]": Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) - successfully_read_rocketpy_motor = \ - MotorController(motor = successfully_read_motor, - motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor + successfully_read_rocketpy_motor = MotorController( + motor=successfully_read_motor, + motor_kind=MotorKinds(successfully_read_motor._motor_kind), + ).rocketpy_motor - return MotorPickle(jsonpickle_rocketpy_motor=jsonpickle.encode(successfully_read_rocketpy_motor)) + return MotorPickle( + jsonpickle_rocketpy_motor=jsonpickle.encode( + successfully_read_rocketpy_motor + ) + ) - async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, Response]": + async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, HTTPException]": """ Update a motor in the database. @@ -136,25 +163,30 @@ async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, Response]": motor_id (int): Motor id. Returns: - Dict[str, Any]: motor id and message. + MotorUpdated: motor id and message. Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) - successfully_updated_motor = \ - await MotorRepository(motor=self.motor, motor_id=motor_id).update_motor(motor_kind = self.motor_kind) + successfully_updated_motor = await MotorRepository( + motor=self.motor, motor_id=motor_id + ).update_motor(motor_kind=self.motor_kind) + if not successfully_updated_motor: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update motor.", + ) - if successfully_updated_motor: - return MotorUpdated(new_motor_id=str(successfully_updated_motor)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return MotorUpdated(new_motor_id=str(successfully_updated_motor)) @staticmethod - async def delete_motor(motor_id: int) -> "Union[MotorDeleted, Response]": + async def delete_motor(motor_id: int) -> "Union[MotorDeleted, HTTPException]": """ Delete a motor from the database. @@ -162,24 +194,30 @@ async def delete_motor(motor_id: int) -> "Union[MotorDeleted, Response]": motor_id (int): motor id. Returns: - Dict[str, str]: motor id and message. + MotorDeleted: motor id and message. Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) - successfully_deleted_motor = \ - await MotorRepository(motor_id=motor_id).delete_motor() - if successfully_deleted_motor: - return MotorDeleted(deleted_motor_id=str(motor_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + successfully_deleted_motor = await MotorRepository( + motor_id=motor_id + ).delete_motor() + if not successfully_deleted_motor: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete motor.", + ) + + return MotorDeleted(deleted_motor_id=str(motor_id)) @staticmethod - async def simulate(motor_id: int) -> "Union[MotorSummary, Response]": + async def simulate(motor_id: int) -> "Union[MotorSummary, HTTPException]": """ Simulate a rocketpy motor. @@ -192,39 +230,48 @@ async def simulate(motor_id: int) -> "Union[MotorSummary, Response]": Raises: HTTP 404 Not Found: If the motor does not exist in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - motor = MotorController(motor=successfully_read_motor, - motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor - motor_simulation_numbers = MotorData( - total_burning_time="Total Burning Time: " + str(motor.burn_out_time) + " s", - - total_propellant_mass="Total Propellant Mass: " - + "{:.3f}".format(motor.propellant_initial_mass) - + " kg", - - average_propellant_exhaust_velocity="Average Propellant Exhaust Velocity: " - + "{:.3f}".format( - motor.exhaust_velocity.average(*motor.burn_time) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." ) - + " m/s", - - average_thrust="Average Thrust: " + "{:.3f}".format(motor.average_thrust) + " N", - - maximum_thrust="Maximum Thrust: " - + str(motor.max_thrust) - + " N at " - + str(motor.max_thrust_time) - + " s after ignition.", - total_impulse="Total Impulse: " + "{:.3f}".format(motor.total_impulse) + " Ns\n" - ) - #motor_simulation_plots = MotorPlots( - # motor.thrust.plot(lower=lower_limit, upper=upper_limit) - #) - - motor_summary = MotorSummary( motor_data = motor_simulation_numbers ) #, plots=motor_simulation_plots ) - return motor_summary + try: + motor = MotorController( + motor=successfully_read_motor, + motor_kind=MotorKinds(successfully_read_motor._motor_kind), + ).rocketpy_motor + motor_simulation_numbers = MotorData( + total_burning_time="Total Burning Time: " + + str(motor.burn_out_time) + + " s", + total_propellant_mass="Total Propellant Mass: " + + "{:.3f}".format(motor.propellant_initial_mass) + + " kg", + average_propellant_exhaust_velocity="Average Propellant Exhaust Velocity: " + + "{:.3f}".format(motor.exhaust_velocity.average(*motor.burn_time)) + + " m/s", + average_thrust="Average Thrust: " + + "{:.3f}".format(motor.average_thrust) + + " N", + maximum_thrust="Maximum Thrust: " + + str(motor.max_thrust) + + " N at " + + str(motor.max_thrust_time) + + " s after ignition.", + total_impulse="Total Impulse: " + + "{:.3f}".format(motor.total_impulse) + + " Ns\n", + ) + # motor_simulation_plots = MotorPlots( + # motor.thrust.plot(lower=lower_limit, upper=upper_limit) + # ) + motor_summary = MotorSummary( + motor_data=motor_simulation_numbers + ) # , plots=motor_simulation_plots ) + return motor_summary + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to simulate motor: {e}", + ) from e diff --git a/lib/controllers/rocket.py b/lib/controllers/rocket.py index 04b502f..1885709 100644 --- a/lib/controllers/rocket.py +++ b/lib/controllers/rocket.py @@ -1,9 +1,10 @@ -from typing import Dict, Any, Union -import jsonpickle +from typing import Union import ast +import jsonpickle + +from fastapi import HTTPException, status -from fastapi import Response, status -#from inspect import getsourcelines +# from inspect import getsourcelines from rocketpy.rocket.parachute import Parachute as RocketpyParachute from rocketpy.rocket.rocket import Rocket as RocketpyRocket @@ -13,13 +14,25 @@ from lib.controllers.motor import MotorController from lib.models.rocket import Rocket, RocketOptions -from lib.models.motor import MotorKinds +from lib.models.motor import MotorKinds from lib.models.aerosurfaces import NoseCone, TrapezoidalFins, Tail from lib.models.parachute import Parachute from lib.repositories.rocket import RocketRepository -from lib.views.rocket import InertiaDetails, RocketGeometricalParameters, RocketAerodynamicsQuantities, ParachuteData, RocketData, RocketSummary, RocketCreated, RocketUpdated, RocketDeleted, RocketPickle - -class RocketController(): +from lib.views.rocket import ( + InertiaDetails, + RocketGeometricalParameters, + RocketAerodynamicsQuantities, + ParachuteData, + RocketData, + RocketSummary, + RocketCreated, + RocketUpdated, + RocketDeleted, + RocketPickle, +) + + +class RocketController: """ Controller for the Rocket model. @@ -29,56 +42,65 @@ class RocketController(): Enables: create a RocketpyRocket object from a Rocket model object. """ + def __init__(self, rocket: Rocket, rocket_option, motor_kind): rocketpy_rocket = RocketpyRocket( - radius=rocket.radius, - mass=rocket.mass, - inertia=rocket.inertia, - power_off_drag=f"lib/data/{rocket_option.value.lower()}/powerOffDragCurve.csv", - power_on_drag=f"lib/data/{rocket_option.value.lower()}/powerOnDragCurve.csv", - center_of_mass_without_motor=rocket.center_of_mass_without_motor, - coordinate_system_orientation=rocket.coordinate_system_orientation + radius=rocket.radius, + mass=rocket.mass, + inertia=rocket.inertia, + power_off_drag=f"lib/data/{rocket_option.value.lower()}/powerOffDragCurve.csv", + power_on_drag=f"lib/data/{rocket_option.value.lower()}/powerOnDragCurve.csv", + center_of_mass_without_motor=rocket.center_of_mass_without_motor, + coordinate_system_orientation=rocket.coordinate_system_orientation, ) - #RailButtons - rocketpy_rocket.set_rail_buttons(upper_button_position=rocket.rail_buttons.upper_button_position, - lower_button_position=rocket.rail_buttons.lower_button_position, - angular_position=rocket.rail_buttons.angular_position) - rocketpy_rocket.add_motor(MotorController(rocket.motor, motor_kind).rocketpy_motor, - rocket.motor_position) + # RailButtons + rocketpy_rocket.set_rail_buttons( + upper_button_position=rocket.rail_buttons.upper_button_position, + lower_button_position=rocket.rail_buttons.lower_button_position, + angular_position=rocket.rail_buttons.angular_position, + ) + rocketpy_rocket.add_motor( + MotorController(rocket.motor, motor_kind).rocketpy_motor, + rocket.motor_position, + ) - #NoseCone + # NoseCone nose = self.NoseConeController(rocket.nose).rocketpy_nose rocketpy_rocket.aerodynamic_surfaces.add(nose, nose.position) rocketpy_rocket.evaluate_static_margin() - #FinSet - #TBD: re-write this to match overall fins not only TrapezoidalFins + # FinSet + # TBD: re-write this to match overall fins not only TrapezoidalFins finset = self.TrapezoidalFinsController(rocket.fins).rocketpy_finset rocketpy_rocket.aerodynamic_surfaces.add(finset, finset.position) rocketpy_rocket.evaluate_static_margin() - #Tail + # Tail tail = self.TailController(rocket.tail).rocketpy_tail rocketpy_rocket.aerodynamic_surfaces.add(tail, tail.position) rocketpy_rocket.evaluate_static_margin() - #Parachutes + # Parachutes for p in range(len(rocket.parachutes)): parachute_trigger = rocket.parachutes[p].triggers[0] if self.ParachuteController.check_trigger(parachute_trigger): - rocket.parachutes[p].triggers[0] = compile(parachute_trigger, '', 'eval') - parachute = self.ParachuteController(rocket.parachutes, p).rocketpy_parachute + rocket.parachutes[p].triggers[0] = compile( + parachute_trigger, "", "eval" + ) + parachute = self.ParachuteController( + rocket.parachutes, p + ).rocketpy_parachute rocketpy_rocket.parachutes.append(parachute) else: print("Parachute trigger not valid. Skipping parachute.") continue - self.rocket_option = rocket_option #tracks rocket option state + self.rocket_option = rocket_option # tracks rocket option state self.rocketpy_rocket = rocketpy_rocket self.rocket = rocket - class NoseConeController(): + class NoseConeController: """ Controller for the NoseCone model. @@ -88,18 +110,19 @@ class NoseConeController(): Enables: - Create a rocketpy.AeroSurface.NoseCone object from a NoseCone model object. """ + def __init__(self, nose: NoseCone): rocketpy_nose = RocketpyNoseCone( - length=nose.length, - kind=nose.kind, - base_radius=nose.base_radius, - rocket_radius=nose.rocket_radius + length=nose.length, + kind=nose.kind, + base_radius=nose.base_radius, + rocket_radius=nose.rocket_radius, ) rocketpy_nose.position = nose.position self.rocketpy_nose = rocketpy_nose self.nose = nose - class TrapezoidalFinsController(): + class TrapezoidalFinsController: """ Controller for the TrapezoidalFins model. @@ -109,21 +132,22 @@ class TrapezoidalFinsController(): Enables: - Create a rocketpy.AeroSurface.TrapezoidalFins object from a TrapezoidalFins model object. """ + def __init__(self, trapezoidal_fins: TrapezoidalFins): rocketpy_finset = RocketpyTrapezoidalFins( - n=trapezoidal_fins.n, - root_chord=trapezoidal_fins.root_chord, - tip_chord=trapezoidal_fins.tip_chord, - span=trapezoidal_fins.span, - cant_angle=trapezoidal_fins.cant_angle, - rocket_radius=trapezoidal_fins.radius, - airfoil=trapezoidal_fins.airfoil + n=trapezoidal_fins.n, + root_chord=trapezoidal_fins.root_chord, + tip_chord=trapezoidal_fins.tip_chord, + span=trapezoidal_fins.span, + cant_angle=trapezoidal_fins.cant_angle, + rocket_radius=trapezoidal_fins.radius, + airfoil=trapezoidal_fins.airfoil, ) rocketpy_finset.position = trapezoidal_fins.position self.rocketpy_finset = rocketpy_finset self.trapezoidal_fins = trapezoidal_fins - class TailController(): + class TailController: """ Controller for the Tail model. @@ -133,18 +157,19 @@ class TailController(): Enables: - Create a rocketpy.AeroSurface.Tail object from a Tail model object. """ + def __init__(self, tail: Tail): rocketpy_tail = RocketpyTail( - top_radius=tail.top_radius, - bottom_radius=tail.bottom_radius, - length=tail.length, - rocket_radius=tail.radius + top_radius=tail.top_radius, + bottom_radius=tail.bottom_radius, + length=tail.length, + rocket_radius=tail.radius, ) rocketpy_tail.position = tail.position self.rocketpy_tail = rocketpy_tail self.tail = tail - class ParachuteController(): + class ParachuteController: """ Controller for the Parachute model. @@ -154,14 +179,15 @@ class ParachuteController(): Enables: - Create a RocketpyParachute.Parachute object from a Parachute model object. """ + def __init__(self, parachute: Parachute, p: int): rocketpy_parachute = RocketpyParachute( - name=parachute[p].name[0], - cd_s=parachute[p].cd_s[0], - trigger=eval(parachute[p].triggers[0]), - sampling_rate=parachute[p].sampling_rate[0], - lag=parachute[p].lag[0], - noise=parachute[p].noise[0] + name=parachute[p].name[0], + cd_s=parachute[p].cd_s[0], + trigger=eval(parachute[p].triggers[0]), + sampling_rate=parachute[p].sampling_rate[0], + lag=parachute[p].lag[0], + noise=parachute[p].noise[0], ) self.rocketpy_parachute = rocketpy_parachute self.parachute = parachute @@ -180,7 +206,7 @@ def check_trigger(expression: str) -> bool: # Parsing the expression into an AST try: - parsed_expression = ast.parse(expression, mode='eval') + parsed_expression = ast.parse(expression, mode="eval") except SyntaxError: print("Invalid syntax.") return False @@ -189,8 +215,10 @@ def check_trigger(expression: str) -> bool: if isinstance(parsed_expression.body, ast.Constant): return True # Name case (supported after beta v1) - if isinstance(parsed_expression.body, ast.Name) \ - and parsed_expression.body.id == "apogee": + if ( + isinstance(parsed_expression.body, ast.Name) + and parsed_expression.body.id == "apogee" + ): global apogee apogee = "apogee" return True @@ -225,21 +253,27 @@ def check_trigger(expression: str) -> bool: return False return True - async def create_rocket(self) -> "Union[RocketCreated, Response]": + async def create_rocket(self) -> "Union[RocketCreated, HTTPException]": """ Create a rocket in the database. Returns: - Dict[str, str]: Rocket id. + RocketCreated: Rocket id. """ rocket = RocketRepository(rocket=self.rocket) - successfully_created_rocket = await rocket.create_rocket(rocket_option = self.rocket_option) - if successfully_created_rocket: - return RocketCreated(rocket_id=str(rocket.rocket_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + successfully_created_rocket = await rocket.create_rocket( + rocket_option=self.rocket_option + ) + if not successfully_created_rocket: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create rocket.", + ) + + return RocketCreated(rocket_id=str(rocket.rocket_id)) @staticmethod - async def get_rocket(rocket_id: int) -> "Union[Rocket, Response]": + async def get_rocket(rocket_id: int) -> "Union[Rocket, HTTPException]": """ Get a rocket from the database. @@ -252,14 +286,20 @@ async def get_rocket(rocket_id: int) -> "Union[Rocket, Response]": Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) + return successfully_read_rocket @staticmethod - async def get_rocketpy_rocket(rocket_id: int) -> "Union[RocketPickle, Response]": + async def get_rocketpy_rocket( + rocket_id: int, + ) -> "Union[RocketPickle, HTTPException]": """ Get a rocketpy rocket object encoded as jsonpickle string from the database. @@ -272,19 +312,29 @@ async def get_rocketpy_rocket(rocket_id: int) -> "Union[RocketPickle, Response]" Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) - successfully_read_rocketpy_rocket = \ - RocketController(rocket=successfully_read_rocket, - rocket_option = RocketOptions(successfully_read_rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket + successfully_read_rocketpy_rocket = RocketController( + rocket=successfully_read_rocket, + rocket_option=RocketOptions(successfully_read_rocket._rocket_option), + motor_kind=MotorKinds(successfully_read_rocket.motor._motor_kind), + ).rocketpy_rocket - return RocketPickle(jsonpickle_rocketpy_rocket=jsonpickle.encode(successfully_read_rocketpy_rocket)) + return RocketPickle( + jsonpickle_rocketpy_rocket=jsonpickle.encode( + successfully_read_rocketpy_rocket + ) + ) - async def update_rocket(self, rocket_id: int) -> "Union[RocketUpdated, Response]": + async def update_rocket( + self, rocket_id: int + ) -> "Union[RocketUpdated, HTTPException]": """ Update a rocket in the database. @@ -292,25 +342,32 @@ async def update_rocket(self, rocket_id: int) -> "Union[RocketUpdated, Response] rocket_id (int): rocket id. Returns: - Dict[str, Any]: rocket id and message. + RocketUpdated: rocket id and message. Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) - successfully_updated_rocket = \ - await RocketRepository(rocket=self.rocket, rocket_id=rocket_id).update_rocket(rocket_option = self.rocket_option) + successfully_updated_rocket = await RocketRepository( + rocket=self.rocket, rocket_id=rocket_id + ).update_rocket(rocket_option=self.rocket_option) + if not successfully_updated_rocket: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update rocket.", + ) - if successfully_updated_rocket: - return RocketUpdated(new_rocket_id=str(successfully_updated_rocket)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return RocketUpdated(new_rocket_id=str(successfully_updated_rocket)) @staticmethod - async def delete_rocket(rocket_id: int) -> "Union[RocketDeleted, Response]": + async def delete_rocket(rocket_id: int) -> "Union[RocketDeleted, HTTPException]": """ Delete a rocket from the database. @@ -318,24 +375,32 @@ async def delete_rocket(rocket_id: int) -> "Union[RocketDeleted, Response]": rocket_id (int): Rocket id. Returns: - Dict[str, str]: Rocket id and message. + RocketDeleted: Rocket id and message. Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) + + successfully_deleted_rocket = await RocketRepository( + rocket_id=rocket_id + ).delete_rocket() + if not successfully_deleted_rocket: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete rocket.", + ) - successfully_deleted_rocket = \ - await RocketRepository(rocket_id=rocket_id).delete_rocket() - if successfully_deleted_rocket: - return RocketDeleted(deleted_rocket_id=str(rocket_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return RocketDeleted(deleted_rocket_id=str(rocket_id)) @staticmethod - async def simulate(rocket_id: int) -> "Union[RocketSummary, Response]": + async def simulate(rocket_id: int) -> "Union[RocketSummary, HTTPException]": """ Simulate a rocket rocket. @@ -348,112 +413,148 @@ async def simulate(rocket_id: int) -> "Union[RocketSummary, Response]": Raises: HTTP 404 Not Found: If the rocket does not exist in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - rocket = RocketController(rocket=successfully_read_rocket, - rocket_option = RocketOptions(successfully_read_rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket - - _inertia_details = InertiaDetails( - rocket_mass_without_propellant = "Rocket Mass: {:.3f} kg (No Propellant)".format(rocket.mass), - rocket_mass_with_propellant = "Rocket Mass: {:.3f} kg (With Propellant)".format(rocket.total_mass(0)), - rocket_inertia_with_motor_without_propellant = [ - "Rocket Inertia (with motor, but without propellant) 11: {:.3f} kg*m2".format(rocket.dry_I_11), - "Rocket Inertia (with motor, but without propellant) 22: {:.3f} kg*m2".format(rocket.dry_I_22), - "Rocket Inertia (with motor, but without propellant) 33: {:.3f} kg*m2".format(rocket.dry_I_33), - "Rocket Inertia (with motor, but without propellant) 12: {:.3f} kg*m2".format(rocket.dry_I_12), - "Rocket Inertia (with motor, but without propellant) 13: {:.3f} kg*m2".format(rocket.dry_I_13), - "Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2".format(rocket.dry_I_23) - ] - ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) - _rocket_geometrical_parameters = RocketGeometricalParameters( - rocket_maximum_radius = "Rocket Maximum Radius: " + str(rocket.radius) + " m", - rocket_frontal_area = "Rocket Frontal Area: " + "{:.6f}".format(rocket.area) + " m2", + try: + rocket = RocketController( + rocket=successfully_read_rocket, + rocket_option=RocketOptions(successfully_read_rocket._rocket_option), + motor_kind=MotorKinds(successfully_read_rocket.motor._motor_kind), + ).rocketpy_rocket + + _inertia_details = InertiaDetails( + rocket_mass_without_propellant="Rocket Mass: {:.3f} kg (No Propellant)".format( + rocket.mass + ), + rocket_mass_with_propellant="Rocket Mass: {:.3f} kg (With Propellant)".format( + rocket.total_mass(0) + ), + rocket_inertia_with_motor_without_propellant=[ + "Rocket Inertia (with motor, but without propellant) 11: {:.3f} kg*m2".format( + rocket.dry_I_11 + ), + "Rocket Inertia (with motor, but without propellant) 22: {:.3f} kg*m2".format( + rocket.dry_I_22 + ), + "Rocket Inertia (with motor, but without propellant) 33: {:.3f} kg*m2".format( + rocket.dry_I_33 + ), + "Rocket Inertia (with motor, but without propellant) 12: {:.3f} kg*m2".format( + rocket.dry_I_12 + ), + "Rocket Inertia (with motor, but without propellant) 13: {:.3f} kg*m2".format( + rocket.dry_I_13 + ), + "Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2".format( + rocket.dry_I_23 + ), + ], + ) - rocket_codm_nozzle_exit_distance = "Rocket Center of Dry Mass - Nozzle Exit Distance: " - + "{:.3f} m".format( - abs( - rocket.center_of_dry_mass_position - rocket.motor_position - ) - ), + _rocket_geometrical_parameters = RocketGeometricalParameters( + rocket_maximum_radius="Rocket Maximum Radius: " + + str(rocket.radius) + + " m", + rocket_frontal_area="Rocket Frontal Area: " + + "{:.6f}".format(rocket.area) + + " m2", + rocket_codm_nozzle_exit_distance="Rocket Center of Dry Mass - Nozzle Exit Distance: " + + "{:.3f} m".format( + abs(rocket.center_of_dry_mass_position - rocket.motor_position) + ), + rocket_codm_center_of_propellant_mass="Rocket Center of Dry Mass - Center of Propellant Mass: " + + "{:.3f} m".format( + abs( + rocket.center_of_propellant_position(0) + - rocket.center_of_dry_mass_position + ) + ), + rocket_codm_loaded_center_of_mass="Rocket Center of Mass - Rocket Loaded Center of Mass: " + + "{:.3f} m".format( + abs(rocket.center_of_mass(0) - rocket.center_of_dry_mass_position) + ), + ) - rocket_codm_center_of_propellant_mass = "Rocket Center of Dry Mass - Center of Propellant Mass: " - + "{:.3f} m".format( - abs( - rocket.center_of_propellant_position(0) - - rocket.center_of_dry_mass_position + _aerodynamics_lift_coefficient_derivatives = {} + for surface, _position in rocket.aerodynamic_surfaces: + name = surface.name + _aerodynamics_lift_coefficient_derivatives[name] = [] + _aerodynamics_lift_coefficient_derivatives[name].append( + name + + " Lift Coefficient Derivative: {:.3f}".format(surface.clalpha(0)) + + "/rad" ) - ), - rocket_codm_loaded_center_of_mass = "Rocket Center of Mass - Rocket Loaded Center of Mass: " - + "{:.3f} m".format( - abs( - rocket.center_of_mass(0) - - rocket.center_of_dry_mass_position + _aerodynamics_center_of_pressure = {} + for surface, _position in rocket.aerodynamic_surfaces: + name = surface.name + cpz = surface.cp[2] + _aerodynamics_center_of_pressure[name] = [] + _aerodynamics_center_of_pressure[name].append( + name + " Center of Pressure to CM: {:.3f}".format(cpz) + " m" ) - ) - ) - _aerodynamics_lift_coefficient_derivatives = {} - for surface, _position in rocket.aerodynamic_surfaces: - name = surface.name - _aerodynamics_lift_coefficient_derivatives[name] = [] - _aerodynamics_lift_coefficient_derivatives[name].append( - name + " Lift Coefficient Derivative: {:.3f}".format(surface.clalpha(0)) + "/rad" + _rocket_aerodynamics_quantities = RocketAerodynamicsQuantities( + aerodynamics_lift_coefficient_derivatives=_aerodynamics_lift_coefficient_derivatives, + aerodynamics_center_of_pressure=_aerodynamics_center_of_pressure, + distance_cop_to_codm="Distance from Center of Pressure to Center of Dry Mass: " + + "{:.3f}".format(rocket.center_of_mass(0) - rocket.cp_position) + + " m", + initial_static_margin="Initial Static Margin: " + + "{:.3f}".format(rocket.static_margin(0)) + + " c", + final_static_margin="Final Static Margin: " + + "{:.3f}".format(rocket.static_margin(rocket.motor.burn_out_time)) + + " c", ) - _aerodynamics_center_of_pressure = {} - for surface, _position in rocket.aerodynamic_surfaces: - name = surface.name - cpz = surface.cp[2] - _aerodynamics_center_of_pressure[name] = [] - _aerodynamics_center_of_pressure[name].append( - name + " Center of Pressure to CM: {:.3f}".format(cpz) + " m" + _parachute_details = {} + _parachute_ejection_signal_trigger = {} + _parachute_ejection_system_refresh_rate = {} + _parachute_lag = {} + for chute in rocket.parachutes: + _parachute_details[chute.name] = chute.__str__() + + if chute.trigger.__name__ == "": + # line = getsourcelines(chute.trigger)[0][0] + # _parachute_ejection_signal_trigger[chute.name] = "Ejection signal trigger: " + line.split("lambda ")[1].split(",")[0].split("\n")[0] + pass + + else: + _parachute_ejection_signal_trigger[chute.name] = ( + "Ejection signal trigger: " + chute.trigger.__name__ + ) + _parachute_ejection_system_refresh_rate[ + chute.name + ] = "Ejection system refresh rate: {chute.sampling_rate:.3f} Hz" + _parachute_lag[ + chute.name + ] = "Time between ejection signal is triggered and the parachute is fully opened: {chute.lag:.1f} s\n" + + _parachute_data = ParachuteData( + parachute_details=_parachute_details, + # parachute_ejection_signal_trigger = _parachute_ejection_signal_trigger, + parachute_ejection_system_refresh_rate=_parachute_ejection_system_refresh_rate, + parachute_lag=_parachute_lag, ) - _rocket_aerodynamics_quantities = RocketAerodynamicsQuantities( - aerodynamics_lift_coefficient_derivatives = _aerodynamics_lift_coefficient_derivatives, - aerodynamics_center_of_pressure = _aerodynamics_center_of_pressure, - distance_cop_to_codm = "Distance from Center of Pressure to Center of Dry Mass: " + "{:.3f}".format(rocket.center_of_mass(0) - rocket.cp_position) + " m", - initial_static_margin = "Initial Static Margin: " + "{:.3f}".format(rocket.static_margin(0)) + " c", - final_static_margin ="Final Static Margin: " + "{:.3f}".format(rocket.static_margin(rocket.motor.burn_out_time)) + " c" - ) - - _parachute_details = {} - _parachute_ejection_signal_trigger = {} - _parachute_ejection_system_refresh_rate = {} - _parachute_lag = {} - for chute in rocket.parachutes: - _parachute_details[chute.name] = chute.__str__() - - if chute.trigger.__name__ == "": - #line = getsourcelines(chute.trigger)[0][0] - #_parachute_ejection_signal_trigger[chute.name] = "Ejection signal trigger: " + line.split("lambda ")[1].split(",")[0].split("\n")[0] - pass - - else: - _parachute_ejection_signal_trigger[chute.name] = "Ejection signal trigger: " + chute.trigger.__name__ - _parachute_ejection_system_refresh_rate[chute.name] = "Ejection system refresh rate: {chute.sampling_rate:.3f} Hz" - _parachute_lag[chute.name] = "Time between ejection signal is triggered and the parachute is fully opened: {chute.lag:.1f} s\n" - - _parachute_data = ParachuteData( - parachute_details = _parachute_details, - #parachute_ejection_signal_trigger = _parachute_ejection_signal_trigger, - parachute_ejection_system_refresh_rate = _parachute_ejection_system_refresh_rate, - parachute_lag = _parachute_lag - ) - - _rocket_data = RocketData( - inertia_details = _inertia_details, - rocket_geometrical_parameters = _rocket_geometrical_parameters, - rocket_aerodynamics_quantities = _rocket_aerodynamics_quantities, - parachute_data = _parachute_data - ) - - rocket_summary = RocketSummary( rocket_data = _rocket_data ) + _rocket_data = RocketData( + inertia_details=_inertia_details, + rocket_geometrical_parameters=_rocket_geometrical_parameters, + rocket_aerodynamics_quantities=_rocket_aerodynamics_quantities, + parachute_data=_parachute_data, + ) - return rocket_summary + rocket_summary = RocketSummary(rocket_data=_rocket_data) + return rocket_summary + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to simulate rocket: {e}", + ) from e diff --git a/lib/models/aerosurfaces.py b/lib/models/aerosurfaces.py index 490c99f..73284ea 100644 --- a/lib/models/aerosurfaces.py +++ b/lib/models/aerosurfaces.py @@ -1,11 +1,13 @@ from typing import Optional from pydantic import BaseModel + class RailButtons(BaseModel, frozen=True): upper_button_position: Optional[float] = -0.5 lower_button_position: Optional[float] = 0.2 angular_position: Optional[float] = 45 + class NoseCone(BaseModel, frozen=True): length: float = 0.55829 kind: str = "vonKarman" @@ -13,6 +15,7 @@ class NoseCone(BaseModel, frozen=True): base_radius: float = 0.0635 rocket_radius: float = 0.0635 + class Fins(BaseModel, frozen=True): n: int = 4 root_chord: float = 0.12 @@ -23,10 +26,12 @@ class Fins(BaseModel, frozen=True): radius: float = 0.0635 airfoil: str = "" + class TrapezoidalFins(Fins, frozen=True): def __init__(self): super().__init__() + class Tail(BaseModel, frozen=True): top_radius: float = 0.0635 bottom_radius: float = 0.0435 diff --git a/lib/models/environment.py b/lib/models/environment.py index 4a1a5bc..0ce9afe 100644 --- a/lib/models/environment.py +++ b/lib/models/environment.py @@ -2,12 +2,15 @@ from typing import Optional from pydantic import BaseModel + class Env(BaseModel, frozen=True): latitude: float = 0 longitude: float = 0 elevation: Optional[int] = 1400 - #Opional parameters - atmospheric_model_type: Optional[str] = 'standard_atmosphere' - atmospheric_model_file: Optional[str] = 'GFS' - date: Optional[datetime.datetime] = datetime.datetime.today() + datetime.timedelta(days=1) + # Opional parameters + atmospheric_model_type: Optional[str] = "standard_atmosphere" + atmospheric_model_file: Optional[str] = "GFS" + date: Optional[datetime.datetime] = datetime.datetime.today() + datetime.timedelta( + days=1 + ) diff --git a/lib/models/flight.py b/lib/models/flight.py index 20182bd..61343cc 100644 --- a/lib/models/flight.py +++ b/lib/models/flight.py @@ -2,6 +2,7 @@ from lib.models.rocket import Rocket from lib.models.environment import Env + class Flight(BaseModel, frozen=True): environment: Env = Env() rocket: Rocket = Rocket() diff --git a/lib/models/motor.py b/lib/models/motor.py index fb57453..7a2a451 100644 --- a/lib/models/motor.py +++ b/lib/models/motor.py @@ -1,29 +1,40 @@ -from rocketpy import LevelBasedTank, MassBasedTank, MassFlowRateBasedTank, UllageBasedTank, SolidMotor, LiquidMotor, HybridMotor, TankGeometry, Function -from typing import Optional, Tuple, List, Dict from enum import Enum +from typing import Optional, Tuple, List +from rocketpy import ( + LevelBasedTank, + MassBasedTank, + MassFlowRateBasedTank, + UllageBasedTank, + TankGeometry, +) from pydantic import BaseModel, PrivateAttr + class MotorKinds(str, Enum): hybrid: str = "Hybrid" solid: str = "Solid" liquid: str = "Liquid" + class MotorEngines(str, Enum): cesaroni: str = "Cesaroni_M1670" custom: str = "Custom" + class TankKinds(str, Enum): level: str = "Level" mass: str = "Mass" mass_flow: str = "MassFlow" ullage: str = "Ullage" + class TankFluids(BaseModel, frozen=True): name: str = "FluidName" density: float = 100.0 + class MotorTank(BaseModel, frozen=True): - #Required parameters + # Required parameters geometry: "List[Tuple[Tuple[float,float],float]]" = [((0, 5), 1), ((5, 10), 2)] tank_kind: TankKinds = TankKinds.mass_flow gas: TankFluids = TankFluids() @@ -31,8 +42,8 @@ class MotorTank(BaseModel, frozen=True): name: str = "Tank" flux_time: "List[float]" = [0, 8] position: float = 1.0 - - #Optional parameters + + # Optional parameters discretize: Optional[int] = 100 liquid_height: Optional[float] = 0.5 liquid_mass: Optional[float] = 5.0 @@ -49,27 +60,29 @@ def __init__(self, **kwargs): super().__init__(**kwargs) tank_core = { "name": self.name, - "geometry": TankGeometry(geometry_dict = { t:f for t,f in self.geometry }), + "geometry": TankGeometry(geometry_dict=dict(self.geometry)), "flux_time": self.flux_time, "gas": self.gas, "liquid": self.liquid, - "discretize": self.discretize + "discretize": self.discretize, } match self.tank_kind: case TankKinds.level: tank = LevelBasedTank(**tank_core, liquid_height=self.liquid_height) case TankKinds.mass: - tank = MassBasedTank(**tank_core, - liquid_mass=self.liquid_mass, gas_mass=self.gas_mass) + tank = MassBasedTank( + **tank_core, liquid_mass=self.liquid_mass, gas_mass=self.gas_mass + ) case TankKinds.mass_flow: - tank = MassFlowRateBasedTank(**tank_core, - gas_mass_flow_rate_in=self.gas_mass_flow_rate_in, + tank = MassFlowRateBasedTank( + **tank_core, + gas_mass_flow_rate_in=self.gas_mass_flow_rate_in, gas_mass_flow_rate_out=self.gas_mass_flow_rate_out, liquid_mass_flow_rate_in=self.liquid_mass_flow_rate_in, liquid_mass_flow_rate_out=self.liquid_mass_flow_rate_out, initial_liquid_mass=self.initial_liquid_mass, - initial_gas_mass=self.initial_gas_mass + initial_gas_mass=self.initial_gas_mass, ) case TankKinds.ullage: tank = UllageBasedTank(**tank_core, ullage=self.ullage) @@ -80,10 +93,11 @@ def __hash__(self): temp = str(temp) return hash(temp) + class Motor(BaseModel, frozen=True): - #TBD: thrust_source must be the id of a previously uploaded .eng file and a list of "default" files must be provided in the api docs + # TBD: thrust_source must be the id of a previously uploaded .eng file and a list of "default" files must be provided in the api docs - #Required parameters + # Required parameters thrust_source: MotorEngines = MotorEngines.cesaroni burn_time: float = 3.9 nozzle_radius: float = 0.033 @@ -92,7 +106,7 @@ class Motor(BaseModel, frozen=True): center_of_dry_mass_position: float = 0.317 _motor_kind: MotorKinds = PrivateAttr() - #Optional parameters + # Optional parameters tanks: Optional["List[MotorTank]"] = [MotorTank()] grain_number: Optional[int] = 5 grain_density: Optional[float] = 1815 @@ -105,7 +119,7 @@ class Motor(BaseModel, frozen=True): interpolation_method: Optional[str] = "linear" coordinate_system_orientation: Optional[str] = "nozzle_to_combustion_chamber" - def __init__(self, motor_kind = MotorKinds.solid, **kwargs): + def __init__(self, motor_kind=MotorKinds.solid, **kwargs): super().__init__(**kwargs) self._motor_kind = motor_kind diff --git a/lib/models/parachute.py b/lib/models/parachute.py index f1106a3..12bf480 100644 --- a/lib/models/parachute.py +++ b/lib/models/parachute.py @@ -1,23 +1,29 @@ from typing import List, Tuple from pydantic import BaseModel + class Parachute(BaseModel, frozen=True): - name: "List[str]" = ["Main","Drogue"] + name: "List[str]" = ["Main", "Drogue"] cd_s: "List[float]" = [10, 1] lag: "List[float]" = [1.5, 1.5] sampling_rate: "List[int]" = [105, 105] noise: "List[Tuple[float, float, float]]" = [(0, 8.3, 0.5), (0, 8.3, 0.5)] - triggers: "List[str]" = ["lambda p, h, y: y[5] < 0 and h < 800", "lambda p, h, y: y[5] < 0"] + triggers: "List[str]" = [ + "lambda p, h, y: y[5] < 0 and h < 800", + "lambda p, h, y: y[5] < 0", + ] def __hash__(self): - return hash(( - tuple(self.name), - tuple(self.cd_s), - tuple(self.sampling_rate), - tuple(self.lag), - tuple(self.noise), - tuple(self.triggers), - )) + return hash( + ( + tuple(self.name), + tuple(self.cd_s), + tuple(self.sampling_rate), + tuple(self.lag), + tuple(self.noise), + tuple(self.triggers), + ) + ) def __getitem__(self, idx): if isinstance(idx, slice): diff --git a/lib/models/rocket.py b/lib/models/rocket.py index c31abda..09d0172 100644 --- a/lib/models/rocket.py +++ b/lib/models/rocket.py @@ -1,16 +1,18 @@ +from enum import Enum from typing import Optional, Tuple, List from pydantic import BaseModel, PrivateAttr -from enum import Enum from lib.models.motor import Motor from lib.models.aerosurfaces import Fins, NoseCone, Tail, RailButtons from lib.models.parachute import Parachute + class RocketOptions(str, Enum): calisto: str = "Calisto" custom: str = "Custom" + class Rocket(BaseModel, frozen=True): - #Required parameters + # Required parameters rail_buttons: RailButtons = RailButtons() motor: Motor = Motor() nose: NoseCone = NoseCone() @@ -22,23 +24,23 @@ class Rocket(BaseModel, frozen=True): radius: float = 0.0632 mass: float = 16.235 motor_position: float = -1.255 - power_off_drag: "List[Tuple[float, float]]" = [ - (0.01,0.333865758), - (0.02,0.394981721), - (0.03,0.407756063) + power_off_drag: "List[Tuple[float, float]]" = [ + (0.01, 0.333865758), + (0.02, 0.394981721), + (0.03, 0.407756063), ] - power_on_drag: "List[Tuple[float, float]]" = [ - (0.01,0.333865758), - (0.02,0.394981721), - (0.03,0.407756063) + power_on_drag: "List[Tuple[float, float]]" = [ + (0.01, 0.333865758), + (0.02, 0.394981721), + (0.03, 0.407756063), ] _rocket_option: RocketOptions = PrivateAttr() - #Optional parameters - #TBD: a list of possible tailToNose values must be provided in the api docs + # Optional parameters + # TBD: a list of possible tailToNose values must be provided in the api docs coordinate_system_orientation: Optional[str] = "tail_to_nose" - def __init__(self, rocket_option = RocketOptions.calisto, **kwargs): + def __init__(self, rocket_option=RocketOptions.calisto, **kwargs): super().__init__(**kwargs) self._rocket_option = rocket_option diff --git a/lib/repositories/environment.py b/lib/repositories/environment.py index ddc9dbc..8632b65 100644 --- a/lib/repositories/environment.py +++ b/lib/repositories/environment.py @@ -1,8 +1,9 @@ +from typing import Union from pymongo.results import InsertOneResult from pymongo.results import DeleteResult from lib.models.environment import Env from lib.repositories.repo import Repository -from typing import Union + class EnvRepository(Repository): """ @@ -41,9 +42,12 @@ async def create_env(self) -> "InsertOneResult": environment_to_dict = self.environment.dict() environment_to_dict["env_id"] = self.env_id return await self.collection.insert_one(environment_to_dict) - except: - raise Exception("Error creating environment") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating environment: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) async def update_env(self) -> "Union[int, None]": """ @@ -57,29 +61,30 @@ async def update_env(self) -> "Union[int, None]": environment_to_dict["env_id"] = self.environment.__hash__() await self.collection.update_one( - { "env_id": self.env_id }, - { "$set": environment_to_dict } + {"env_id": self.env_id}, {"$set": environment_to_dict} ) self.env_id = environment_to_dict["env_id"] return self.env_id - except: - raise Exception("Error updating environment") + except Exception as e: + raise Exception(f"Error updating environment: {str(e)}") from e + finally: + self.__del__() async def get_env(self) -> "Union[Env, None]": """ Gets a environment from the database - + Returns: models.Env: Model environment object """ try: - environment = await self.collection.find_one({ "env_id": self.env_id }) + environment = await self.collection.find_one({"env_id": self.env_id}) if environment is not None: return Env.parse_obj(environment) return None - except: - raise Exception("Error getting environment") + except Exception as e: + raise Exception(f"Error getting environment: {str(e)}") from e async def delete_env(self) -> "DeleteResult": """ @@ -89,6 +94,8 @@ async def delete_env(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "env_id": self.env_id }) - except: - raise Exception("Error deleting environment") + return await self.collection.delete_one({"env_id": self.env_id}) + except Exception as e: + raise Exception(f"Error deleting environment: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/repositories/flight.py b/lib/repositories/flight.py index eea93de..6f3f47b 100644 --- a/lib/repositories/flight.py +++ b/lib/repositories/flight.py @@ -4,6 +4,7 @@ from lib.models.flight import Flight from lib.repositories.repo import Repository + class FlightRepository(Repository): """ Flight repository @@ -26,7 +27,9 @@ def __init__(self, flight: Flight = None, flight_id: str = None): def __del__(self): super().__del__() - async def create_flight(self, motor_kind: str = "Solid", rocket_option: str = "Calisto") -> "InsertOneResult": + async def create_flight( + self, motor_kind: str = "Solid", rocket_option: str = "Calisto" + ) -> "InsertOneResult": """ Creates a flight in the database @@ -43,11 +46,16 @@ async def create_flight(self, motor_kind: str = "Solid", rocket_option: str = "C flight_to_dict["rocket"]["rocket_option"] = rocket_option flight_to_dict["rocket"]["motor"]["motor_kind"] = motor_kind return await self.collection.insert_one(flight_to_dict) - except: - raise Exception("Error creating flight") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating flight: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) - async def update_flight(self, motor_kind: str = "Solid", rocket_option: str = "Calisto") -> "Union[int, None]": + async def update_flight( + self, motor_kind: str = "Solid", rocket_option: str = "Calisto" + ) -> "Union[int, None]": """ Updates a flight in the database @@ -60,30 +68,31 @@ async def update_flight(self, motor_kind: str = "Solid", rocket_option: str = "C flight_to_dict["rocket"]["rocket_option"] = rocket_option flight_to_dict["rocket"]["motor"]["motor_kind"] = motor_kind - updated_flight = await self.collection.update_one( - { "flight_id": self.flight_id }, - { "$set": flight_to_dict } + await self.collection.update_one( + {"flight_id": self.flight_id}, {"$set": flight_to_dict} ) self.flight_id = flight_to_dict["flight_id"] - return self.flight_id - except: - raise Exception("Error updating flight") + return self.flight_id + except Exception as e: + raise Exception(f"Error updating flight: {str(e)}") from e + finally: + self.__del__() async def get_flight(self) -> "Union[Flight, None]": """ Gets a flight from the database - + Returns: models.Flight: Model flight object """ try: - flight = await self.collection.find_one({ "flight_id": self.flight_id }) + flight = await self.collection.find_one({"flight_id": self.flight_id}) if flight is not None: return Flight.parse_obj(flight) return None - except: - raise Exception("Error getting flight") + except Exception as e: + raise Exception(f"Error getting flight: {str(e)}") from e async def delete_flight(self) -> "DeleteResult": """ @@ -93,6 +102,8 @@ async def delete_flight(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "flight_id": self.flight_id }) - except: - raise Exception("Error deleting flight") + return await self.collection.delete_one({"flight_id": self.flight_id}) + except Exception as e: + raise Exception(f"Error deleting flight: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/repositories/motor.py b/lib/repositories/motor.py index 030bf3e..95883f1 100644 --- a/lib/repositories/motor.py +++ b/lib/repositories/motor.py @@ -4,6 +4,7 @@ from lib.models.motor import Motor from lib.repositories.repo import Repository + class MotorRepository(Repository): """ Motor repository @@ -42,9 +43,12 @@ async def create_motor(self, motor_kind: str = "solid") -> "InsertOneResult": motor_to_dict["motor_id"] = self.motor_id motor_to_dict["motor_kind"] = motor_kind return await self.collection.insert_one(motor_to_dict) - except: - raise Exception("Error creating motor") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating motor: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) async def update_motor(self, motor_kind: str = "solid") -> "Union[int, None]": """ @@ -58,30 +62,31 @@ async def update_motor(self, motor_kind: str = "solid") -> "Union[int, None]": motor_to_dict["motor_id"] = self.motor.__hash__() motor_to_dict["motor_kind"] = motor_kind - updated_motor = await self.collection.update_one( - { "motor_id": self.motor_id }, - { "$set": motor_to_dict } + await self.collection.update_one( + {"motor_id": self.motor_id}, {"$set": motor_to_dict} ) self.motor_id = motor_to_dict["motor_id"] - return self.motor_id - except: - raise Exception("Error updating motor") + return self.motor_id + except Exception as e: + raise Exception(f"Error updating motor: {str(e)}") from e + finally: + self.__del__() async def get_motor(self) -> "Union[motor, None]": """ Gets a motor from the database - + Returns: models.motor: Model motor object """ try: - motor = await self.collection.find_one({ "motor_id": self.motor_id }) + motor = await self.collection.find_one({"motor_id": self.motor_id}) if motor is not None: return Motor.parse_obj(motor) return None - except: - raise Exception("Error getting motor") + except Exception as e: + raise Exception(f"Error getting motor: {str(e)}") from e async def delete_motor(self) -> "DeleteResult": """ @@ -91,6 +96,8 @@ async def delete_motor(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "motor_id": self.motor_id }) - except: - raise Exception("Error deleting motor") + return await self.collection.delete_one({"motor_id": self.motor_id}) + except Exception as e: + raise Exception(f"Error deleting motor: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/repositories/repo.py b/lib/repositories/repo.py index 26aec23..24401c4 100644 --- a/lib/repositories/repo.py +++ b/lib/repositories/repo.py @@ -1,12 +1,13 @@ -import asyncio from motor.motor_asyncio import AsyncIOMotorClient from pymongo.server_api import ServerApi from lib.secrets import secrets_instance + class Repository: """ Base class for all repositories (singleton) """ + _self = None def __new__(cls, *args, **kwargs): @@ -15,8 +16,12 @@ def __new__(cls, *args, **kwargs): return cls._self def __init__(self, collection: str): - self.connection_string = secrets_instance.get_secret("MONGODB_CONNECTION_STRING") - self.client = AsyncIOMotorClient(self.connection_string, server_api=ServerApi('1')) + self.connection_string = secrets_instance.get_secret( + "MONGODB_CONNECTION_STRING" + ) + self.client = AsyncIOMotorClient( + self.connection_string, server_api=ServerApi("1") + ) self.db = self.client.rocketpy self.collection = self.db[collection] diff --git a/lib/repositories/rocket.py b/lib/repositories/rocket.py index ab03807..2d64fce 100644 --- a/lib/repositories/rocket.py +++ b/lib/repositories/rocket.py @@ -4,6 +4,7 @@ from lib.models.rocket import Rocket from lib.repositories.repo import Repository + class RocketRepository(Repository): """ Rocket repository @@ -42,9 +43,12 @@ async def create_rocket(self, rocket_option: str = "Calisto") -> "InsertOneResul rocket_to_dict["rocket_id"] = self.rocket_id rocket_to_dict["rocket_option"] = rocket_option return await self.collection.insert_one(rocket_to_dict) - except: - raise Exception("Error creating rocket") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating rocket: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) async def update_rocket(self, rocket_option: str = "Calisto") -> "Union[int, None]": """ @@ -58,30 +62,31 @@ async def update_rocket(self, rocket_option: str = "Calisto") -> "Union[int, Non rocket_to_dict["rocket_id"] = self.rocket.__hash__() rocket_to_dict["rocket_option"] = rocket_option - updated_rocket = await self.collection.update_one( - { "rocket_id": self.rocket_id }, - { "$set": rocket_to_dict } + await self.collection.update_one( + {"rocket_id": self.rocket_id}, {"$set": rocket_to_dict} ) self.rocket_id = rocket_to_dict["rocket_id"] - return self.rocket_id - except: - raise Exception("Error updating rocket") + return self.rocket_id + except Exception as e: + raise Exception(f"Error updating rocket: {str(e)}") from e + finally: + self.__del__() async def get_rocket(self) -> "Union[Rocket, None]": """ Gets a rocket from the database - + Returns: models.Rocket: Model rocket object """ try: - rocket = await self.collection.find_one({ "rocket_id": self.rocket_id }) + rocket = await self.collection.find_one({"rocket_id": self.rocket_id}) if rocket is not None: return Rocket.parse_obj(rocket) return None - except: - raise Exception("Error getting rocket") + except Exception as e: + raise Exception(f"Error getting rocket: {str(e)}") from e async def delete_rocket(self) -> "DeleteResult": """ @@ -91,6 +96,8 @@ async def delete_rocket(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "rocket_id": self.rocket_id }) - except: - raise Exception("Error deleting rocket") + return await self.collection.delete_one({"rocket_id": self.rocket_id}) + except Exception as e: + raise Exception(f"Error deleting rocket: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/routes/environment.py b/lib/routes/environment.py new file mode 100644 index 0000000..e2e8f0c --- /dev/null +++ b/lib/routes/environment.py @@ -0,0 +1,93 @@ +""" +Environment routes +""" +from fastapi import APIRouter + +from lib.views.environment import ( + EnvSummary, + EnvCreated, + EnvUpdated, + EnvDeleted, + EnvPickle, +) +from lib.models.environment import Env +from lib.controllers.environment import EnvController + +router = APIRouter( + prefix="/environments", + tags=["ENVIRONMENT"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_env(env: Env) -> "EnvCreated": + """ + Creates a new environment + + ## Args + ``` Env object as a JSON ``` + """ + return await EnvController(env).create_env() + + +@router.get("/{env_id}") +async def read_env(env_id: int) -> "Env": + """ + Reads an environment + + ## Args + ``` env_id: Environment ID hash ``` + """ + return await EnvController.get_env(env_id) + + +@router.put("/{env_id}") +async def update_env(env_id: int, env: Env) -> "EnvUpdated": + """ + Updates an environment + + ## Args + ``` + env_id: Environment ID hash + env: Env object as JSON + ``` + """ + return await EnvController(env).update_env(env_id) + + +@router.delete("/{env_id}") +async def delete_env(env_id: int) -> "EnvDeleted": + """ + Deletes an environment + + ## Args + ``` env_id: Environment ID hash ``` + """ + return await EnvController.delete_env(env_id) + + +@router.get("/rocketpy/{env_id}") +async def read_rocketpy_env(env_id: int) -> "EnvPickle": + """ + Reads a rocketpy environment + + ## Args + ``` env_id: Environment ID hash ``` + """ + return await EnvController.get_rocketpy_env(env_id) + + +@router.get("/{env_id}/simulate") +async def simulate_env(env_id: int) -> "EnvSummary": + """ + Simulates an environment + + ## Args + ``` env_id: Env ID hash ``` + """ + return await EnvController.simulate(env_id) diff --git a/lib/routes/flight.py b/lib/routes/flight.py new file mode 100644 index 0000000..5e2962b --- /dev/null +++ b/lib/routes/flight.py @@ -0,0 +1,134 @@ +""" +Flight routes +""" +from fastapi import APIRouter + +from lib.views.flight import ( + FlightSummary, + FlightCreated, + FlightUpdated, + FlightDeleted, + FlightPickle, +) +from lib.models.environment import Env +from lib.models.flight import Flight +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import MotorKinds +from lib.controllers.flight import FlightController + +router = APIRouter( + prefix="/flights", + tags=["FLIGHT"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_flight( + flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "FlightCreated": + """ + Creates a new flight + + ## Args + ``` Flight object as JSON ``` + """ + return await FlightController(flight, rocket_option, motor_kind).create_flight() + + +@router.get("/{flight_id}") +async def read_flight(flight_id: int) -> "Flight": + """ + Reads a flight + + ## Args + ``` flight_id: Flight ID hash ``` + """ + return await FlightController.get_flight(flight_id) + + +@router.get("/rocketpy/{flight_id}") +async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": + """ + Reads a rocketpy flight object + + ## Args + ``` flight_id: Flight ID hash. ``` + """ + return await FlightController.get_rocketpy_flight(flight_id) + + +@router.put("/{flight_id}/env") +async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": + """ + Updates flight environment + + ## Args + ``` + flight_id: Flight ID hash + env: env object as JSON + ``` + """ + return await FlightController.update_env(flight_id, env) + + +@router.put("/{flight_id}/rocket") +async def update_flight_rocket( + flight_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "FlightUpdated": + """ + Updates flight rocket. + + ## Args + ``` + flight_id: Flight ID hash. + rocket: Rocket object as JSON + ``` + """ + return await FlightController.update_rocket( + flight_id, rocket, rocket_option, motor_kind + ) + + +@router.put("/{flight_id}") +async def update_flight( + flight_id: int, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "FlightUpdated": + """ + Updates Flight object + + ## Args + ``` + flight_id: Flight ID hash. + flight: Flight object as JSON + ``` + """ + return await FlightController(flight, rocket_option, motor_kind).update_flight( + flight_id + ) + + +@router.delete("/{flight_id}") +async def delete_flight(flight_id: int) -> "FlightDeleted": + """ + Deletes a flight + + ## Args + ``` flight_id: Flight ID hash ``` + """ + return await FlightController.delete_flight(flight_id) + + +@router.get("/{flight_id}/simulate") +async def simulate_flight(flight_id: int) -> "FlightSummary": + """ + Simulates a flight + + ## Args + ``` flight_id: Flight ID hash ``` + """ + return await FlightController.simulate(flight_id) diff --git a/lib/routes/motor.py b/lib/routes/motor.py new file mode 100644 index 0000000..c29d90b --- /dev/null +++ b/lib/routes/motor.py @@ -0,0 +1,95 @@ +""" +Motor routes +""" +from fastapi import APIRouter + +from lib.views.motor import ( + MotorSummary, + MotorCreated, + MotorUpdated, + MotorDeleted, + MotorPickle, +) +from lib.models.motor import Motor, MotorKinds +from lib.controllers.motor import MotorController + +router = APIRouter( + prefix="/motors", + tags=["MOTOR"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": + """ + Creates a new motor + + ## Args + ``` Motor object as a JSON ``` + """ + return await MotorController(motor, motor_kind).create_motor() + + +@router.get("/{motor_id}") +async def read_motor(motor_id: int) -> "Motor": + """ + Reads a motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.get_motor(motor_id) + + +@router.put("/{motor_id}") +async def update_motor( + motor_id: int, motor: Motor, motor_kind: MotorKinds +) -> "MotorUpdated": + """ + Updates a motor + + ## Args + ``` + motor_id: Motor ID hash + motor: Motor object as JSON + ``` + """ + return await MotorController(motor, motor_kind).update_motor(motor_id) + + +@router.delete("/{motor_id}") +async def delete_motor(motor_id: int) -> "MotorDeleted": + """ + Deletes a motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.delete_motor(motor_id) + + +@router.get("/rocketpy/{motor_id}") +async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": + """ + Reads a rocketpy motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.get_rocketpy_motor(motor_id) + + +@router.get("/{motor_id}/simulate") +async def simulate_motor(motor_id: int) -> "MotorSummary": + """ + Simulates a motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.simulate(motor_id) diff --git a/lib/routes/rocket.py b/lib/routes/rocket.py new file mode 100644 index 0000000..fded8cc --- /dev/null +++ b/lib/routes/rocket.py @@ -0,0 +1,100 @@ +""" +Rocket routes +""" +from fastapi import APIRouter + +from lib.views.rocket import ( + RocketSummary, + RocketCreated, + RocketUpdated, + RocketDeleted, + RocketPickle, +) +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import MotorKinds +from lib.controllers.rocket import RocketController + +router = APIRouter( + prefix="/rockets", + tags=["ROCKET"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_rocket( + rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "RocketCreated": + """ + Creates a new rocket + + ## Args + ``` Rocket object as a JSON ``` + """ + return await RocketController(rocket, rocket_option, motor_kind).create_rocket() + + +@router.get("/{rocket_id}") +async def read_rocket(rocket_id: int) -> Rocket: + """ + Reads a rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.get_rocket(rocket_id) + + +@router.put("/{rocket_id}") +async def update_rocket( + rocket_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "RocketUpdated": + """ + Updates a rocket + + ## Args + ``` + rocket_id: Rocket ID hash + rocket: Rocket object as JSON + ``` + """ + return await RocketController(rocket, rocket_option, motor_kind).update_rocket( + rocket_id + ) + + +@router.delete("/{rocket_id}") +async def delete_rocket(rocket_id: int) -> "RocketDeleted": + """ + Deletes a rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.delete_rocket(rocket_id) + + +@router.get("/rocketpy/{rocket_id}") +async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": + """ + Reads a rocketpy rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.get_rocketpy_rocket(rocket_id) + + +@router.get("/{rocket_id}/simulate") +async def simulate_rocket(rocket_id: int) -> "RocketSummary": + """ + Simulates a rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.simulate(rocket_id) diff --git a/lib/secrets.py b/lib/secrets.py index e719203..422a491 100644 --- a/lib/secrets.py +++ b/lib/secrets.py @@ -2,10 +2,12 @@ from dotenv import dotenv_values from pydantic import BaseModel + class Secrets(BaseModel): """ - Secrets class to load secrets from .env file + Secrets class to load secrets from .env file """ + secrets: dict = dotenv_values(".env") @staticmethod @@ -18,5 +20,6 @@ def get_secret(self, key): return self.get_os_secret(key) return dotenv_secret + # global instance secrets_instance = Secrets() diff --git a/lib/views/environment.py b/lib/views/environment.py index de1ace9..51b4216 100644 --- a/lib/views/environment.py +++ b/lib/views/environment.py @@ -1,9 +1,10 @@ from typing import List from pydantic import BaseModel + class EnvData(BaseModel): - #TBD: review grav type - #grav: "Any" + # TBD: review grav type + # grav: "Any" elevation: int model_type: str model_type_max_expected_height: int @@ -18,6 +19,7 @@ class EnvData(BaseModel): lat: float lon: float + class EnvPlots(BaseModel): grid: "List[float]" wind_speed: "List[float]" @@ -29,21 +31,26 @@ class EnvPlots(BaseModel): pressure: "List[float]" temperature: "List[float]" + class EnvSummary(BaseModel): env_data: EnvData env_plots: EnvPlots + class EnvCreated(BaseModel): - env_id: str + env_id: str message: str = "Environment successfully created" + class EnvUpdated(BaseModel): - new_env_id: str + new_env_id: str message: str = "Environment successfully updated" + class EnvDeleted(BaseModel): - deleted_env_id: str + deleted_env_id: str message: str = "Environment successfully deleted" + class EnvPickle(BaseModel): jsonpickle_rocketpy_env: str diff --git a/lib/views/flight.py b/lib/views/flight.py index d038bb5..3d64591 100644 --- a/lib/views/flight.py +++ b/lib/views/flight.py @@ -1,6 +1,7 @@ from typing import Optional, Any from pydantic import BaseModel + class InitialConditions(BaseModel): initial_position: str initial_velocity: str @@ -8,6 +9,7 @@ class InitialConditions(BaseModel): initial_angular_position: str initial_angular_velocity: str + class NumericalIntegrationSettings(BaseModel): max_time: str max_time_step: str @@ -20,15 +22,18 @@ class NumericalIntegrationSettings(BaseModel): function_evaluations_per_time_step: str avg_function_evaluations_per_time_step: str + class SurfaceWindConditions(BaseModel): frontal_surface_wind_speed: str lateral_surface_wind_speed: str + class LaunchRailConditions(BaseModel): rail_length: str flight_inclination: str flight_heading: str + class OutOfRailConditions(BaseModel): out_of_rail_time: str out_of_rail_velocity: str @@ -37,6 +42,7 @@ class OutOfRailConditions(BaseModel): out_of_rail_thrust_weight_ratio: str out_of_rail_reynolds_number: str + class BurnoutConditions(BaseModel): burnout_time: str burnout_rocket_velocity: str @@ -45,11 +51,13 @@ class BurnoutConditions(BaseModel): burnout_mach_number: str burnout_kinetic_energy: str + class ApogeeConditions(BaseModel): apogee_time: str apogee_altitude: str apogee_freestream_speed: str + class MaximumValues(BaseModel): maximum_speed: str maximum_mach_number: str @@ -64,15 +72,18 @@ class MaximumValues(BaseModel): maximum_lower_rail_button_normal_force: str maximum_lower_rail_button_shear_force: str + class ImpactConditions(BaseModel): x_impact_position: "Optional[str]" y_impact_position: "Optional[str]" time_of_impact: "Optional[str]" impact_velocity: "Optional[str]" + class EventsRegistered(BaseModel): events_trace: "Optional[Any]" + class FlightData(BaseModel): initial_conditions: InitialConditions numerical_integration_settings: NumericalIntegrationSettings @@ -85,24 +96,30 @@ class FlightData(BaseModel): impact_conditions: ImpactConditions events_registered: "Optional[EventsRegistered]" + class FlightPlots(BaseModel): pass + class FlightSummary(BaseModel): flight_data: FlightData - #flight_plots: FlightPlots + # flight_plots: FlightPlots + class FlightCreated(BaseModel): - flight_id: str + flight_id: str message: str = "Flight successfully created" + class FlightUpdated(BaseModel): - new_flight_id: str + new_flight_id: str message: str = "Flight successfully updated" + class FlightDeleted(BaseModel): - deleted_flight_id: str + deleted_flight_id: str message: str = "Flight successfully deleted" + class FlightPickle(BaseModel): jsonpickle_rocketpy_flight: str diff --git a/lib/views/motor.py b/lib/views/motor.py index 402477a..0be590e 100644 --- a/lib/views/motor.py +++ b/lib/views/motor.py @@ -1,6 +1,7 @@ from typing import List, Any from pydantic import BaseModel + class MotorData(BaseModel): total_burning_time: str total_propellant_mass: str @@ -9,6 +10,7 @@ class MotorData(BaseModel): maximum_thrust: str total_impulse: str + class MotorPlots(BaseModel): thrust: List[Any] total_mass: List[Any] @@ -20,21 +22,26 @@ class MotorPlots(BaseModel): i_13: List[Any] i_23: List[Any] + class MotorSummary(BaseModel): motor_data: MotorData - #motor_plots: MotorPlots + # motor_plots: MotorPlots + class MotorCreated(BaseModel): - motor_id: str + motor_id: str message: str = "Motor successfully created" + class MotorUpdated(BaseModel): - new_motor_id: str + new_motor_id: str message: str = "Motor successfully updated" + class MotorDeleted(BaseModel): - deleted_motor_id: str + deleted_motor_id: str message: str = "Motor successfully deleted" + class MotorPickle(BaseModel): jsonpickle_rocketpy_motor: str diff --git a/lib/views/rocket.py b/lib/views/rocket.py index f7d184f..07ee848 100644 --- a/lib/views/rocket.py +++ b/lib/views/rocket.py @@ -1,11 +1,13 @@ from typing import List, Any, Optional from pydantic import BaseModel + class InertiaDetails(BaseModel): rocket_mass_without_propellant: str rocket_mass_with_propellant: str rocket_inertia_with_motor_without_propellant: "List[str]" + class RocketGeometricalParameters(BaseModel): rocket_maximum_radius: str rocket_frontal_area: str @@ -13,6 +15,7 @@ class RocketGeometricalParameters(BaseModel): rocket_codm_center_of_propellant_mass: str rocket_codm_loaded_center_of_mass: str + class RocketAerodynamicsQuantities(BaseModel): aerodynamics_lift_coefficient_derivatives: "Any" aerodynamics_center_of_pressure: "Any" @@ -20,36 +23,44 @@ class RocketAerodynamicsQuantities(BaseModel): initial_static_margin: str final_static_margin: str + class ParachuteData(BaseModel): parachute_details: "Any" # parachute_ejection_signal_trigger: "Any" parachute_ejection_system_refresh_rate: "Optional[Any]" parachute_lag: "Any" + class RocketData(BaseModel): inertia_details: InertiaDetails rocket_geometrical_parameters: RocketGeometricalParameters rocket_aerodynamics_quantities: RocketAerodynamicsQuantities parachute_data: ParachuteData + class RocketPlots(BaseModel): pass + class RocketSummary(BaseModel): rocket_data: RocketData - #rocket_plots: RocketPlots + # rocket_plots: RocketPlots + class RocketCreated(BaseModel): rocket_id: str message: str = "Rocket successfully created" + class RocketUpdated(BaseModel): - new_rocket_id: str + new_rocket_id: str message: str = "Rocket successfully updated" + class RocketDeleted(BaseModel): - deleted_rocket_id: str + deleted_rocket_id: str message: str = "Rocket successfully deleted" + class RocketPickle(BaseModel): jsonpickle_rocketpy_rocket: str diff --git a/test/Infinity-API.postman_collection.json b/test/Infinity-API.postman_collection.json index 203e044..4ff23fc 100644 --- a/test/Infinity-API.postman_collection.json +++ b/test/Infinity-API.postman_collection.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "00c970b2-c429-4ecd-a1f7-de23aa286d10", "name": "Infinity-API", - "description": "# About this collection\n\nThe API under this collection includes four artifacts **{Environment, Flight, Motor and Rocket}** with 6 endpoints each covering artifact **creation, reading, editing, deleting, simulating and retrieving artifact as jsonpickle string.**\n\n- POST `api/artifact/{{artifact_id}}` { message, artifact_id }\n \n- GET `api/artifact/{{artifact_id}}` { Artifact }\n \n- GET `api/rocketpy/artifact/{{artifact_id}}` { json_pickle_string_artifact }\n \n- GET `api/simulate/artifact/{{artifact_id}}` { ArtifactSimulationSummary }\n \n- PUT `api/artifact/{{artifact_id}}` { message, new_artifact_id }\n \n- DELETE `api/artifact/{{artifact_id}}` { deleted_artifact_id, message }\n \n\n**Flight artifact** have also additional routes that allows to update its own artifacts.\n\n- POST `api/flight/{{flight_id}}/artifact/` { message, flight_id }\n \n\n## **Disclaimer**\n\nCurrently the API only supports Solid motors and TrapezoidalFins, sorry for that, we are working to expand its capabilities soon.\n\n## **Using this collection**\n\n- Run this collection by clicking on \"Run\".\n \n\n\n\n## Additional resources\n\n[Scripting in Postman](https://learning.postman.com/docs/writing-scripts/intro-to-scripts/)\n\n[Test script examples](https://learning.postman.com/docs/writing-scripts/script-references/test-examples/)\n\n[Postman Sandbox API reference](https://learning.postman.com/docs/sending-requests/grpc/postman-sandbox-api/#writing-assertions)\n\n[Using the Collection Runner](https://learning.postman.com/docs/collections/running-collections/intro-to-collection-runs/)", + "description": "# About this collection\n\nThe API under this collection includes four artifacts **{Environment, Flight, Motor and Rocket}** with 6 endpoints each covering artifact **creation, reading, editing, deleting, simulating and retrieving artifact as jsonpickle string.**\n\n- POST `api/artifact/{{artifact_id}}` { message, artifact_id }\n- GET `api/artifact/{{artifact_id}}` { Artifact }\n- GET `api/rocketpy/artifact/{{artifact_id}}` { json_pickle_string_artifact }\n- GET `api/simulate/artifact/{{artifact_id}}` { ArtifactSimulationSummary }\n- PUT `api/artifact/{{artifact_id}}` { message, new_artifact_id }\n- DELETE `api/artifact/{{artifact_id}}` { deleted_artifact_id, message }\n \n\n**Flight artifact** have also additional routes that allows to update its own artifacts.\n\n- POST `api/flight/{{flight_id}}/artifact/` { message, flight_id }\n \n\n## **Using this collection**\n\n- Run this collection by clicking on \"Run\".\n \n\n\n\n## Additional resources\n\n[Scripting in Postman](https://learning.postman.com/docs/writing-scripts/intro-to-scripts/)\n\n[Test script examples](https://learning.postman.com/docs/writing-scripts/script-references/test-examples/)\n\n[Postman Sandbox API reference](https://learning.postman.com/docs/sending-requests/grpc/postman-sandbox-api/#writing-assertions)\n\n[Using the Collection Runner](https://learning.postman.com/docs/collections/running-collections/intro-to-collection-runs/)", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "29631298", "_collection_link": "https://rocketpy-team.postman.co/workspace/Team-Workspace~d228e0d7-1148-4935-8e58-7db52744ee04/collection/29631298-00c970b2-c429-4ecd-a1f7-de23aa286d10?action=share&source=collection_link&creator=29631298" @@ -398,849 +398,2647 @@ "name": "Flight", "item": [ { - "name": "Create Flight", - "event": [ + "name": "Hybrid", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// reduce environment date for future assertion", - "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", - "", - "// save flight parameters", - "pm.environment.set('rail_length', flightRequest.rail_length) ", - "pm.environment.set('inclination', flightRequest.inclination)", - "pm.environment.set('heading', flightRequest.heading)", - "", - "// flight environment", - "pm.environment.set('flight_id', apiRspn.flight_id) ", - "pm.environment.set('latitude', flightRequest.environment.latitude)", - "pm.environment.set('longitude', flightRequest.environment.longitude)", - "pm.environment.set('elevation', flightRequest.environment.elevation) ", - "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", - "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", - "pm.environment.set('date', flightRequest.environment.date) ", - "", - "// flight rocket", - "pm.environment.set('radius', flightRequest.rocket.radius)", - "pm.environment.set('mass', flightRequest.rocket.mass)", - "pm.environment.set('inertia', flightRequest.rocket.inertia)", - "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", - "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", - "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", - "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", - "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", - "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", - "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", - "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", - "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", - "", - "// flight rocket motor", - "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", - "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", - "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", - "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", - "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", - "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", - "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", - "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", - "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", - "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", - "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", - "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", - "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", - "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", - "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", - "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", - "", - "// flight rocket nose", - "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", - "pm.environment.set('kind', flightRequest.rocket.nose.kind)", - "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", - "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", - "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", - "", - "// flight rocket fins", - "pm.environment.set('n', flightRequest.rocket.fins.n)", - "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", - "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", - "pm.environment.set('span', flightRequest.rocket.fins.span)", - "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", - "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", - "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", - "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", - "", - "// flight rocket tail", - "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", - "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", - "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", - "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", - "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", - "", - "// flight rocket parachute", - "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", - "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", - "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", - "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", - "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", - "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", - "", - "//TEST", - "bdd = \"Given a valid Flight POST request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", - " pm.expect(apiRspn.flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Create Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('flight_id', apiRspn.flight_id) ", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight POST request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", + " pm.expect(apiRspn.flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1400,\n \"latitude\": 0,\n \"longitude\": 0\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/?rocket_option=Calisto&motor_kind=Hybrid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"rail_length\": 5.2,\n \"inclination\": 85,\n \"heading\": 0,\n \"environment\": {\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1405,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n },\n \"rocket\": {\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n }\n } \n}", - "options": { - "raw": { - "language": "json" + "name": "Read Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "var returned_date = apiRspn.environment.date;", + "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", + "", + "//TEST", + "bdd = \"Given a valid Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid flight \", function () {", + " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", + " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", + " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", + " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", + " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", + " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", + " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", + " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", + " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", + " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", + " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", + " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", + " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", + " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", + " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", + " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", + " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", + " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", + " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", + " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", + " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", + " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", + " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", + " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", + " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", + " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", + " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", + " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", + " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", + " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", + " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", + " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", + " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", + " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", + " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", + " });" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/", - "host": [ - "{{endpoint}}" + { + "name": "Read rocketpy Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "//TEST", + "bdd = \"Given a valid rocketpy Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "rocketpy", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] - }, - { - "name": "Read Flight", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "", - "var returned_date = apiRspn.environment.date;", - "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", - "", - "//TEST", - "bdd = \"Given a valid Flight GET request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid flight \", function () {", - " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", - " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", - " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", - " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", - " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", - " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", - " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", - " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", - " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", - " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", - " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", - " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", - " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", - " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", - " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", - " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", - " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", - " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", - " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", - " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", - " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", - " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", - " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", - " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", - " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", - " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", - " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", - " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", - " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", - " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", - " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", - " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", - " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", - " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", - " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", - " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", - " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", - " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", - " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", - " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", - " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", - " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", - " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", - " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}", - "host": [ - "{{endpoint}}" + "name": "Simulate Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.flight_data).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Read rocketpy Flight", - "event": [ + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "simulate" + ] + } + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "", - "//TEST", - "bdd = \"Given a valid rocketpy Flight GET request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", - "host": [ - "{{endpoint}}" + "name": "Update Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "rocketpy", - "{{flight_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Simulate Flight", - "event": [ + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1300,\n \"latitude\": 2,\n \"longitude\": 1\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/?rocket_option=Calisto&motor_kind=Hybrid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "//TEST", - "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.flight_data).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", - " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", - " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", - "host": [ - "{{endpoint}}" + "name": "Update Flight Environment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save environment parameters", + "pm.environment.set('env_id', apiRspn.new_env_id) ", + "pm.environment.set('latitude', flightRequest.latitude)", + "pm.environment.set('longitude', flightRequest.longitude)", + "pm.environment.set('elevation', flightRequest.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.date) ", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}", - "simulate" - ] + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/env", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "env" + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Update Flight Rocket", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save rocket parameters", + "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", + "pm.environment.set('radius', flightRequest.radius)", + "pm.environment.set('mass', flightRequest.mass)", + "pm.environment.set('inertia', flightRequest.inertia)", + "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", + "", + "// rocket motor", + "pm.environment.set('burn_time', flightRequest.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", + "", + "// rocket nose", + "pm.environment.set('nose_length', flightRequest.nose.length)", + "pm.environment.set('kind', flightRequest.nose.kind)", + "pm.environment.set('nose_position', flightRequest.nose.position)", + "pm.environment.set('base_radius', flightRequest.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", + "", + "// rocket fins", + "pm.environment.set('n', flightRequest.fins.n)", + "pm.environment.set('root_chord', flightRequest.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", + "pm.environment.set('span', flightRequest.fins.span)", + "pm.environment.set('fin_position', flightRequest.fins.position)", + "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.fins.radius)", + "pm.environment.set('airfoil', flightRequest.fins.airfoil)", + "", + "// rocket tail", + "pm.environment.set('top_radius', flightRequest.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.tail.length)", + "pm.environment.set('tail_position', flightRequest.tail.position)", + "pm.environment.set('tail_radius', flightRequest.tail.radius)", + "", + "// rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 6.8,\n \"center_of_dry_mass_position\": 0.512,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 10,\n \"liquid\": {\n \"density\": 10,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/rocket?rocket_option=Calisto&motor_kind=Hybrid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "rocket" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Delete Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid Flight DELETE request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", + " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "Update Flight", - "event": [ + "name": "Liquid", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// reduce environment date for future assertion", - "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", - "", - "// save flight parameters", - "pm.environment.set('flight_id', apiRspn.new_flight_id) ", - "pm.environment.set('rail_length', flightRequest.rail_length) ", - "pm.environment.set('inclination', flightRequest.inclination)", - "pm.environment.set('heading', flightRequest.heading)", - "", - "// flight environment", - "pm.environment.set('latitude', flightRequest.environment.latitude)", - "pm.environment.set('longitude', flightRequest.environment.longitude)", - "pm.environment.set('elevation', flightRequest.environment.elevation) ", - "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", - "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", - "pm.environment.set('date', flightRequest.environment.date) ", - "", - "// flight rocket", - "pm.environment.set('radius', flightRequest.rocket.radius)", - "pm.environment.set('mass', flightRequest.rocket.mass)", - "pm.environment.set('inertia', flightRequest.rocket.inertia)", - "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", - "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", - "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", - "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", - "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", - "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", - "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", - "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", - "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", - "", - "// flight rocket motor", - "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", - "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", - "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", - "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", - "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", - "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", - "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", - "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", - "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", - "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", - "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", - "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", - "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", - "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", - "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", - "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", - "", - "// flight rocket nose", - "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", - "pm.environment.set('kind', flightRequest.rocket.nose.kind)", - "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", - "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", - "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", - "", - "// flight rocket fins", - "pm.environment.set('n', flightRequest.rocket.fins.n)", - "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", - "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", - "pm.environment.set('span', flightRequest.rocket.fins.span)", - "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", - "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", - "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", - "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", - "", - "// flight rocket tail", - "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", - "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", - "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", - "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", - "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", - "", - "// flight rocket parachute", - "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", - "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", - "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", - "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", - "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", - "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", - "", - "//TEST", - "bdd = \"Given a valid Flight PUT request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", - " });", - " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", - " pm.expect(apiRspn.new_flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Create Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('flight_id', apiRspn.flight_id) ", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight POST request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", + " pm.expect(apiRspn.flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1400,\n \"latitude\": 0,\n \"longitude\": 0\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/?rocket_option=Calisto&motor_kind=Liquid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Liquid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"rail_length\": 5.2,\n \"inclination\": 85,\n \"heading\": 0,\n \"environment\": {\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1405,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n },\n \"rocket\": {\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n }\n } \n}", - "options": { - "raw": { - "language": "json" + "name": "Read Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "var returned_date = apiRspn.environment.date;", + "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", + "", + "//TEST", + "bdd = \"Given a valid Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid flight \", function () {", + " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", + " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", + " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", + " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", + " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", + " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", + " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", + " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", + " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", + " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", + " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", + " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", + " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", + " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", + " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", + " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", + " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", + " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", + " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", + " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", + " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", + " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", + " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", + " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", + " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", + " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", + " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", + " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", + " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", + " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", + " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", + " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", + " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", + " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", + " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", + " });" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}", - "host": [ - "{{endpoint}}" + { + "name": "Read rocketpy Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "//TEST", + "bdd = \"Given a valid rocketpy Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "rocketpy", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] - }, - { - "name": "Update Flight Environment", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// save new flight id", - "pm.environment.set('flight_id', apiRspn.new_flight_id) ", - "", - "// save environment parameters", - "pm.environment.set('env_id', apiRspn.new_env_id) ", - "pm.environment.set('latitude', flightRequest.latitude)", - "pm.environment.set('longitude', flightRequest.longitude)", - "pm.environment.set('elevation', flightRequest.elevation) ", - "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", - "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", - "pm.environment.set('date', flightRequest.date) ", - "", - "//TEST", - "bdd = \"Given a valid Flight PUT request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", - " });", - " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", - " pm.expect(apiRspn.new_flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Simulate Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.flight_data).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "simulate" + ] + } + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", - "options": { - "raw": { - "language": "json" + "name": "Update Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1300,\n \"latitude\": 2,\n \"longitude\": 1\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/?rocket_option=Calisto&motor_kind=Liquid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Liquid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}/env", - "host": [ - "{{endpoint}}" + { + "name": "Update Flight Environment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save environment parameters", + "pm.environment.set('env_id', apiRspn.new_env_id) ", + "pm.environment.set('latitude', flightRequest.latitude)", + "pm.environment.set('longitude', flightRequest.longitude)", + "pm.environment.set('elevation', flightRequest.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.date) ", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}", - "env" - ] + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/env", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "env" + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] + { + "name": "Update Flight Rocket", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save rocket parameters", + "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", + "pm.environment.set('radius', flightRequest.radius)", + "pm.environment.set('mass', flightRequest.mass)", + "pm.environment.set('inertia', flightRequest.inertia)", + "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", + "", + "// rocket motor", + "pm.environment.set('burn_time', flightRequest.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", + "", + "// rocket nose", + "pm.environment.set('nose_length', flightRequest.nose.length)", + "pm.environment.set('kind', flightRequest.nose.kind)", + "pm.environment.set('nose_position', flightRequest.nose.position)", + "pm.environment.set('base_radius', flightRequest.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", + "", + "// rocket fins", + "pm.environment.set('n', flightRequest.fins.n)", + "pm.environment.set('root_chord', flightRequest.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", + "pm.environment.set('span', flightRequest.fins.span)", + "pm.environment.set('fin_position', flightRequest.fins.position)", + "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.fins.radius)", + "pm.environment.set('airfoil', flightRequest.fins.airfoil)", + "", + "// rocket tail", + "pm.environment.set('top_radius', flightRequest.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.tail.length)", + "pm.environment.set('tail_position', flightRequest.tail.position)", + "pm.environment.set('tail_radius', flightRequest.tail.radius)", + "", + "// rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 6.8,\n \"center_of_dry_mass_position\": 0.512,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 10,\n \"liquid\": {\n \"density\": 10,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/rocket?rocket_option=Calisto&motor_kind=Liquid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "rocket" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Liquid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Delete Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid Flight DELETE request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", + " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] + } + ] }, { - "name": "Update Flight Rocket", - "event": [ + "name": "Solid", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// save new flight id", - "pm.environment.set('flight_id', apiRspn.new_flight_id) ", - "", - "// save rocket parameters", - "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", - "pm.environment.set('radius', flightRequest.radius)", - "pm.environment.set('mass', flightRequest.mass)", - "pm.environment.set('inertia', flightRequest.inertia)", - "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", - "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", - "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", - "pm.environment.set('motor_position', flightRequest.motor_position)", - "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", - "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", - "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", - "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", - "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", - "", - "// rocket motor", - "pm.environment.set('burn_time', flightRequest.motor.burn_time)", - "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", - "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", - "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", - "pm.environment.set('grain_number', flightRequest.motor.grain_number)", - "pm.environment.set('grain_density', flightRequest.motor.grain_density)", - "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", - "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", - "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", - "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", - "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", - "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", - "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", - "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", - "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", - "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", - "", - "// rocket nose", - "pm.environment.set('nose_length', flightRequest.nose.length)", - "pm.environment.set('kind', flightRequest.nose.kind)", - "pm.environment.set('nose_position', flightRequest.nose.position)", - "pm.environment.set('base_radius', flightRequest.nose.base_radius)", - "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", - "", - "// rocket fins", - "pm.environment.set('n', flightRequest.fins.n)", - "pm.environment.set('root_chord', flightRequest.fins.root_chord)", - "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", - "pm.environment.set('span', flightRequest.fins.span)", - "pm.environment.set('fin_position', flightRequest.fins.position)", - "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", - "pm.environment.set('fin_radius', flightRequest.fins.radius)", - "pm.environment.set('airfoil', flightRequest.fins.airfoil)", - "", - "// rocket tail", - "pm.environment.set('top_radius', flightRequest.tail.top_radius)", - "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", - "pm.environment.set('tail_length', flightRequest.tail.length)", - "pm.environment.set('tail_position', flightRequest.tail.position)", - "pm.environment.set('tail_radius', flightRequest.tail.radius)", - "", - "// rocket parachute", - "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", - "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", - "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", - "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", - "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", - "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", - "", - "//TEST", - "bdd = \"Given a valid Flight PUT request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", - " });", - " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", - " pm.expect(apiRspn.new_flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Create Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('flight_id', apiRspn.flight_id) ", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight POST request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", + " pm.expect(apiRspn.flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1400,\n \"latitude\": 0,\n \"longitude\": 0\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/?rocket_option=Calisto&motor_kind=Solid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n } \n}", - "options": { - "raw": { - "language": "json" + "name": "Read Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "var returned_date = apiRspn.environment.date;", + "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", + "", + "//TEST", + "bdd = \"Given a valid Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid flight \", function () {", + " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", + " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", + " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", + " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", + " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", + " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", + " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", + " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", + " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", + " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", + " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", + " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", + " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", + " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", + " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", + " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", + " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", + " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", + " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", + " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", + " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", + " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", + " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", + " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", + " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", + " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", + " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", + " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", + " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", + " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", + " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", + " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", + " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", + " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", + " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", + " });" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}/rocket", - "host": [ - "{{endpoint}}" + { + "name": "Read rocketpy Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "//TEST", + "bdd = \"Given a valid rocketpy Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}", - "rocket" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "rocketpy", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] - }, - { - "name": "Delete Flight", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "//TEST", - "bdd = \"Given a valid Flight DELETE request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", - " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}", - "host": [ - "{{endpoint}}" + "name": "Simulate Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.flight_data).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "simulate" + ] + } + }, + "response": [] + }, + { + "name": "Update Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1300,\n \"latitude\": 2,\n \"longitude\": 1\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/?rocket_option=Calisto&motor_kind=Solid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Update Flight Environment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save environment parameters", + "pm.environment.set('env_id', apiRspn.new_env_id) ", + "pm.environment.set('latitude', flightRequest.latitude)", + "pm.environment.set('longitude', flightRequest.longitude)", + "pm.environment.set('elevation', flightRequest.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.date) ", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/env", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "env" + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Update Flight Rocket", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save rocket parameters", + "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", + "pm.environment.set('radius', flightRequest.radius)", + "pm.environment.set('mass', flightRequest.mass)", + "pm.environment.set('inertia', flightRequest.inertia)", + "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", + "", + "// rocket motor", + "pm.environment.set('burn_time', flightRequest.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", + "", + "// rocket nose", + "pm.environment.set('nose_length', flightRequest.nose.length)", + "pm.environment.set('kind', flightRequest.nose.kind)", + "pm.environment.set('nose_position', flightRequest.nose.position)", + "pm.environment.set('base_radius', flightRequest.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", + "", + "// rocket fins", + "pm.environment.set('n', flightRequest.fins.n)", + "pm.environment.set('root_chord', flightRequest.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", + "pm.environment.set('span', flightRequest.fins.span)", + "pm.environment.set('fin_position', flightRequest.fins.position)", + "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.fins.radius)", + "pm.environment.set('airfoil', flightRequest.fins.airfoil)", + "", + "// rocket tail", + "pm.environment.set('top_radius', flightRequest.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.tail.length)", + "pm.environment.set('tail_position', flightRequest.tail.position)", + "pm.environment.set('tail_radius', flightRequest.tail.radius)", + "", + "// rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 6.8,\n \"center_of_dry_mass_position\": 0.512,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 10,\n \"liquid\": {\n \"density\": 10,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/rocket?rocket_option=Calisto&motor_kind=Solid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "rocket" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Delete Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid Flight DELETE request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", + " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] } ] }, @@ -1314,7 +3112,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", + "raw": "{\n \"thrust_source\": \"Cesaroni_M1670\",\n \"burn_time\": 3.9,\n \"nozzle_radius\": 0.033,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"center_of_dry_mass_position\": 0.317,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", "options": { "raw": { "language": "json" @@ -1322,13 +3120,19 @@ } }, "url": { - "raw": "{{endpoint}}/motors/", + "raw": "{{endpoint}}/motors/?motor_kind=Solid", "host": [ "{{endpoint}}" ], "path": [ "motors", "" + ], + "query": [ + { + "key": "motor_kind", + "value": "Solid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." @@ -1562,7 +3366,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", + "raw": "{\n \"thrust_source\": \"Cesaroni_M1670\",\n \"burn_time\": 3.9,\n \"nozzle_radius\": 0.033,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"center_of_dry_mass_position\": 0.317,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", "options": { "raw": { "language": "json" @@ -1570,13 +3374,20 @@ } }, "url": { - "raw": "{{endpoint}}/motors/{{motor_id}}", + "raw": "{{endpoint}}/motors/{{motor_id}}/?motor_kind=Liquid", "host": [ "{{endpoint}}" ], "path": [ "motors", - "{{motor_id}}" + "{{motor_id}}", + "" + ], + "query": [ + { + "key": "motor_kind", + "value": "Liquid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." @@ -1747,7 +3558,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n } \n}", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", "options": { "raw": { "language": "json" @@ -1755,13 +3566,23 @@ } }, "url": { - "raw": "{{endpoint}}/rockets/", + "raw": "{{endpoint}}/rockets/?rocket_option=Calisto&motor_kind=Solid", "host": [ "{{endpoint}}" ], "path": [ "rockets", "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." @@ -2101,7 +3922,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n } \n}", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 47,\n \"lower_button_position\": 0.1,\n \"upper_button_position\": -0.2\n },\n \"motor\": {\n \"burn_time\": 5.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", "options": { "raw": { "language": "json" @@ -2109,13 +3930,24 @@ } }, "url": { - "raw": "{{endpoint}}/rockets/{{rocket_id}}", + "raw": "{{endpoint}}/rockets/{{rocket_id}}/?rocket_option=Calisto&motor_kind=Hybrid", "host": [ "{{endpoint}}" ], "path": [ "rockets", - "{{rocket_id}}" + "{{rocket_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection."