functional and commented
This commit is contained in:
parent
eb203bcf00
commit
e2f3584f47
10 changed files with 604 additions and 38 deletions
|
|
@ -1,11 +0,0 @@
|
|||
/**
|
||||
* @file BS_Praktikum4.cpp
|
||||
* @brief Implementierung der Funktionen für das BS_Praktikum4 Projekt
|
||||
*/
|
||||
|
||||
#include "BS_Praktikum4.h"
|
||||
#include <iostream>
|
||||
|
||||
void sayHello() {
|
||||
std::cout << "Hallo aus der BS_Praktikum4 Implementierung!" << std::endl;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
/**
|
||||
* @file BS_Praktikum4.h
|
||||
* @brief Hauptheader für das BS_Praktikum4 Projekt
|
||||
*/
|
||||
|
||||
#ifndef BS_Praktikum4_H
|
||||
#define BS_Praktikum4_H
|
||||
|
||||
/**
|
||||
* @brief Gibt eine Begrüßungsnachricht aus
|
||||
*/
|
||||
void sayHello();
|
||||
|
||||
#endif // BS_Praktikum4_H
|
||||
|
|
@ -19,7 +19,11 @@ file(GLOB SOURCES *.cpp)
|
|||
file(GLOB HEADERS *.h *.hpp)
|
||||
|
||||
# Ausführbare Datei erstellen
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
add_executable(${PROJECT_NAME} ${SOURCES}
|
||||
ring_buffer.h
|
||||
analysis_model.h
|
||||
sensor_network.cpp
|
||||
sensor_network.h)
|
||||
|
||||
# Installation konfigurieren
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
|
||||
|
|
|
|||
155
README.md
155
README.md
|
|
@ -0,0 +1,155 @@
|
|||
/* ### Erklärung der Praktikumsaufgabe (Synchronisation)
|
||||
|
||||
#### **Kernziel der Aufgabe**
|
||||
Sie sollen ein **multithreaded Sensornetzwerk** simulieren, das drei Komponenten umfasst:
|
||||
1. **Sensoren (Producer)**
|
||||
- Erzeugen regelmäßig Messdaten (z.B. Temperatur) als Zufallszahlen.
|
||||
- Speichern Daten in einem **gemeinsamen Ringpuffer** (Thread-sicher synchronisiert).
|
||||
2. **Analyse-Module (Consumer & Reader)**
|
||||
- Entnehmen Daten aus dem Ringpuffer.
|
||||
- Lesen **gleichzeitig** ein zentrales Analysemodell (z.B. Kalibrierungsdaten).
|
||||
3. **System-Controller (Writer)**
|
||||
- Aktualisiert das Analysemodell in zufälligen Abständen.
|
||||
|
||||
#### **Technische Anforderungen**
|
||||
1. **Synchronisationsmechanismen**
|
||||
- Nutzen Sie **nur Semaphore und Mutex** (wie in der Vorlesung behandelt).
|
||||
- Vermeiden Sie **Race Conditions** und **Deadlocks**.
|
||||
2. **Ringpuffer**
|
||||
- Vorgegebene Implementierung (nicht threadsicher!) → Sie müssen **Zugriff synchronisieren**.
|
||||
- Eigenschaften:
|
||||
- Größe `BUFFER_SIZE` (z.B. 8 Elemente).
|
||||
- Überschreibt älteste Daten bei Vollschreiben (`ring_push`).
|
||||
- Blockiert beim Lesen, wenn leer (`ring_pop`).
|
||||
3. **Analysemodell**
|
||||
- Einfache Implementierung als **globaler Integer** (keine komplexe Analyse).
|
||||
- **Reader-Writer-Problem**:
|
||||
- Mehrere Leser (Analyse-Module) dürfen **gleichzeitig** lesen.
|
||||
- Writer (Controller) benötigt **exklusiven Zugriff** (kein Leser/Writer aktiv).
|
||||
- **Starvation des Writers verhindern** (Controller darf nicht endlos warten).
|
||||
4. **Simulation**
|
||||
- Sensoren/Controller: `sleep()` + `rand()` für Intervalle.
|
||||
- Ausgaben: Nutzen Sie `printf()` zur Beobachtung des Systems.
|
||||
|
||||
---
|
||||
|
||||
### **Zu implementierende Synchronisation**
|
||||
#### 1. **Ringpuffer (Producer-Consumer Pattern)**
|
||||
- **Problem**: Paralleler Zugriff von Sensoren (Produzenten) und Analyse-Modulen (Konsumenten).
|
||||
- **Lösung**:
|
||||
- **Mutex**: Schützt den Puffer bei `push`/`pop` (strukturelle Integrität).
|
||||
- **Semaphore `items`**: Zählt belegte Pufferplätze.
|
||||
- Initialwert: `0` (leer).
|
||||
- **Producer** (Sensor): Erhöht `items` **nur wenn Puffer vorher nicht voll war** (sonst Überschreiben ohne Signal).
|
||||
- **Consumer** (Analyse-Modul): Wartet auf `items > 0` vor dem Lesen.
|
||||
|
||||
```c
|
||||
// Pseudocode: Sensor (Producer)
|
||||
pthread_mutex_lock(&buffer_mutex);
|
||||
bool was_full = ring_is_full(&rb);
|
||||
ring_push(&rb, new_data);
|
||||
if (!was_full) sem_post(&items); // Signal an Consumer
|
||||
pthread_mutex_unlock(&buffer_mutex);
|
||||
|
||||
// Pseudocode: Analyse-Modul (Consumer)
|
||||
sem_wait(&items); // Wartet auf Daten
|
||||
pthread_mutex_lock(&buffer_mutex);
|
||||
ring_pop(&rb, &data);
|
||||
pthread_mutex_unlock(&buffer_mutex);
|
||||
```
|
||||
|
||||
#### 2. **Analysemodell (Reader-Writer Problem mit Fairness)**
|
||||
- **Problem**: Paralleles Lesen vs. exklusives Schreiben + Verhinderung von Writer-Starvation.
|
||||
- **Lösung** (Fairness mit "Turnstile"-Semaphor):
|
||||
- **Semaphore `turn`**: Garantiert FIFO-Reihenfolge (Fairness).
|
||||
- **Semaphore `rw_mutex`**: Schützt exklusive Schreibzugriffe.
|
||||
- **Mutex `read_mutex`**: Schützt Leser-Zähler (`read_count`).
|
||||
- **Integer `read_count`**: Zählt aktive Leser.
|
||||
|
||||
```c
|
||||
// Pseudocode: Analyse-Modul (Reader)
|
||||
sem_wait(&turn); // Stelle dich in Warteschlange
|
||||
sem_post(&turn); // Weiterleitung (sofort)
|
||||
pthread_mutex_lock(&read_mutex);
|
||||
read_count++;
|
||||
if (read_count == 1) sem_wait(&rw_mutex); // Erster Leser sperrt Writer
|
||||
pthread_mutex_unlock(&read_mutex);
|
||||
|
||||
// LESEZUGRIFF auf shared_model
|
||||
|
||||
pthread_mutex_lock(&read_mutex);
|
||||
read_count--;
|
||||
if (read_count == 0) sem_post(&rw_mutex); // Letzter Leser erlaubt Writer
|
||||
pthread_mutex_unlock(&read_mutex);
|
||||
|
||||
// Pseudocode: Controller (Writer)
|
||||
sem_wait(&turn); // Warte auf deine Reihenfolge
|
||||
sem_wait(&rw_mutex); // Fordere exklusiven Zugriff an
|
||||
sem_post(&turn); // Freigabe für Nächsten in Warteschlange
|
||||
|
||||
// SCHREIBZUGRIFF auf shared_model
|
||||
|
||||
sem_post(&rw_mutex); // Freigabe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Test und Reflexion**
|
||||
Starten Sie das System mit verschiedenen Parametern und beobachten Sie:
|
||||
1. **Durchsatzrate**
|
||||
- Wie viele Daten werden pro Zeiteinheit verarbeitet?
|
||||
- Variieren Sie: Puffergröße, Anzahl der Sensoren/Analyse-Module.
|
||||
2. **Starvation/Deadlocks**
|
||||
- Tritt Starvation auf (z.B. Controller wird blockiert)?
|
||||
- Verursachen bestimmte Konfigurationen Deadlocks?
|
||||
3. **Lastszenarien**
|
||||
- **Viele Sensoren**: Läuft der Puffer über? (Datenverlust durch Überschreiben)
|
||||
- **Viele Leser**: Wird der Controller blockiert? Wie wirkt sich Fairness aus?
|
||||
4. **Systemunterbrechungen**
|
||||
- Simulieren Sie OTA-Updates: Controller führt `sleep()` während Schreibzugriff aus → Blockiert Leser?
|
||||
5. **Optimale Parameter**
|
||||
- Finden Sie Konfigurationen, bei denen das System besonders effizient ist.
|
||||
|
||||
---
|
||||
|
||||
### **Umsetzungshinweise**
|
||||
- **Globale Variablen**:
|
||||
```c
|
||||
RingBuffer rb; // Ringpuffer
|
||||
int shared_model = 0; // Analysemodell (Integer)
|
||||
sem_t items, rw_mutex, turn; // Semaphore
|
||||
pthread_mutex_t buffer_mutex, read_mutex; // Mutexe
|
||||
int read_count = 0; // Zähler aktiver Leser
|
||||
```
|
||||
- **Thread-Erstellung**:
|
||||
```c
|
||||
// Beispiel: Starte 2 Sensoren, 3 Analyse-Module, 1 Controller
|
||||
pthread_t sensor_threads[2], analyser_threads[3], controller_thread;
|
||||
for (int i = 0; i < 2; i++) pthread_create(&sensor_threads[i], NULL, sensor_func, NULL);
|
||||
for (int i = 0; i < 3; i++) pthread_create(&analyser_threads[i], NULL, analyser_func, NULL);
|
||||
pthread_create(&controller_thread, NULL, controller_func, NULL);
|
||||
```
|
||||
|
||||
- **Wichtig**: Initialisieren Sie alle Synchronisationsmittel vor Thread-Start!
|
||||
```c
|
||||
sem_init(&items, 0, 0); // Initialwert 0
|
||||
sem_init(&rw_mutex, 0, 1); // Initialwert 1 (frei)
|
||||
sem_init(&turn, 0, 1); // Initialwert 1 (FIFO-Reihenfolge)
|
||||
pthread_mutex_init(&buffer_mutex, NULL);
|
||||
pthread_mutex_init(&read_mutex, NULL);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Warum diese Lösung?**
|
||||
- **Ringpuffer**: Produzenten überschreiben alte Daten, Konsumenten warten bei Leerpuffer → Effizient & verlusttolerant.
|
||||
- **Analysemodell**:
|
||||
- `turn`-Semaphor verhindert Writer-Starvation (Fairness).
|
||||
- Leser können parallel arbeiten, solange kein Writer aktiv ist.
|
||||
- **Synchronisationsmittel**: Beschränkt auf Semaphore/Mutex (vorgabekonform).
|
||||
|
||||
Mit dieser Struktur erfüllen Sie alle Lernziele:
|
||||
✅ Reader-Writer-Problem
|
||||
✅ Producer-Consumer-Pattern
|
||||
✅ Vermeidung von Race Conditions & Deadlocks!
|
||||
*/
|
||||
75
analysis_model.h
Normal file
75
analysis_model.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/**
|
||||
* Implementiert das Reader-Writer Problem mit:
|
||||
* - Mehrere gleichzeitige Leser
|
||||
* - Exklusiver Zugriff für Schreiber
|
||||
* - Verhindert Writer-Starvation
|
||||
*/
|
||||
class AnalysisModel {
|
||||
int value = 0; // Das geteilte Analysemodell (vereinfacht)
|
||||
int reader_count = 0; // Zählt aktive Leser
|
||||
|
||||
// Synchronisationsprimitive
|
||||
std::mutex model_mutex; // Schützt Schreibzugriffe (exklusiv)
|
||||
std::mutex count_mutex; // Schützt Leserzähler
|
||||
std::condition_variable no_writer; // Garantiert Fairness
|
||||
|
||||
public:
|
||||
/**
|
||||
* Lesender Zugriff
|
||||
* @return Aktueller Wert des Modells
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Sperrt count_mutex und inkrementiert reader_count
|
||||
* 2. Erster Leser sperrt model_mutex (blockiert Writer)
|
||||
* 3. Entsperrt count_mutex während des Lesens
|
||||
* 4. Liest Wert
|
||||
* 5. Sperrt count_mutex zum Dekrementieren
|
||||
* 6. Letzter Leser entsperrt model_mutex und benachrichtigt Writer
|
||||
*/
|
||||
int read() {
|
||||
std::unique_lock<std::mutex> count_lock(count_mutex);
|
||||
reader_count++;
|
||||
|
||||
// Erster Leser sperrt für Writer
|
||||
if(reader_count == 1) {
|
||||
model_mutex.lock();
|
||||
}
|
||||
count_lock.unlock();
|
||||
|
||||
// Kritischer Abschnitt (Lesen, kann parallel erfolgen)
|
||||
int result = value;
|
||||
|
||||
count_lock.lock();
|
||||
reader_count--;
|
||||
// Letzter Leser gibt für Writer frei
|
||||
if(reader_count == 0) {
|
||||
model_mutex.unlock();
|
||||
no_writer.notify_one();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schreibender Zugriff
|
||||
* @param new_value Neuer Wert für das Modell
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Sperrt model_mutex (exklusiver Zugriff)
|
||||
* 2. Schreibt neuen Wert
|
||||
* 3. Wartet bis alle Leser fertig sind (Starvation Prevention)
|
||||
*/
|
||||
void write(int new_value) {
|
||||
std::unique_lock<std::mutex> lock(model_mutex);
|
||||
value = new_value;
|
||||
|
||||
// Verhindert Writer-Starvation
|
||||
no_writer.wait(lock, [this]() {
|
||||
return reader_count == 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
98
main.cpp
98
main.cpp
|
|
@ -1,16 +1,90 @@
|
|||
/**
|
||||
* @file main.cpp
|
||||
* @brief Hauptdatei für das BS_Praktikum4 Projekt
|
||||
*/
|
||||
|
||||
#include "sensor_network.h"
|
||||
#include <iostream>
|
||||
#include "BS_Praktikum4.h"
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
|
||||
// Standardkonfiguration
|
||||
constexpr size_t DEFAULT_NUM_SENSORS = 3;
|
||||
constexpr size_t DEFAULT_NUM_ANALYSERS = 2;
|
||||
constexpr int DEFAULT_RUN_TIME = 30; // Sekunden
|
||||
constexpr size_t DEFAULT_BUFFER_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Führt die Simulation mit gegebenen Parametern aus
|
||||
* @tparam N Puffergröße
|
||||
*/
|
||||
template<size_t N>
|
||||
void run_simulation(size_t num_sensors, size_t num_analysers, int run_time) {
|
||||
SensorNetwork<N> network;
|
||||
std::cout << "\n=== Simulation gestartet ===\n"
|
||||
<< "Sensoren: " << num_sensors << "\n"
|
||||
<< "Analysemodule: " << num_analysers << "\n"
|
||||
<< "Puffergröße: " << N << "\n"
|
||||
<< "Laufzeit: " << run_time << "s\n\n";
|
||||
|
||||
network.start(num_sensors, num_analysers);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(run_time));
|
||||
network.stop();
|
||||
|
||||
std::cout << "\n=== Simulation beendet ===\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest Benutzereingabe mit Standardwert
|
||||
* @param prompt Eingabeaufforderung
|
||||
* @param default_value Standardwert bei leerer Eingabe
|
||||
* @return Eingegebener oder Standardwert
|
||||
*/
|
||||
size_t get_input(const std::string& prompt, size_t default_value) {
|
||||
std::cout << prompt << " [" << default_value << "]: ";
|
||||
std::string input;
|
||||
std::getline(std::cin, input);
|
||||
|
||||
// Verwende Standardwert bei leerer Eingabe
|
||||
if(input.empty()) return default_value;
|
||||
|
||||
// Konvertiere Eingabe
|
||||
try {
|
||||
return std::stoul(input);
|
||||
} catch(...) {
|
||||
std::cout << "Ungültige Eingabe. Verwende Standardwert: "
|
||||
<< default_value << "\n";
|
||||
return default_value;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "Willkommen zum BS_Praktikum4 Projekt!" << std::endl;
|
||||
|
||||
// Beispielaufruf einer Funktion aus der Header-Datei
|
||||
sayHello();
|
||||
|
||||
std::cout << "=== Sensornetzwerk-Simulation ===\n"
|
||||
<< "(Leere Eingabe verwendet Standardwerte)\n";
|
||||
|
||||
// Interaktive Konfiguration
|
||||
size_t num_sensors = get_input("Anzahl Sensoren", DEFAULT_NUM_SENSORS);
|
||||
size_t num_analysers = get_input("Anzahl Analysemodule", DEFAULT_NUM_ANALYSERS);
|
||||
int run_time = static_cast<int>(
|
||||
get_input("Laufzeit (Sekunden)", DEFAULT_RUN_TIME)
|
||||
);
|
||||
size_t buffer_size = get_input("Puffergröße", DEFAULT_BUFFER_SIZE);
|
||||
|
||||
// Starte Simulation basierend auf Puffergröße
|
||||
switch(buffer_size) {
|
||||
case 8:
|
||||
run_simulation<8>(num_sensors, num_analysers, run_time);
|
||||
break;
|
||||
case 16:
|
||||
run_simulation<16>(num_sensors, num_analysers, run_time);
|
||||
break;
|
||||
case 32:
|
||||
run_simulation<32>(num_sensors, num_analysers, run_time);
|
||||
break;
|
||||
default:
|
||||
std::cout << "Nicht unterstützte Puffergröße. Verwende Standard ("
|
||||
<< DEFAULT_BUFFER_SIZE << ")\n";
|
||||
run_simulation<DEFAULT_BUFFER_SIZE>(
|
||||
num_sensors, num_analysers, run_time
|
||||
);
|
||||
}
|
||||
|
||||
std::cout << "Simulation erfolgreich abgeschlossen.\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
104
ring_buffer.h
Normal file
104
ring_buffer.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/**
|
||||
* Thread-sicherer Ringpuffer mit fester Größe
|
||||
* @tparam N Größe des Puffers (muss > 1 sein)
|
||||
*
|
||||
* Implementiert das Producer-Consumer Pattern mit:
|
||||
* - Mutex für exklusiven Zugriff
|
||||
* - Condition Variable für blockierendes Pop
|
||||
* - Überschreibt älteste Daten bei vollem Puffer
|
||||
*/
|
||||
template <size_t N>
|
||||
class RingBuffer {
|
||||
static_assert(N > 1, "Buffer size must be greater than 1");
|
||||
|
||||
private:
|
||||
std::vector<int> data; // Speicher für Elemente
|
||||
size_t read_ptr = 0; // Lesezeiger (nächstes zu lesendes Element)
|
||||
size_t write_ptr = 0; // Schreibzeiger (nächstes freie Position)
|
||||
bool full = false; // Flag, ob Puffer voll ist
|
||||
|
||||
// Synchronisationsprimitive
|
||||
std::mutex mtx; // Schützt alle internen Zustände
|
||||
std::condition_variable not_empty; // Signalisiert, dass Daten verfügbar sind
|
||||
|
||||
// Hilfsfunktion: Zeiger mit Ringverhalten bewegen
|
||||
size_t advance(size_t ptr) const {
|
||||
return (ptr + 1) % N;
|
||||
}
|
||||
|
||||
public:
|
||||
RingBuffer() : data(N, 0) {}
|
||||
|
||||
/**
|
||||
* Schreibt Wert in den Puffer
|
||||
* @param value Der zu schreibende Wert
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Sperrt Mutex für exklusiven Zugriff
|
||||
* 2. Schreibt Wert an aktueller write_ptr
|
||||
* 3. Bei vollem Puffer: Bewegt read_ptr (überschreibt ältesten Wert)
|
||||
* 4. Aktualisiert write_ptr und full-Flag
|
||||
* 5. Benachrichtigt einen wartenden Consumer
|
||||
*/
|
||||
void push(int value) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
|
||||
// Schreibe Wert
|
||||
data[write_ptr] = value;
|
||||
|
||||
// Überschreibe ältesten Wert bei vollem Puffer
|
||||
if(full) {
|
||||
read_ptr = advance(read_ptr);
|
||||
}
|
||||
|
||||
// Zeiger aktualisieren
|
||||
write_ptr = advance(write_ptr);
|
||||
full = (write_ptr == read_ptr);
|
||||
|
||||
// Benachrichtige einen wartenden Consumer
|
||||
not_empty.notify_one();
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest Wert aus dem Puffer (blockierend)
|
||||
* @return Der gelesene Wert
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Sperrt Mutex
|
||||
* 2. Wartet mit Condition Variable bis Daten verfügbar
|
||||
* 3. Liest Wert an read_ptr
|
||||
* 4. Aktualisiert read_ptr und full-Flag
|
||||
* 5. Gibt Wert zurück
|
||||
*/
|
||||
int pop() {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
|
||||
// Warte bis Daten verfügbar (verhindert Busy Waiting)
|
||||
not_empty.wait(lock, [this]() {
|
||||
return !is_empty();
|
||||
});
|
||||
|
||||
// Lese und aktualisiere Zustand
|
||||
int value = data[read_ptr];
|
||||
read_ptr = advance(read_ptr);
|
||||
full = false;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Prüft ob Puffer leer ist
|
||||
bool is_empty() const {
|
||||
return !full && (read_ptr == write_ptr);
|
||||
}
|
||||
|
||||
// Prüft ob Puffer voll ist
|
||||
bool is_full() const {
|
||||
return full;
|
||||
}
|
||||
};
|
||||
BIN
sensor_network
Executable file
BIN
sensor_network
Executable file
Binary file not shown.
138
sensor_network.cpp
Normal file
138
sensor_network.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include "sensor_network.h"
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
/**
|
||||
* Startet alle Threads des Netzwerks
|
||||
* @param num_sensors Anzahl der Sensor-Threads
|
||||
* @param num_analysers Anzahl der Analyse-Threads
|
||||
*/
|
||||
template <size_t N>
|
||||
void SensorNetwork<N>::start(size_t num_sensors, size_t num_analysers) {
|
||||
running = true;
|
||||
|
||||
// Starte Sensor-Threads
|
||||
for(size_t i = 0; i < num_sensors; ++i) {
|
||||
sensors.emplace_back([this, i] {
|
||||
sensor_thread(i);
|
||||
});
|
||||
}
|
||||
|
||||
// Starte Analyse-Threads
|
||||
for(size_t i = 0; i < num_analysers; ++i) {
|
||||
analysers.emplace_back([this, i] {
|
||||
analyser_thread(i);
|
||||
});
|
||||
}
|
||||
|
||||
// Starte Controller-Thread
|
||||
controller = std::thread([this] {
|
||||
controller_thread();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stoppt alle Threads und wartet auf Beendigung
|
||||
*/
|
||||
template <size_t N>
|
||||
void SensorNetwork<N>::stop() {
|
||||
running = false;
|
||||
|
||||
// Warte auf Thread-Ende
|
||||
for(auto& t : sensors) {
|
||||
if (t.joinable()) t.join();
|
||||
}
|
||||
for(auto& t : analysers) {
|
||||
if (t.joinable()) t.join();
|
||||
}
|
||||
if (controller.joinable()) {
|
||||
controller.join();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-Funktion für Sensoren (Producer)
|
||||
* @param id Eindeutige ID des Sensors
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Generiert zufällige Messwerte
|
||||
* 2. Wartet zufällige Zeit (Messintervall)
|
||||
* 3. Schreibt Daten in Ringpuffer
|
||||
*/
|
||||
template <size_t N>
|
||||
void SensorNetwork<N>::sensor_thread(int id) {
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> data_gen(0, 100); // Messwerte 0-100
|
||||
std::uniform_int_distribution<> sleep_gen(100, 500); // Intervall 100-500ms
|
||||
|
||||
while(running) {
|
||||
// Simuliere Messintervall
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(sleep_gen(gen))
|
||||
);
|
||||
|
||||
// Generiere und schreibe Messwert
|
||||
int value = data_gen(gen);
|
||||
buffer.push(value);
|
||||
|
||||
std::cout << "Sensor " << id << " produced: " << value << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-Funktion für Analyse-Module (Consumer)
|
||||
* @param id Eindeutige ID des Moduls
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Liest Daten aus Ringpuffer (blockierend)
|
||||
* 2. Liest aktuelles Analysemodell
|
||||
* 3. Verarbeitet Daten (hier nur Ausgabe)
|
||||
*/
|
||||
template <size_t N>
|
||||
void SensorNetwork<N>::analyser_thread(int id) {
|
||||
while(running) {
|
||||
// Blockierendes Lesen aus Puffer
|
||||
int data = buffer.pop();
|
||||
|
||||
// Lesender Zugriff auf Analysemodell
|
||||
int model_value = model.read();
|
||||
|
||||
std::cout << "Analyser " << id << " processed: " << data
|
||||
<< " | Model: " << model_value << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-Funktion für System-Controller (Writer)
|
||||
*
|
||||
* Funktionsweise:
|
||||
* 1. Wartet zufällige Zeit zwischen Updates
|
||||
* 2. Schreibt neuen Wert ins Analysemodell
|
||||
*/
|
||||
template <size_t N>
|
||||
void SensorNetwork<N>::controller_thread() {
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> update_gen(0, 100); // Modellwerte
|
||||
std::uniform_int_distribution<> sleep_gen(500, 2000); // Update-Intervall
|
||||
|
||||
while(running) {
|
||||
// Warte bis zum nächsten Update
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(sleep_gen(gen))
|
||||
);
|
||||
|
||||
// Aktualisiere Analysemodell
|
||||
int new_value = update_gen(gen);
|
||||
model.write(new_value);
|
||||
|
||||
std::cout << "Controller updated model to: " << new_value << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Explizite Instanziierung für gängige Puffergrößen
|
||||
template class SensorNetwork<8>;
|
||||
template class SensorNetwork<16>;
|
||||
template class SensorNetwork<32>;
|
||||
41
sensor_network.h
Normal file
41
sensor_network.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include "ring_buffer.h"
|
||||
#include "analysis_model.h"
|
||||
|
||||
/**
|
||||
* Hauptklasse des Sensornetzwerks
|
||||
* @tparam N Größe des Ringpuffers
|
||||
*
|
||||
* Verwaltet alle Komponenten:
|
||||
* - Ringpuffer für Sensordaten
|
||||
* - Analysemodell
|
||||
* - Threads für Sensoren, Analyse und Controller
|
||||
*/
|
||||
template <size_t N>
|
||||
class SensorNetwork {
|
||||
RingBuffer<N> buffer; // Gemeinsamer Datenpuffer
|
||||
AnalysisModel model; // Geteiltes Analysemodell
|
||||
std::atomic<bool> running{false}; // Steuerflag für Threads
|
||||
|
||||
// Thread-Container
|
||||
std::vector<std::thread> sensors;
|
||||
std::vector<std::thread> analysers;
|
||||
std::thread controller;
|
||||
|
||||
public:
|
||||
~SensorNetwork() {
|
||||
if (running) stop();
|
||||
}
|
||||
|
||||
void start(size_t num_sensors, size_t num_analysers);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
// Thread-Funktionen
|
||||
void sensor_thread(int id);
|
||||
void analyser_thread(int id);
|
||||
void controller_thread();
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue