Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added images/Airplane.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Baseballbat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Boat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Bus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Car.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Stopsign.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Suitcase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Tennisracket.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Umbrella.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/basketball.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions include/camera/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include <optional>
#include <chrono>
#include <opencv2/opencv.hpp>
#include <string>
#include "utilities/base64.hpp"

// Struct to hold the image
struct ImageData {
cv::Mat DATA;
std::string filename;
};

// Utility function for converting an image loaded with OpenCV into base-64
Expand Down
2 changes: 1 addition & 1 deletion include/camera/mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
const int DUMMY_INTERVAL = 1000;
const int DUMMY_TIMEOUT = 1000;
const int IMAGE_COUNT = 5;
const std::string images_dir = "/workspaces/tests/images/";
const std::string images_dir = "/workspaces/images/";

/**
*
Expand Down
2 changes: 2 additions & 0 deletions include/core/mission_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ class MissionState {
bool is_prepared = false;
int task_progress = 0;
std::string current_tick_name;
std::string image_object;

// Mutex to protect shared data between the main loop and the server thread
std::mutex state_mut;
std::mutex image_mut;

MissionState();
~MissionState();
Expand Down
2 changes: 1 addition & 1 deletion include/network/gcs_macros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
#define GCS_HANDLE(Method, uri) Method##_##uri
#define DEF_GCS_HANDLE(Method, uri) void GCS_HANDLE(Method, uri) (GCS_HANDLER_PARAMS)

#endif // INCLUDE_NETWORK_GCS_MACROS_HPP_
#endif // INCLUDE_NETWORK_GCS_MACROS_HPP_
7 changes: 6 additions & 1 deletion include/network/gcs_routes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
#define INCLUDE_NETWORK_GCS_ROUTES_HPP_

#include <httplib.h>

#include <memory>

#include "core/mission_state.hpp"
#include "network/gcs_macros.hpp"

DEF_GCS_HANDLE(Get, status);

DEF_GCS_HANDLE(Get, tick);
DEF_GCS_HANDLE(Get, capture);

#endif // INCLUDE_NETWORK_GCS_ROUTES_HPP_
DEF_GCS_HANDLE(Post, message);

#endif // INCLUDE_NETWORK_GCS_ROUTES_HPP_
26 changes: 26 additions & 0 deletions protos/onboarding.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,34 @@ syntax = "proto3";

option go_package = "gcs-onboarding/internal/protos";

enum ODLCObjects {
Undefined = 0;
Mannequin = 1;
Car = 2;
Motorcycle = 3;
Airplane = 4;
Bus = 5;
Boat = 6;
Stopsign = 7;
Snowboard = 8;
Umbrella = 9;
SoccerBall = 10;
Basketball = 11;
Volleyball = 12;
Football = 13;
Baseballbat = 14;
Mattress = 15;
Tennisracket = 16;
Suitcase = 17;
Skis = 18;
}

message OBCStatus {
string current_tick_name = 1;
bool is_connected = 2;
double mission_progress_percent = 3;
}

message DetectedObject {
ODLCObjects object = 1;
}
21 changes: 19 additions & 2 deletions src/camera/mock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,34 @@ std::optional<ImageData> MockCamera::takePicture(const std::chrono::milliseconds
entries.push_back(entry);
}

cv::Mat captured_image = cv::imread(entries[image_index].path().string());
image_index++; //
// Debug: show how many entries we found
std::cout << "MockCamera: entries found = " << entries.size() << std::endl;

if (entries.empty()) {
std::cout << "MockCamera: no entries in images_dir, returning empty image" << std::endl;
return {};
}

size_t idx = static_cast<size_t>(image_index) % entries.size();
std::string path_str = entries[idx].path().string();
std::cout << "MockCamera: loading index " << idx << " from path: " << path_str << std::endl;
cv::Mat captured_image = cv::imread(path_str);
image_index++;

auto now = std::chrono::steady_clock::now();

if (std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time) >= timeout) {
return {};
}

if (captured_image.empty()) {
std::cout << "MockCamera: imread returned empty image for path: " << path_str << std::endl;
return {};
}

ImageData image_data;
image_data.DATA = captured_image;
image_data.filename = entries[idx].path().stem().string();

return image_data;
}
Expand Down
7 changes: 4 additions & 3 deletions src/network/gcs.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include "network/gcs.hpp"

#include <iostream>

#include "network/gcs_routes.hpp"

GCSServer::GCSServer(uint16_t port, std::shared_ptr<MissionState> state)
: state{state} {

GCSServer::GCSServer(uint16_t port, std::shared_ptr<MissionState> state) : state{state} {
this->_bindHandlers();

this->server_thread = std::thread([this, port]() {
Expand All @@ -24,5 +24,6 @@ void GCSServer::_bindHandlers() {
// Use our macro to bind the GET /status route
BIND_HANDLER(Get, status);
BIND_HANDLER(Get, tick);
BIND_HANDLER(Post, message);
BIND_HANDLER(Get, capture);
}
55 changes: 53 additions & 2 deletions src/network/gcs_routes.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include "network/gcs_routes.hpp"
#include "core/mission_state.hpp"

#include <google/protobuf/util/json_util.h>

#include "core/mission_state.hpp"
#include "nlohmann/json.hpp"

// This needs to be included to get the definition of our proto message
#include "onboarding.pb.h"

Expand Down Expand Up @@ -34,12 +37,60 @@ DEF_GCS_HANDLE(Get, tick) {
response.status = 200;
}

DEF_GCS_HANDLE(Post, message) {
std::string received_message = request.body;

DetectedObject detected_proto;
auto parse_status = google::protobuf::util::JsonStringToMessage(received_message, &detected_proto);
if (!parse_status.ok()) {
response.set_content("Invalid JSON for DetectedObject", "text/plain");
response.status = 400;
return;
}

const std::string detected_name = ODLCObjects_Name(detected_proto.object());

{
std::lock_guard<std::mutex> lock(state->image_mut);

// Compare detected object name to current image filename (stem)
if (state->image.has_value()) {
const std::string &filename = state->image->filename;
state->image_object = detected_name;

std::cout << "Detected Objecct: " << detected_name << "\n Ground Truth:" << filename << std::endl;
if (!detected_name.empty() && detected_name == filename) {
state->image_state = MissionState::ImageState::VALID;
std::cout << "Message Accepted!" << std::endl;
response.set_content("Matched object is correct. Ending mission.", "application/json");
response.status = 200;
} else {
state->image_state = MissionState::ImageState::INVALID;
std::cout << "Message Rejected" << std::endl;
response.set_content("Matched object is incorrect. Going to take another picture.", "application/json");
response.status = 200;
}
} else {
state->image_state = MissionState::ImageState::INVALID;
}
}

}

DEF_GCS_HANDLE(Get, capture) {
std::lock_guard<std::mutex> lock(state->state_mut);
std::optional<ImageData> img = state->image;
std::cout << "img.has_value() = " << img.has_value() << std::endl;
if (!img.has_value()) {
std::cout << "No image available" << std::endl;
response.set_content("No image available", "text/plain");
response.status = 503;
return;
}
std::string img_b64 = cvMatToBase64(img->DATA);

state->has_captured = true;

response.set_content(img_b64, "text/plain");
response.status = 200;
}
}
5 changes: 3 additions & 2 deletions src/ticks/cvloiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ CVLoiterTick::CVLoiterTick(std::shared_ptr<MissionState> state)
: Tick(state, TickID::CVLoiter) {}

void CVLoiterTick::init() {
std::cout << "Initializing CVLoiter..." << std::endl;
state->has_captured = false;
}

Expand All @@ -18,10 +19,10 @@ std::chrono::milliseconds CVLoiterTick::getWait() const {


Tick* CVLoiterTick::tick() {

std::cout << "CVLoiterTick: has_captured = " << state->has_captured << std::endl;
if (state->has_captured) {
return new SwitchTick(state);
} else {
return new CVLoiterTick(state);
return nullptr;
}
}
2 changes: 1 addition & 1 deletion src/ticks/verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Tick* VerifyTick::tick() {

// Waiting for verification from handler
if (state->image_state == MissionState::ImageState::WAITING) {
return new VerifyTick(state);
return nullptr;
} else if (state->image_state == MissionState::ImageState::VALID) {
// Image is valid, we can end the mission
return new EndTick(state);
Expand Down
20 changes: 20 additions & 0 deletions tests/integration/take_pictures.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <iostream>
#include <thread>
#include <chrono>

#include "camera/mock.hpp"

int main() {
MockCamera camera;
int init = camera.getImageCount();

camera.startTakingPictures(std::chrono::milliseconds(DUMMY_TIMEOUT));
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
camera.stopTakingPictures();

int post = camera.getImageCount();
std::cout << "# of images taken:" << post << std::endl;

return (post > init) ? 0 : 2;
}

23 changes: 23 additions & 0 deletions tests/unit/mock_camera_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include <chrono>

#include "camera/mock.hpp"

TEST(MockCameraTest, FilePathValid) {
MockCamera camera;

std::vector<std::filesystem::directory_entry> entries;
for (auto& entry : std::filesystem::directory_iterator(images_dir)) {
entries.push_back(entry);
}

// Expect set filepath has images
EXPECT_FALSE(entries.empty());
}

TEST(MockCameraTest, TestTakingPicture) {
MockCamera camera;

std::optional<ImageData> img = camera.takePicture(std::chrono::milliseconds(DUMMY_TIMEOUT));
EXPECT_TRUE(img.has_value());
}