Skip to content

Mock Camera Task

Projects Involved: OBC

Concepts: Mocking and Concurrency

Abstract:

The purpose of this task is to implement a mock camera that simulates the behavior of a real camera. Instead of physically capturing images through a real camera, the mock camera "captures" images by loading them from a directory.

Goals:

  • Learn the purpose of mocking in software development
  • Serve as an introduction to concurrency and expose you to working in a multithreaded environment

Files Involved:

OBC

Reference Files:

  • include/camera/interface.hpp, include/camera/interface.cpp
  • include/include/mock.hpp
  • include/utilities/base64.hpp, src/utilities/base64.cpp
  • include/utilities/locks.hpp, src/utilities/locks.cpp
  • include/ticks/camera.hpp, src/ticks/camera.cpp

Files to create and work on:

  • src/camera/mock.cpp

Base Steps:

Basic Understanding

In software development, mocking is a technique used to simulate objects or components whose behavior is complex, impractical to test, or simply does not exist. A mock object allows developers to test interactions between components and the actual object that the mock object is supposed to simulate. A mock object will often have the same interface as the real object, allowing switching between the two to be seamless. In this task, the goal is to implement a mock camera to simulate a image capture functionality. You'll find that there are a few components in the OBC that are mocked since flying the plane frequently is very impractical and in some cases is still in production.

Read more about mocking

(Optional) Read more in detail on threads and locks

Implementation

Taking Pictures

This task requires you to implement the method takePicture() of the mock camera.

Taking a picture should essentially be loading an image from a directory of images, you will find the path to the directory stored in images_dir in mock.hpp as well as supplied timeouts.

There is already code provided for you that retrieves all the entries in the directory, along with the logic that picks the index in entries to randomly pick.

1. Implement loading a picture

For this, you should use cv::imread(path) which takes in the path of the file you are trying to load, and then returns a cv::Mat.

You should also check if such returned image is empty. (Hint: Use: empty())

2. Load the return ImageData

Check the ImageData object in interface.hpp. What needs to be set in the object?

You should take note of the return type as you are not just simply returning an ImageData struct


Streaming Pictures (OPTIONAL)

The second part of this task is optional. This part will introduce you to using threads and locks.

1. Implement startTakingPictures().

If the camera is not already taking pictures, launch a thread to start taking pictures. Note here the function the thread should start executing is captureInterval() (hint: look at std::thread() on how to launch a thread)

2. Implement captureInterval().

This is the main logic of capturing images with a given interval. You should check the time the last image was captured and see if it is time to take another picture. If the elapsed time has been greater than interval, take another picture. Otherwise, intentionally put the thread to sleep (hint: look at this_thread::sleep_for() on how to put a thread to sleep)

3. Implement processCapturedImage().

This function should just add the image to the mock_images data structure if an image was successfully taken. It is important to acquire a lock before modifying the mock_images data structure

4. Implement getImageCount()

The function should acquire a lock before getting the image count. Getting the image count can be done directly with mock_images

Tips:

Tip 1: Return Type As mentioned take note of the return type of `takePicture()` which is `std::optional`
Tip 2: Control Flow
  • Pay attention to the types of locks you are acquiring. For example if we are trying to get data from a data structure which lock should we use? The same question could be asked when trying to put data into the data structure.
  • There will be minimal use of concurrency in this task and even though there is minimal usage, the placement of locks and usage of threads is important. The wrong usage could potentially lead to deadlocks, hanging threads, or performance issues
  • Plan out the core logic then identify any critical sections to minimize confusion; if you run into any trouble, feel free to reach out to any of the members of the software team