This repository is the delivery of the first assignment of the Advanced and Robot Programming course. The project, implemented in C, simulates a drone moving in a 2D space to reach targets in the environment while avoiding obstacles. The GUI is implemented using ncurses, and communication between processes is via pipes and signals.
To set up the project, follow these steps:
- Clone the repository:
git@github.com:MattiaTinfena/AdvancedRobotProgramming-Assignment.git - Make the installation script executable:
chmod +x install.sh - Run the script:
./install.sh
The install.sh scripts automates the installation of required dependencies for the project and performs the following actions:
- Updates the package lists and upgrades installed packages.
- Installs necessary terminal emulators (Terminator and Konsole).
- Installs development libraries for ncurses and cJSON.
- Cleans up previous build files and compiles the project.
After installation, you can run the project using one of the following methods:
- Direct execution:
./bin/main - Using the vscode environment with the launch.json file provided in the .vscode folder.
If some changes are made to the code, it is necessary to run the make command to recompile the project before running it. Otherwise, you can use the "Run Code" if you are using vscode.
However, when compiling with VS Code, some issues cause the window to resize incorrectly. To resolve this, it is advisable to run make clean followed by make from terminal.
The project architecture of the assignment includes 6 active components, a parameter file ("appsettings.json"), and a log folder ("log") for all log files.
The blackboard serves as the central communication center between components:
- Receives target and obstacle positions from their respective components.
- Sends target and obstacle positions to the drone and receives drone position updates and applied forces.
- Processes user input and forwards it to the drone component for position updates.
Additionally, the blackboard:
- Displays a real-time map of the environment, showing the drone, targets, and obstacles;
- Displays the score, the player's name, the difficulty, the time remaining and the level;
- Computes an overall score based on number of targets and difficulty.
The primitives of the blackboard are the following:
- File Manipulation: Functions like fopen(), fwrite(), and fclose() are used to open a log file, write messages, and close it.
- flock() used in the function writeSecure() manages file locking:
- LOCK_EX for exclusive write access.
- LOCK_SH for shared read access.
- LOCK_UN to unlock the file.
- Process Management and Signal Handling:
- getpid(), which retrieves the process ID.
- sigaction() handles signals from the watchdog (SIGUSR1), manages termination cleanup (SIGTERM), and detects window resizing (SIGWINCH).
- kill() allows you to safely shut down all processes.
- sigset_t mask ensures safe signal handling with pselect().
- Interprocess Communication (IPC) via Pipes:
- write() and read() facilitate data exchange between processes in writeMsg(), writeInputMsg(), readMsg() and readInputMsg().
- UI Handling with Ncurses:
- initscr(), start_color(), curs_set(), noecho(), cbreak() configure input and display settings.
- getmaxyx() retrieves terminal size for dynamic UI.
- newwin(), box(), wrefresh(), mvwprintw(), and werase() manage windows and content rendering.
- Configuration & JSON Parsing:
- cJSON_Parse(), cJSON_GetObjectItemCaseSensitive(), cJSON_Print(), and cJSON_Delete() handle reading, modifying, and freeing configuration data.
readMsg() and writeMsg() facilitate IPC via pipes by reading and writing a Message structure, logging errors, and terminating on failure. readInputMsg() and writeInputMsg() do the same but with an inputMessage structure.
The watchdog continuously monitors system activity, detecting inactivity and triggering alerts when no computations occur. If necessary, it can stop the system to maintain proper functionality. It periodically sends a SIGUSR1 signal to all monitored processes to verify their responsiveness. If a process fails to respond, the watchdog logs the issue and terminates all the process.
The watchdog utilizes the following primitives:
- File Manipulation: Functions like fopen(), fwrite(), and fclose() are used to open a log file, write messages, and close it.
- Process Management and Signal Handling:
- kill(): used to send a signal
- getpid(): used to get the process ID of the watchdog and write it on the passParam file.
- Sigaction(): used to initialize the signal handler to handle the signal sent by the watchdog.
In addition to those, writeSecure() and readSecure() are custom functions designed for safe file operations. writeSecure() allows for writing or modifying a specific line in a file while ensuring that no two processes modify it simultaneously. Meanwhile, readSecure() reads a specific line while maintaining safe concurrent access.
The passparam file is used from the various process to write their process id so that the watchdog is able to read them and send them a signal to check if they are still running.
The input handles user input and displays relevant information using the ncurses library, including:
- The leaderboard
- The drone's position and speed
- The forces acting on the drone
The starting leaderboard is read from the "appsettings.json" file and updated at the end of the game when the user decides to save and close the game.
Upon startup, the user selects:
- The player's name;
- A key configuration between the default one and a custom one;
- A difficulty level between easy and difficult.
In easy mode, the map remains static, while in hard mode, it dynamically changes every few seconds, awarding double points.
After the user has selected the options, the game starts, and the user can control the drone using the key configuration of its choice. The eight external keys can be used to move the drone by adding a force in the respective direction. On the other hand, the central key is used to instantly zero all the forces, in order to see the inertia on the drone.
In addition, the user can choose to pause the game at any time by pressing the 'p' key, or to quit the game by pressing the 'q' key. Other keys pressed are ignored.
The input utilizes the following primitives:
- File Manipulation: fopen(), fwrite(), and fclose() to open, write, and close log files.
- flock() used in the function writeSecure() manages file locking:
- LOCK_EX for exclusive write access.
- LOCK_SH for shared read access.
- LOCK_UN to unlock the file.
- Process Management & Signal Handling:
- getpid(), which retrieves the process ID.
- sigaction() handles signals from the watchdog (SIGUSR1), manages termination cleanup (SIGTERM), and detects window resizing (SIGWINCH).
- Interprocess Communication (IPC) via Pipes: write() and read() facilitate data exchange between processes.
- UI Handling with Ncurses:
- initscr(), start_color(), curs_set(), noecho(), cbreak(), and nodelay() configure input and display settings.
- getmaxyx() retrieves terminal size for dynamic UI.
- newwin(), box(), wrefresh(), mvwprintw(), and werase() manage windows and content rendering.
- Configuration & JSON Parsing:
- cJSON_Parse(), cJSON_GetObjectItemCaseSensitive(), cJSON_Print(), and cJSON_Delete() handle reading, modifying, and freeing configuration data.
In addition, writeSecure() and readSecure() ensure safe file operations. writeSecure() modifies a specific line while preventing concurrent writes, and readSecure() reads a line with safe shared access. Lastly, readInputMsg() and writeInputMsg() facilitate IPC via pipes by reading and writing the InputMessage structure, logging errors, and terminating on failure.
The target and obstacle processes function similarly, generating random positions within the environment while ensuring they are not too close to the drone or to the other elements of the same type. These positions are then sent to the blackboard for display and processing. Additionally, obstacles are strategically placed to avoid being too close to targets, allowing the drone to reach all the targets without any issues.
The target and obstacle utilize the following primitives:
- File Manipulation: Functions like fopen(), fwrite(), and fclose() are used to open a log file, write messages, and close it.
- flock() used in the function writePid() manages file locking:
- LOCK_EX for exclusive write access.
- LOCK_SH for shared read access.
- LOCK_UN to unlock the file.
- Process Management and Signal Handling:
- writePid(), which retrieves the process ID using getpid().
- sigaction() handles signals from the watchdog (SIGUSR1), manages termination cleanup (SIGTERM).
- Interprocess Communication (IPC) via Pipes:
- write() and read() facilitate data exchange between processes.
- Configuration & JSON Parsing:
- cJSON_Parse(), cJSON_GetObjectItemCaseSensitive(), cJSON_Print(), and cJSON_Delete() handle reading, modifying, and freeing configuration data.
In addition, readMsg() and writeMsg() facilitate IPC via pipes by reading and writing the Message structure, logging errors, and terminating on failure.
The drone handles movement and interaction with targets and obstacles, using force-based navigation. The formula used to calculate the next position of the drone is the following:
where:
-
$m$ is the drone's mass -
$x_{i - 1}$ and$x_{i-2}$ are the drone's position respectivelly at istance$i-1$ and istance$i-2$ -
$T$ is the period -
$k$ is the viscous costant -
$F_x$ is the sum of all the forces in that given direction
For the y coordinate the formula is the same. Analyzing how the total force acting on the drone was calculated, this is given by:
- User input, where each key pressed adjusts the force vector by increasing the corresponding force applied to the drone.
- Repulsive force from the obstacles;
where
- Attractive force from the targets.
where
The drone utilizes the following primitives:
- File Manipulation: Functions like fopen(), fwrite(), and fclose() are used to open a log file, write messages, and close it.
- flock() used in the function writePid() manages file locking:
- LOCK_EX for exclusive write access.
- LOCK_SH for shared read access.
- LOCK_UN to unlock the file.
- Process Management and Signal Handling:
- writePid(), which retrieves the process ID using getpid().
- Sigaction() used to initialize the signal handler to handle the signal sent by the watchdog
- Interprocess Communication (IPC) via Pipes:
- write() and read() facilitate data exchange between processes in writeMsg() and readMsg().
- Configuration & JSON Parsing:
- cJSON_Parse(), cJSON_GetObjectItemCaseSensitive(), cJSON_Print(), and cJSON_Delete() handle reading, modifying, and freeing configuration data.
In particular, writeSecure() ensure safe file operations, allowing to modify a specific line while preventing concurrent writes. Lastly, readMsg() and writeMsg() facilitate IPC via pipes by reading and writing a Message structure, logging errors, and terminating on failure.
All configurable parameters are stored in the appsettings.json file and can be modified. These parameters include:
- Player Settings: Player name, difficulty level, and default key bindings, which can be customized at the start of the game.
- Game Progression: Initial level and time per level.
- Environment Settings: Number of targets and obstacles, determining the density of entities in each level.
- Level Scaling: Incremental increase in targets and obstacles as the game progresses.
- Leaderboard: Player rankings, which are updated at the end of each game session.
Logs are available to assist developers in debugging the project and to provide users with insights into the execution process. Each component generates its own log file, storing relevant information, all of which are located in the logs folder. The level of detail in the logs varies based on the project's build mode. In debug mode, more detailed information is recorded, while in release mode, logging is minimized. This behavior is controlled by the USE_DEBUG flag and developed using MACROS.
📂 AdvancedRobotProgramming-Assignment
│── 📄 Makefile
│── 📄 appsettings.json
│── 📄 README.md
│── 📂 docs/
│ ├── 🖼️ ARP-ass1.png
│ ├── 🎞️ Game.gif
│ ├── 🎞️ menu.gif
│
│── 📂 cJSON/
│ ├── 📄 cJSON.h
│
│── 📂 include/
│ ├── 📄 auxfunc.h
│ ├── 📄 drone.h
│ ├── 📄 input.h
│ ├── 📄 keyboardMap.h
│ ├── 📄 log.h
│ ├── 📄 obstacle.h
│ ├── 📄 target.h
│ ├── 📄 watchdog.h
│
│── 📂 src/
│ ├── 📄 auxfunc.c
│ ├── 📄 blackBoard.c
│ ├── 📄 drone.c
│ ├── 📄 input.c
│ ├── 📄 main.c
│ ├── 📄 obstacle.c
│ ├── 📄 target.c
│ ├── 📄 watchdog.c
│
│── 📄 install.sh*


