Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
022ea2e
project skeleton
Luca-02 May 9, 2025
306c0cf
first implementation of Renewable energy provider
Luca-02 May 10, 2025
b8fe68b
improved testing of RenewableEnergyProvider
Luca-02 May 10, 2025
34335b6
"press any key to terminate" functionality added to RenewableEnergyPr…
Luca-02 May 13, 2025
8670e9b
correct QoS constant naming in PeriodicPowerRequestSimulator
Luca-02 May 13, 2025
2de8bc1
add caching for Gradle dependencies in CI workflow
Luca-02 May 13, 2025
887f4e0
replace caching step with Gradle setup action in CI workflow
Luca-02 May 13, 2025
3cc32ea
configure Java toolchain to use version 17 and update CI workflow for…
Luca-02 May 13, 2025
4377aa9
Merge pull request #1 from Luca-02/energy-provider-feature
Luca-02 May 13, 2025
d971da8
init admin.server module
Luca-02 May 13, 2025
5876666
add API exception handling and power plant registration functionality
Luca-02 May 15, 2025
92d1f1f
add power plant exception handling and refactor service methods
Luca-02 May 15, 2025
692fa20
refactor exception handling and update, added documentation, testing …
Luca-02 May 15, 2025
8658edd
add pollution monitoring functionality with new endpoints and models
Luca-02 May 17, 2025
6881902
enhance MyReadWriteLock implementation with FIFO queue for fairness a…
Luca-02 May 19, 2025
234b4bc
refactor PollutionService and tests, improve documentation and rename…
Luca-02 May 20, 2025
e380ab7
refactor PollutionController and PowerPlantService, update method nam…
Luca-02 May 21, 2025
3ccfeb0
refactor emission response model, introduce new Measurement and Buffe…
Luca-02 May 24, 2025
de1aad0
refactor project structure and rename classes for clarity, improved P…
Luca-02 May 24, 2025
b9674e2
refactor package structure and rename classes for consistency across …
Luca-02 May 24, 2025
b64fa7d
add network module
Luca-02 May 24, 2025
fe80866
refactor constants and improve RenewableEnergyProvider and testing
Luca-02 May 24, 2025
cc1c224
add documentation comments for PollutionController, PollutionSubscrib…
Luca-02 May 28, 2025
88acdff
Merge pull request #2 from Luca-02/admin-server-feature
Luca-02 May 28, 2025
974831c
first implementation of administration client module
Luca-02 May 28, 2025
284393f
enhance ApiClient and Client classes with detailed documentation and …
Luca-02 May 29, 2025
8b72b5b
refactor ApiClient in admin client and ApiExceptions in admin server …
Luca-02 May 31, 2025
2d855d8
Merge pull request #3 from Luca-02/admin-client-feature
Luca-02 May 31, 2025
4d6942d
refactor Client class to implement a Menu for command handling and im…
Luca-02 Jun 3, 2025
83a20f6
refactor package structure and enhance SlidingWindowBuffer with detai…
Luca-02 Jun 3, 2025
fdae214
start implementation of power plant network, improved global structur…
Luca-02 Jun 3, 2025
7dce0a5
improved PowerPlant service error handling, implemented PeriodicSendM…
Luca-02 Jun 5, 2025
319ce35
implemented registerToNetwork, subscribeToEnergyRequestTopic, waitFor…
Luca-02 Jun 6, 2025
b3a0c4a
add NetworkManager for managing power plant network and refactor rela…
Luca-02 Jun 9, 2025
f67f404
refactor command execution flow and enhance gRPC integration in Power…
Luca-02 Jun 10, 2025
135b335
refactor network management and state handling in PowerPlantPeer; int…
Luca-02 Jun 12, 2025
91a2191
refactor PowerPlantPeer and related classes; enhance network manageme…
Luca-02 Jun 13, 2025
2bb1006
added PowerRequestCallback for MQTT callback handling, first implemen…
Luca-02 Jun 14, 2025
eadc0e9
finished join node procedure, added successor stub in peer attributes
Luca-02 Jun 16, 2025
c71578f
fixed RenewableEnergyProviderTest
Luca-02 Jun 16, 2025
e1a2327
improved ring management, added a peer lock to manage concurrency bet…
Luca-02 Jun 19, 2025
c36eed9
started ElectionManager implementation with chang and roberts algorithm
Luca-02 Jun 19, 2025
33915b2
improved ElectionManager
Luca-02 Jun 20, 2025
9add5b5
little global improvements
Luca-02 Jun 21, 2025
a0b7685
finished election protocol
Luca-02 Jun 23, 2025
acee0f6
final improvement on election protocol
Luca-02 Jun 23, 2025
1cde82c
fixed rejection of election with EnergyRequest attempt_number
Luca-02 Jun 25, 2025
fec64b1
minor fix and added RetryExecutor
Luca-02 Jun 26, 2025
0893c53
global minor fix, added delete power plant endpoint
Luca-02 Jun 26, 2025
d074b9c
added documentation and README.md
Luca-02 Jun 27, 2025
91b3b19
updated README.md
Luca-02 Jun 27, 2025
4fad755
last improvements
Luca-02 Jun 28, 2025
93a8ae4
final fix
Luca-02 Jun 30, 2025
d456de9
updated gradle.yml
Luca-02 Jun 30, 2025
2254adf
little fix
Luca-02 Jun 30, 2025
e2af1dc
fix PeerLockTest testNetworkAndElectionMutualExclusion()
Luca-02 Jun 30, 2025
8089ad4
fix PeerLockTest
Luca-02 Jun 30, 2025
0e15dda
Merge pull request #4 from Luca-02/plant-network-feature
Luca-02 Jun 30, 2025
03df44a
little fix
Luca-02 Jun 30, 2025
d7462e5
updated README.md
Jun 30, 2025
d0b28f7
updated README.md
Jun 30, 2025
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
38 changes: 38 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Gradle CI

on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop

jobs:
build:

runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Grant execute permission for Gradle wrapper
run: chmod +x gradlew

- name: Build with Gradle
run: ./gradlew clean build

- name: Run tests
run: ./gradlew clean test
Binary file added DESM_doc.pdf
Binary file not shown.
Binary file added DPS_Project_2025.pdf
Binary file not shown.
109 changes: 108 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,108 @@
## DESM - Distributed Energy Supply Management
# DESM - Distributed Energy System Management

This is a master's degree project that was developed for the Distributed and Pervasive Systems course (A.Y 2024/25).

## Project Overview

The DESM (Distributed Energy Supply Management) project simulates a renewable energy supply networkRepository. A renewable energy provider generates power from sources like wind, hydro, and solar. When renewable energy alone can't meet demand, the provider relies on a networkRepository of thermal power plants to cover the shortfall.

These thermal plants compete to fulfill energy requests, with the lowest bidder winning the assignment. An administration server manages the registration/removement of thermal plants and collects environmental data, such as pollutant emissions, from their sensors. This data is periodically sent to the server, which an administration client can query for statistics.

![DESM Architecture](./assets/system-architecture.png)

## System Architecture

For sake of simplicity and ease of development, this project is structured as a Gradle multi-module project, rather than a full-fledged microservices architecture, even though it comprises distinct logical services.
The main goal of the project is focused on inter-process communication and application of algorithms for distributed systems.

### Renewable Energy Provider

This entity represents the source of energy demand in the system. It initiates energy production requests when renewable sources are insufficient, transmitting this data to the Network Thermal Power Plants through MQTT.

### Power Plant Network

This is the core distributed component, a networkRepository of interconnected **Thermal Power Plant Peers** operating in a ring topology. Each peer is an independent node responsible for:

* **Network Management**: Manage the ring structure, maintaining an up-to-date local view of the ring topology.
* **Election Manager**: Manage the elections for the incoming energy request that have to be fulfilled using the adapted Chang-Roberts algorithm.
* **Pollution Monitoring**: Collecting CO2 emission data and periodically transmitting this data to the Administration Server through MQTT, processed using a sliding window mechanism.

### Administration Server
A REST server that dynamically adds/removes power plants to DESM and allows the administration client to see the currently active plants in the networkRepository and to compute statistics about pollution levels.

### Administration Client

A client cli that allows querying the administration server to obtain information about the currently active thermal power plants in the networkRepository and their emissions.

## Setup and Run

To set up and run the DESM project, follow these steps:

### Prerequisites

