This project relies on the Echo framework. Read a bit more about it if you aren't familiar.
This project uses go 1.18. Details about the packages it relies on are in the go.mod file.
To run the project for development, simply run:
go run main.go controllers.go odlc.go pathfinding.go
To build a binary for the project, run:
go build
When running, the root of the project is available at localhost:1323.
For testing endpoints for development, it's highly recommended that you use an API client. Insomnia is a great choice.
Controllers are paired routes and HTTP request methods. Middleware, logging, and everything else should be configured here.
Endpoints and controllers are defined here, with sample request/response bodies (if applicable):
-
(GET) /waypoints- Returns a list of all
Waypoints currently registered in the database. - Sample response body:
[ { "id": 1, "name": "Alpha", "longitude": 13.4, "latitude": -4.5, "altitude": 20.4 }, { "id": 2, "name": "Beta", "longitude": 13.57, "latitude": -4, "altitude": 50.4 } ] - Returns a list of all
-
(POST) /waypoints- Registers a provided list of
Waypoints into the database. If this request is made multiple times, any duplicateWaypoints will not be re-registered into the database (a duplicateWaypointis anyWaypointwhose fields are identical, except possiblyid). - Sample request body:
[ { "id": -1, "name": "Alpha", "longitude": 13.4, "latitude": -4.5, "altitude": 20.4 }, { "id": -1, "name": "Beta", "longitude": 13.57, "latitude": -4.0, "altitude": 50.4 } ]
- Registers a provided list of
-
(GET) /routes- Returns a list of all
AEACRoutescurrently registered in the database - Sample response body:
[ { "id": 1, "number": 1, "start_waypoint": "alpha", "end_waypoint": "delta", "passengers": 5, "max_vehicle_weight": 30.5, "value": 13.2, "remarks": "", "order": 0 }, { "id": 2, "number": 2, "start_waypoint": "delta", "end_waypoint": "zeta", "passengers": 10, "max_vehicle_weight": 20.05, "value": 17.3, "remarks": "remark", "order": 1 } ] - Returns a list of all
-
(POST) /routes- Registers a provided list of
AEACRoutesinto the database. If this request is made multiple times, any duplicate routes will not be re-registered into the database (a duplicate route is anyAEACRouteswhose fields are identical, except possiblyid). - Sample request body:
[ { "id": -1, "number": 1, "start_waypoint": "alpha", "end_waypoint": "delta", "passengers": 5, "max_vehicle_weight": 30.5, "value": 13.2, "remarks": "", "order": 0 }, { "id": -1, "number": 2, "start_waypoint": "delta", "end_waypoint": "zeta", "passengers": 10, "max_vehicle_weight": 20.05, "value": 17.3, "remarks": "remark", "order": 1 } ] - Registers a provided list of
-
(GET) /nextroute- Gets the next
AEACRoutesthat should be followed (the one with the lowestorderout of the remainingAEACRoutes). - Deletes the
AEACRoutesfrom the database after it is returned. - Sample response body:
{ "id": 1, "number": 1, "start_waypoint": "alpha", "end_waypoint": "delta", "passengers": 5, "max_vehicle_weight": 30.5, "value": 13.2, "remarks": "", "order": 0 } - Gets the next
-
(GET) /status- Gets the current aircraft status (velocity, longitude, latitude, altitude, heading, battery voltage) from Mission Planner.
- Sample response body:
{ "velocity": 10.4, "latitude": -35.3627175, "longitude": 149.1514354, "altitude": 17.0569992065, "heading": 265.771789551, "voltage": 1.5 }
Functions that manage the storage of Waypoint and AEACRoutes with the local sqlite database are declared here:
-
cleanDB() error- Deletes all
Waypoints andAEACRoutescurrently registered in the sqlite database by issuing a DELETE query for each table, creating the requisite database tables if they do not already exist. - Returns an error if either the creation of the database tables fails, or if the execution of any DELETE queries fail, and
nilotherwise.
- Deletes all
-
connectToDB() *sql.DB- Creates a local sqlite database with filename
database.sqlitein the root directory of the project if it does not already exist, and starts a database connection. - Returns a pointer to a
sql.DBobject required for sending queries to the database. panics if the sqlite file cannot be created or opened.
- Creates a local sqlite database with filename
-
Migrate() error- Reads the SQL queries from
migrate.sqllocated in the root directory of the project, and executes each query against the database created byconnectToDB()to initialize any required tables. - Returns an error if
migrate.sqldoes not exist, or if executing the queries against the sqlite database fails for any reason,nilotherwise.
- Reads the SQL queries from
-
(wp *Waypoint) Create() error- Saves the current
Waypointstruct in the database, and updates the currentWaypointstruct with the correct serializedID. If thisWaypointalready exists in the database (a database record exists that has the same name, longitude, latitude, and altitude), no new record is created, but thewpstruct this method was called on will still be updated to the correct value in the database. - Requires that this
Waypoint.ID == -1(i.e. thisWaypointhas not yet been registered into the database). - Calling
wp.Create()mutateswp.IDand replaces it with the ID assigned by the database serialization (primary key). - Must be called on a
Waypointstruct (i.e. callingwp.Create()).
- Saves the current
-
(wp Waypoint) Update() error- Updates the database record for a given
Waypointbased on its ID. All records in the database withid == wp.IDwill have their field values (ex.name,longitude,latitude,altitude) updated to the field values ofwp. - Requires that
wp.Create()has already been called for thisWaypoint(i.e., thatwp.ID != -1). - Must be called on a
Waypointstruct (i.e. callingwp.Update()).
- Updates the database record for a given
-
(wp Waypoint) Delete() error- Deletes all database records with
id == wp.ID. - Must be called on a
Waypointstruct (i.e. callingwp.Delete()).
- Deletes all database records with
-
(wp *Waypoint) Get() error- Writes the values stored in the database for this given
Waypoints ID to theWaypointobject. - Calling
wp.Get()mutateswpand replaces all field values inwpwith the values from the first record in the database withid == wp.ID. - Must be called on a
Waypointstruct (i.e. callingwp.Get()).
- Writes the values stored in the database for this given
-
getAllWaypoints() (*Queue, error)- Gets all
Waypoints currently registered in the database. - Returns a pointer to a
Queuethat contains all currently registeredWaypoints.
- Gets all
-
(r *AEACRoutes) Create() error- Saves the current
AEACRoutesstruct in the database, and updates the currentAEACRoutesstruct with the correct serializedID. If thisAEACRoutesalready exists in the database (a database record exists that has the same number, start_waypoint, end_waypoint, ...), no new record is created, but therstruct this method was called on will still be updated to the correct value in the database. - Requires that this
AEACRoutes.ID == -1(i.e. thisAEACRouteshas not yet been registered into the database). - Calling
r.Create()mutatesr.IDand replaces it with the ID assigned by the database serialization (primary key). - Must be called on a
AEACRoutesstruct (i.e. callingr.Create()).
- Saves the current
-
(r AEACRoutes) Update() error- Updates the database record for a given
AEACRouteswith the data in the AEACRoutes struct based on its ID. - Requires that
r.Create()has already been called for thisAEACRoutes(i.e., thatr.ID != -1). - Must be called on a
AEACRoutesstruct (i.e. callingr.Update()).
- Updates the database record for a given
-
(r AEACRoutes) Delete() error- Deletes all database records with
id == r.ID. - Must be called on a
AEACRoutesstruct (i.e. callingr.Delete()).
- Deletes all database records with
-
(r *AEACRoutes) Get() error- Writes the values stored in the database for this given
AEACRoutess ID to theAEACRoutesobject. - Calling
r.Get()mutatesrand replaces all field values inrwith the values from the first record in the database withid == r.ID. - Must be called on a
AEACRoutesstruct (i.e. callingr.Get()).
- Writes the values stored in the database for this given
-
getAllRoutes() (*[]AEACRoutes, error)- Gets all
AEACRoutesregistered in the database. - Returns a pointer to an
[]AEACRoutesthat contains all routes currently in the database.
- Gets all
Functions that interface with MissionPlanner-Scripts are declared here. For more detailed specifications on endpoint behavior, see https://github.com/ubcuas/MissionPlanner-Scripts
-
GetQueue() (*Queue, error)- Gets the current queue of
Waypoints that constitute the path the drone is following from MissionPlanner. - Returns: Pointer to a
Queuecontaining theWaypoints generated from the data in the response from MissionPlanner - Interfaces with MissionPlanner-Scripts'
(GET) /queueendpoint.
- Gets the current queue of
-
PostQueue(queue *Queue) error- Overrides the current path that the drone is following with a new
QueueofWaypoints. - Param:
queue- a pointer to aQueuestruct containing the new sequence of waypoints to follow. - Interfaces with MissionPlanner-Scripts'
(POST) /queueendpoint.
- Overrides the current path that the drone is following with a new
-
GetAircraftStatus() (*AircraftStatus, error)- Gets the current aircraft status from MissionPlanner
- Returns: a pointer to a
AircraftStatusstruct with fields populated with data from MissionPlanner response. - Interfaces with MissionPlanner-Scripts'
(GET) /statusendpoint.
-
LockAircraft() error- Locks the aircraft (prevents the aircraft from moving based on the MissionPlanner queue).
- Interfaces with MissionPlanner-Scripts'
(GET) /lockendpoint.
-
UnlockAircraft() error- Unlocks the aircraft (resume aircraft movement based on the MissionPlanner queue).
- Interfaces with MissionPlanner-Scripts'
(GET) /unlockendpoint.
-
ReturnToLaunch() error- Directs the aircraft to head back to the original launch site, or a newly defined
homewaypoint. - Interfaces with the MissionPlanner-Scripts'
(GET) /rtlendpoint.
- Directs the aircraft to head back to the original launch site, or a newly defined
-
LandImmediately() error- Immediately descends the aircraft and lands over its current position.
- Interfaces with the MissionPlanner-Scripts'
(GET) /landendpoint.
-
PostHome(home *Waypoint) error- Sets the
homewaypoint of the aircraft. This is the waypoint that the aircraft will return and land at ifReturnToLaunch()is called. - Param:
home- TheWaypointto set as the new home waypoint for the aircraft. - Interfaces with MissionPlanner-Scripts'
(POST) /homeendpoint.
- Sets the
-
Takeoff(altitude float64) error- Commands the aircraft to take off to a given altitude, measured above sea level. This must be issued while the aircraft is armed in MissionPlanner.
- Interfaces with MissionPlanner-Scripts'
(POST) /takeoffendpoint.
Any functions related to interfacing with ODLC are declared here.
Functions that interface with the Pathfinding module. For more information, see https://github.com/ubcuas/Pathfinding
-
(r AEACRoutes) toPFRoute(startWaypointID int, endWaypointID int) PFRoute- Helper function to convert existing
AEACRoutesstruct into a format compatible with pathfinding data ingest. - Param:
startWaypointID- the integer ID of theWaypointcorresponding tor.StartWaypoint. This is determined by the database serialization and so the constituentWaypoints ofrmust have beenCreate()d already. - Param:
endWaypointID- the integer ID fof theWaypointcorresponding tor.EndWaypoint. - Returns a
PFRoutestruct that represents the same route asr.
- Helper function to convert existing
-
(pfInput PathfindingInput) createPathfindingInput() error- Creates the input
Text.jsonfile for the pathfinding module from the data contained within aPathfindingInputstruct. This file is at./pathfinding/Text.json. - Returns an error if unable to write to the specified location.
- Creates the input
-
runPathfinding() error-
Run the pathfinding module.
-
Requires that an input json file named
Text.jsonhas already been created in./pathfinding/. -
On success, creates an output json file in
./pathfinding/output.jsoncontaining a json object determining the order of routes to be taken, based on the information provided inpfInputwhenCreatePathfindingInputwas called. -
For example, this may be the contents of
./pathfinding/output.json:{"Routes":[1,3,2]}in this case, we should first take the route in
pfInput.AEACRouteswithID == 1, then the route inpfInput.AEACRouteswithID == 3, and finally the route inpfInput.AEACRouteswithID == 2. -
Returns an error if no matching input json file exists, or if the pathfinding executable is not located at
./pathfinding/UAS-Pathfinding.exe, or if the output file is not properly created.
-
-
(pfInput PathfindingInput) readPathfindingOutput() (*[]AEACRoutes, error)- Reads the output of the pathfinding module and determines the order of routes to be taken. This is done by searching
pfInput.AEACRoutesfor anAEACRouteswith a matchingID, updating that route'sOrder, and appending it to a[]AEACRoutes. - Returns a pointer to that
[]AEACRoutesafter all routes are appended. - Requires that
pfInputis the samePathfindingInputstruct that was used to callcreatePathfindingInput().
- Reads the output of the pathfinding module and determines the order of routes to be taken. This is done by searching
Any functions related to interfacing with the RCOMMS system are declared here. Most likely, this include instantiation of socket connections via a middleware to allow for endpoints in the form of controllers to be reached.
Any functions related to sending emails. Used to automate sending the route plan email. Includes formating and sending an email with a given route.
Tests are ran on a per file basis. For example, tests for odlc.go will be in odlc_test.go.
Tests can be ran with go test <test files to run> <files being tested>.
Alternatively, go test will automatically test all files in the project.
Be sure to write a test for every function you make, barring some exceptions like main.
There are options for coverage and verbosity as part of go test.
The database is a SQLite based database generated by shell scripts.
format: Tablename (Corresponding Go Struct)
- id: serial, primary key, internal
- name: text, identifier, arbitrary and provided by the competition
- longitude (degrees)
- latitude (degrees)
- altitude (meters above sea level)
- id: serial, primary key, internal
- number: integer, provided by the competition, used to identify routes to the judges
- start_waypoint_name: name of the starting waypoint
- end_waypoint_name: name of the ending waypoint
- passengers: number of passengers being transported
- max_vehicle_weight: maximum weight of the vehicle allowed for the route (kg or lbs? unsure yet. just a number for now)
- value: $ value of route, floating point value
- remarks: text, indicates any additional details about the parameters of the route
- order: cardinal number indicating the nth order we are taking this route in. nullable. if null, we are not taking this route.