Capture Route
Projects Involved: OBC and GCS
Concepts: Ticks + Networking
Abstract:
You'll be implementing a GET /capture HTTP endpoint that runs on the Onboard Computer (OBC) - the computer physically mounted on the drone. This endpoint allows the remote Ground Control Station (GCS) to retrieve images captured by the drone's camera system during autonomous missions.
Lifecycle of a API call (This example is for /status
the GCS):
+----------------+ +------------------+ +-----------------+
| Your Browser | | GCS Go Backend | | OBC C++ Backend |
| (React App) | | (Proxy on :8080) | |(Server on :5010)|
+----------------+ +------------------+ +-----------------+
| | |
| 1. GET /api/v1/obc/status |
|--------------------->| |
| | 2. Proxies request |
| | to the OBC |
| | |
| | GET /status |
| |--------------------->|
| | | 3. Reads MissionState,
| | | builds a Protobuf
| | | object, then converts
| | | it to a JSON string.
| | |
| | Response with |
| | status JSON |
| | <--------------------|
| | |
| 4. Forwards the | |
| *exact same* JSON | |
| response to the | |
| browser | |
| | |
| Response with JSON | |
|<---------------------| |
| | |
| 5. React parses JSON | |
| and updates UI | |
v v v
After implementing the OBC handler, you will modify the go backend code on the GCS project to setup the client side.
Goals:
- Learn how to setup an HTTP route: The backbone of GCS-OBC communication is HTTP requests.
- Learn how to setup the GCS Backend: The go backend runs the HTTP server which should send the
/capture
request.
Files Involved:
OBC
src/network/gcs.cpp
src/network/gcs_routes.cpp
include/network/gcs_routes.hpp
GCS
backend-go/internal/obc/client.go
backend-go/internal/server/server.go
Base Steps (OBC):
1. Add the HTTP route binding
Add BIND_HANDLER(Get, capture);
to the _bindHandlers()
function:
src/network/gcs.cpp
.
Then add DEF_GCS_HANDLE(Get, capture);
to include/network/gcs_routes.hpp
2. Create the Handler Function Skeleton
In src/network/gcs_routes.cpp
, add the handler function definition with the basic structure:
3. Implement Thread Safety
Inside your handler, you'll need to safely access the shared mission state. Since multiple threads can access the mission state simultaneously (HTTP requests, ticks, other handlers), we need to prevent race conditions.
What's a race condition? It's when multiple threads try to read/write the same data at the same time, leading to unpredictable results or crashes.
The solution: Use a mutex (mutual exclusion) to ensure only one thread accesses the state at a time.
4. Access the Image Data
You need to get the captured image from the mission state.
5. Handle the "No Image" Case
If there is no image, properly set response content and status. Status should be set to 501.
6. Convert Image Format
Use cvMatToBase64()
to convert the image to base64 format.
7. Set the Capture Flag
Recall, the previous task was writing the CVLoiter tick. What do we need to set in this handler so that the program can move to the next tick?
8. Set the Response
Set the response content and status appropriately. The response content should contain the converted image and the status should be 200.
By this point, your OBC-side HTTP Request should be ready. Lets set up the Client-side code on the GCS!
Next Steps (GCS)
9. Learn the go backend.
The GCS backend starts from the main.go
file.
- Pro tip, Use Ctrl+P
to open a search to navigate to a specific file.
As you can see, the file is simple. It initalizes obcClient
, which models the functions that are used to communicate to the OBC. In the actual GCS, we have several 'clients'. (databaseClient, mavlinkClient, etc.). IN the mock project, we will only use obcClient
.
On top of that, there is the httpServer
which spawns the networking server.
10. Register a new route to the server
In the setupRouter()
function in server.go
, you can find where the existing routes are registered to the server. Theses are routes are called by the Frontend.
Ex. StatusPage.tsx
calls:
/status
request to the OBC through the httpServer
.
Add a new route handler for
/capture
. (Remember it is a GET request)
11. Add a new go function
Each route needs to have an go function which runs when the route is called from the frontend. This should be pretty much the same as existing functions, so refer to those.
12. Add new obcClient function
Ex. For /api/v1/obc/tick
:
obcCient
for /capture
. (Again this should be pretty much similar to existing routes)
By this stage the backend setup on both project should be complete. Move on to the next tab to learn how to modify the frontend to call the route you just added!
💡 Tips:
Tip 1: Using std::optional for image data
Usestd::optional
variable to store state->image
- this allows you to use the has_value()
function to safely check if an image exists.
Tip 2: Thread safety with mutex
For step 3, you need to protect access to the shared mission state using a mutex. Usestd::lock_guard
to automatically lock and unlock:
This ensures that only one thread can access the mission state at a time, preventing race conditions.
Tip 3: Setting the capture flag
For step 7, you need to setstate->has_captured = true;
to indicate that the image has been successfully captured and retrieved.