* [**Java Development Kit**](https://www.oracle.com/it/java/technologies/downloads/): Version 17 or higher.
* [**MQTT Broker**](https://mosquitto.org/download/): An MQTT broker (e.g. Mosquitto, HiveMQ Community Edition) must be running and accessible. The system defaults to `tcp://localhost:1883`.

### Getting Started

1. **Clone the Repository**:
First, clone the project repository to your local machine:

```bash
git clone https://github.com/Luca-02/desm.git
```

2. **Clean and Build**:
Navigate to the root directory of the cloned repository and perform a clean build for all subprojects:

```bash
cd desm
./gradlew clean build
```

### Running the Components

Ensure your MQTT broker is running before proceeding. You should start the components in the following order: Administration Server, Administration Client, then the Power Plant Peers, and finally, the Renewable Energy Provider.

1. **Administration Server**:
Navigate to the `desm-server` directory and run the server application:

```bash
cd .\desm-server\
../gradlew run --console=plain
```

2. **Administration Client**:
Navigate to the `desm-client` directory, and run the client application to interact with the Administration Server:

```bash
cd .\desm-client\
../gradlew run --console=plain
```

2. **Power Plant Peers**:
Open a new terminal windows for each power plant peer you want to run. Navigate to the `desm-networkRepository` directory and run the application. Each instance will represent a single power plant:

```bash
cd .\desm-networkRepository\
../gradlew run --console=plain
```

4. **Renewable Energy Provider**:
Navigate to the `desm-provider` directory and run the renewable energy provider application to generate energy requests:

```bash
cd .\desm-provider\
../gradlew run --console=plain
```

## Technologies Used

* **Java**: Core programming language for all components.
* **Gradle**: For project build automation.
* **Eclipse Paho MQTT Client**: For MQTT communication.
* **gRPC**: For RPC communication between networkRepository peers.
* **Spring Boot**: Framework for developing the Administration Server.
* **RESTful APIs**: For client-server communication.
* **Gson**: For JSON serialization/deserialization across components.
* **JUnit 5**: For unit and integration testing.
Binary file added assets/system-architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
allprojects {
group 'org.example'
version '1.0-SNAPSHOT'
}

subprojects {
apply plugin: 'java'
apply plugin: 'application'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

dependencies {
if (project.name != 'desm-core') {
implementation project(':desm-core')
}

// Testing
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.mockito:mockito-core:5.17.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.17.0'
testImplementation 'org.mockito:mockito-inline:5.2.0'

// Lombok
compileOnly 'org.projectlombok:lombok:1.18.38'
annotationProcessor 'org.projectlombok:lombok:1.18.38'
testCompileOnly 'org.projectlombok:lombok:1.18.38'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.38'

// Gson
implementation 'com.google.code.gson:gson:2.13.1'

// MQTT
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
}

repositories {
mavenCentral()
}

test {
useJUnitPlatform()
}
}
7 changes: 7 additions & 0 deletions desm-client/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
application {
mainClassName = "org.example.desm.client.DesmClient"
}

run {
standardInput = System.in
}
40 changes: 40 additions & 0 deletions desm-client/src/main/java/org/example/desm/client/Client.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.example.desm.client;

import org.example.desm.client.command.AvailablePowerPlant;
import org.example.desm.client.command.QueryAverageEmission;

import java.util.Scanner;

public class Client {
public final Scanner scanner;
private final Menu mainMenu;

/**
* Constructor for the Client class.
*/
public Client() {
this.scanner = new Scanner(System.in);
this.mainMenu = buildMainMenu();
}

/**
* Starts the client, displaying the menu.
*/
public void start() {
mainMenu.show();
scanner.close();
}

/**
* Builds the main menu with available commands.
*
* @return The main menu
*/
private Menu buildMainMenu() {
int i = 0;
Menu mainMenu = new Menu("Main Menu", scanner);
mainMenu.addCommand(new AvailablePowerPlant(++i, "View all power plants", scanner));
mainMenu.addCommand(new QueryAverageEmission(++i, "Query average emission", scanner));
return mainMenu;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example.desm.client;

public class DesmClient {
public static void main(String[] args) {
new Client().start();
}
}
72 changes: 72 additions & 0 deletions desm-client/src/main/java/org/example/desm/client/Menu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.example.desm.client;

import org.example.desm.client.command.Command;

import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

/**
* Represents a menu that displays a list of commands and allows the user to select one to execute.
*/
public class Menu {
private final String title;
private final Scanner scanner;
private final Map<Integer, Command> commands;

/**
* Constructor for the Menu class.
*
* @param title The title of the menu
* @param scanner The scanner for user input
*/
public Menu(String title, Scanner scanner) {
this.title = title;
this.scanner = scanner;
this.commands = new TreeMap<>();
}

/**
* Adds a command to the menu.
*
* @param command The command to add
*/
public void addCommand(Command command) {
commands.put(command.getId(), command);
}

/**
* Displays the menu and handles user input.
*/
public void show() {
boolean isExiting = false;
while (!isExiting) {
System.out.println("===== " + title + " =====");
for (Command command : commands.values()) {
System.out.println(command.getId() + ". " + command.getName());
}
System.out.println("0. Exit");

System.out.print("Choose: ");
String input = scanner.nextLine().trim();

try {
int choice = Integer.parseInt(input);
if (choice == 0) {
System.out.println("\nGoodbye!");
isExiting = true;
continue;
}

Command command = commands.get(choice);
if (command != null) {
command.execute();
} else {
System.out.println("Invalid option.");
}
} catch (NumberFormatException e) {
System.out.println("Please enter a valid number.");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.example.desm.client.command;

import org.example.desm.common.ApiClient;
import org.example.desm.common.ResponseErrorExceptions;
import org.example.desm.common.dto.PowerPlant;

import java.io.IOException;
import java.util.List;
import java.util.Scanner;

/**
* Custom {@link Command} to retrieve and display all available power plants.
* This command fetches the list of power plants from the API and prints them.
*/
public class AvailablePowerPlant extends Command {
public AvailablePowerPlant(int id, String name, Scanner scanner) {
super(id, name, scanner);
}

@Override
protected void command() {
try {
List<PowerPlant> plants = ApiClient.getAllPlants();
if (plants.isEmpty()) {
System.out.println("No power plants registered.");
} else {
for (PowerPlant plant : plants) {
System.out.println(plant);
}
}
} catch (ResponseErrorExceptions e) {
System.err.println(e.getMessage());
} catch (IOException | InterruptedException e) {
System.err.println("Unable to retrieve plants!");
}
}
}
Loading