Compare commits
2 commits
extremelyS
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| cbcaff9685 | |||
| d194d9e18d |
6 changed files with 233 additions and 157 deletions
|
|
@ -1,32 +1,43 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread-sicheres Analysemodell
|
|
||||||
* Vereinfachte Implementierung mit:
|
|
||||||
* - Einfachem Mutex-Schutz (kein Reader-Writer-Lock)
|
|
||||||
* - Für seltene Schreibzugriffe geeignet
|
|
||||||
*/
|
|
||||||
class AnalysisModel {
|
class AnalysisModel {
|
||||||
int value = 0; // Der gespeicherte Wert
|
int value = 0;
|
||||||
std::mutex mtx; // Schützt Lese/Schreibzugriffe
|
int reader_count = 0;
|
||||||
|
|
||||||
|
std::mutex model_mutex;
|
||||||
|
std::mutex count_mutex;
|
||||||
|
std::condition_variable no_writer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* Liest den aktuellen Wert
|
|
||||||
* @return Der gespeicherte Wert
|
|
||||||
*/
|
|
||||||
int read() {
|
int read() {
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> count_lock(count_mutex);
|
||||||
return value;
|
reader_count++;
|
||||||
|
|
||||||
|
if(reader_count == 1) {
|
||||||
|
model_mutex.lock();
|
||||||
|
}
|
||||||
|
count_lock.unlock();
|
||||||
|
|
||||||
|
int result = value;
|
||||||
|
|
||||||
|
count_lock.lock();
|
||||||
|
reader_count--;
|
||||||
|
if(reader_count == 0) {
|
||||||
|
model_mutex.unlock();
|
||||||
|
no_writer.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void write(int new_value) {
|
||||||
* Schreibt einen neuen Wert
|
std::unique_lock<std::mutex> lock(model_mutex);
|
||||||
* @param new_val Der neue Wert
|
value = new_value;
|
||||||
*/
|
|
||||||
void write(int new_val) {
|
no_writer.wait(lock, [this]() {
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
return reader_count == 0;
|
||||||
value = new_val;
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
89
main.cpp
89
main.cpp
|
|
@ -1,26 +1,75 @@
|
||||||
#include "sensor_network.h"
|
#include "sensor_network.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
/**
|
constexpr size_t DEFAULT_NUM_SENSORS = 3;
|
||||||
* Hauptprogramm
|
constexpr size_t DEFAULT_NUM_ANALYSERS = 2;
|
||||||
* Startet die Simulation mit festen Parametern
|
constexpr int DEFAULT_RUN_TIME = 30;
|
||||||
* (Könnte leicht für interaktive Eingabe erweitert werden)
|
constexpr size_t DEFAULT_BUFFER_SIZE = 8;
|
||||||
*/
|
|
||||||
int main() {
|
template<size_t N>
|
||||||
// Netzwerk mit Puffergröße 8 erstellen
|
void run_simulation(size_t num_sensors, size_t num_analysers, int run_time) {
|
||||||
SensorNetwork<8> network;
|
SensorNetwork<N> network;
|
||||||
|
std::cout << "\n=== Simulation gestartet ===\n"
|
||||||
std::cout << "Starting simulation...\n";
|
<< "Sensoren: " << num_sensors << "\n"
|
||||||
|
<< "Analysemodule: " << num_analysers << "\n"
|
||||||
// 2 Sensoren und 2 Analyse-Module starten
|
<< "Puffergröße: " << N << "\n"
|
||||||
network.start(2, 2);
|
<< "Laufzeit: " << run_time << "s\n\n";
|
||||||
|
|
||||||
// 30 Sekunden laufen lassen
|
network.start(num_sensors, num_analysers);
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(30));
|
std::this_thread::sleep_for(std::chrono::seconds(run_time));
|
||||||
|
|
||||||
// Netzwerk stoppen
|
|
||||||
network.stop();
|
network.stop();
|
||||||
|
|
||||||
std::cout << "Simulation finished\n";
|
std::cout << "\n=== Simulation beendet ===\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if(input.empty()) return default_value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return std::stoul(input);
|
||||||
|
} catch(...) {
|
||||||
|
std::cout << "Ungültige Eingabe. Verwende Standardwert: "
|
||||||
|
<< default_value << "\n";
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "=== Sensornetzwerk-Simulation ===\n"
|
||||||
|
<< "(Leere Eingabe verwendet Standardwerte)\n";
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,63 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread-sicherer Ringpuffer mit fester Größe N
|
|
||||||
* Implementiert das Producer-Consumer-Pattern mit:
|
|
||||||
* - Mutex für exklusiven Zugriff
|
|
||||||
* - Condition Variable für blockierendes Lesen
|
|
||||||
* - Überschreibt älteste Daten bei vollem Puffer
|
|
||||||
*/
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
class RingBuffer {
|
class RingBuffer {
|
||||||
std::array<int, N> data; // Speicher für die Elemente
|
static_assert(N > 1, "Buffer size must be greater than 1");
|
||||||
size_t read = 0; // Lese-Position
|
|
||||||
size_t write = 0; // Schreib-Position
|
|
||||||
bool full = false; // Flag für vollen Puffer
|
|
||||||
|
|
||||||
std::mutex mtx; // Schützt alle Zugriffe
|
private:
|
||||||
std::condition_variable cv; // Synchronisiert Leser
|
std::vector<int> data;
|
||||||
|
size_t read_ptr = 0;
|
||||||
|
size_t write_ptr = 0;
|
||||||
|
bool full = false;
|
||||||
|
|
||||||
|
std::mutex mtx;
|
||||||
|
std::condition_variable not_empty;
|
||||||
|
|
||||||
|
size_t advance(size_t ptr) const {
|
||||||
|
return (ptr + 1) % N;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
RingBuffer() : data(N, 0) {}
|
||||||
* Schreibt einen Wert in den Puffer
|
|
||||||
* @param value Der zu schreibende Wert
|
|
||||||
*
|
|
||||||
* Funktionsablauf:
|
|
||||||
* 1. Sperrt den Puffer mit Mutex
|
|
||||||
* 2. Schreibt Wert an aktueller Position
|
|
||||||
* 3. Überschreibt ältesten Wert wenn voll
|
|
||||||
* 4. Aktualisiert Schreib-Position
|
|
||||||
* 5. Benachrichtigt wartende Leser
|
|
||||||
*/
|
|
||||||
void push(int value) {
|
void push(int value) {
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
data[write] = value;
|
|
||||||
write = (write + 1) % N; // Ringverhalten
|
data[write_ptr] = value;
|
||||||
if (full) read = (read + 1) % N; // Überschreiben
|
|
||||||
full = (write == read); // Update Voll-Flag
|
if(full) {
|
||||||
cv.notify_one(); // Wecke einen Leser
|
read_ptr = advance(read_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_ptr = advance(write_ptr);
|
||||||
|
full = (write_ptr == read_ptr);
|
||||||
|
|
||||||
|
not_empty.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Liest einen Wert aus dem Puffer (blockierend)
|
|
||||||
* @return Der gelesene Wert
|
|
||||||
*
|
|
||||||
* Funktionsablauf:
|
|
||||||
* 1. Sperrt den Puffer
|
|
||||||
* 2. Wartet bis Daten verfügbar
|
|
||||||
* 3. Liest Wert und aktualisiert Position
|
|
||||||
* 4. Gibt Wert zurück
|
|
||||||
*/
|
|
||||||
int pop() {
|
int pop() {
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
// Warte bis Daten da sind (verhindert Busy Waiting)
|
|
||||||
cv.wait(lock, [this]{ return full || write != read; });
|
not_empty.wait(lock, [this]() {
|
||||||
int val = data[read];
|
return !is_empty();
|
||||||
read = (read + 1) % N; // Ringverhalten
|
});
|
||||||
full = false; // Nicht mehr voll
|
|
||||||
return val;
|
int value = data[read_ptr];
|
||||||
|
read_ptr = advance(read_ptr);
|
||||||
|
full = false;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return !full && (read_ptr == write_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_full() const {
|
||||||
|
return full;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
BIN
sensor_network
BIN
sensor_network
Binary file not shown.
|
|
@ -3,77 +3,92 @@
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
/**
|
|
||||||
* Startet das Sensornetzwerk
|
|
||||||
* @param sensors Anzahl der Sensor-Threads
|
|
||||||
* @param analysers Anzahl der Analyse-Threads
|
|
||||||
*/
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
void SensorNetwork<N>::start(size_t sensors, size_t analysers) {
|
void SensorNetwork<N>::start(size_t num_sensors, size_t num_analysers) {
|
||||||
running = true;
|
running = true;
|
||||||
|
|
||||||
// Sensor-Threads erstellen
|
for(size_t i = 0; i < num_sensors; ++i) {
|
||||||
for (size_t i = 0; i < sensors; ++i) {
|
sensors.emplace_back([this, i] {
|
||||||
threads.emplace_back([this] {
|
sensor_thread(i);
|
||||||
std::mt19937 gen(std::random_device{}());
|
|
||||||
std::uniform_int_distribution<> dist(0, 100);
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
// Zufälliges Intervall (100-500ms)
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(100 + gen() % 400));
|
|
||||||
|
|
||||||
// Messwert generieren und speichern
|
|
||||||
buffer.push(dist(gen));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Analyse-Threads erstellen
|
for(size_t i = 0; i < num_analysers; ++i) {
|
||||||
for (size_t i = 0; i < analysers; ++i) {
|
analysers.emplace_back([this, i] {
|
||||||
threads.emplace_back([this] {
|
analyser_thread(i);
|
||||||
while (running) {
|
|
||||||
// Daten aus Puffer lesen
|
|
||||||
int data = buffer.pop();
|
|
||||||
|
|
||||||
// Analysemodell lesen
|
|
||||||
int model_val = model.read();
|
|
||||||
|
|
||||||
// Ausgabe (könnte auch analysieren)
|
|
||||||
std::cout << "Data: " << data
|
|
||||||
<< " Model: " << model_val << "\n";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Controller-Thread erstellen
|
controller = std::thread([this] {
|
||||||
threads.emplace_back([this] {
|
controller_thread();
|
||||||
std::mt19937 gen(std::random_device{}());
|
|
||||||
while (running) {
|
|
||||||
// Zufälliges Update-Intervall (500-2000ms)
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(500 + gen() % 1500));
|
|
||||||
|
|
||||||
// Analysemodell aktualisieren
|
|
||||||
model.write(gen() % 100);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Stoppt das Sensornetzwerk und wartet auf Threads
|
|
||||||
*/
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
void SensorNetwork<N>::stop() {
|
void SensorNetwork<N>::stop() {
|
||||||
running = false; // Signal zum Stoppen
|
running = false;
|
||||||
|
|
||||||
// Auf alle Threads warten
|
for(auto& t : sensors) {
|
||||||
for (auto& t : threads) {
|
|
||||||
if (t.joinable()) t.join();
|
if (t.joinable()) t.join();
|
||||||
}
|
}
|
||||||
|
for(auto& t : analysers) {
|
||||||
|
if (t.joinable()) t.join();
|
||||||
|
}
|
||||||
|
if (controller.joinable()) {
|
||||||
|
controller.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
std::uniform_int_distribution<> sleep_gen(100, 500);
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(sleep_gen(gen))
|
||||||
|
);
|
||||||
|
|
||||||
|
int value = data_gen(gen);
|
||||||
|
buffer.push(value);
|
||||||
|
|
||||||
|
std::cout << "Sensor " << id << " produced: " << value << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
void SensorNetwork<N>::analyser_thread(int id) {
|
||||||
|
while(running) {
|
||||||
|
int data = buffer.pop();
|
||||||
|
|
||||||
|
int model_value = model.read();
|
||||||
|
|
||||||
|
std::cout << "Analyser " << id << " processed: " << data
|
||||||
|
<< " | Model: " << model_value << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
std::uniform_int_distribution<> sleep_gen(500, 2000);
|
||||||
|
|
||||||
|
while(running) {
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(sleep_gen(gen))
|
||||||
|
);
|
||||||
|
|
||||||
|
int new_value = update_gen(gen);
|
||||||
|
model.write(new_value);
|
||||||
|
|
||||||
|
std::cout << "Controller updated model to: " << new_value << "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explizite Instanziierungen für gängige Puffergrößen
|
|
||||||
template class SensorNetwork<8>;
|
template class SensorNetwork<8>;
|
||||||
template class SensorNetwork<16>;
|
template class SensorNetwork<16>;
|
||||||
template class SensorNetwork<32>;
|
template class SensorNetwork<32>;
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,26 @@
|
||||||
#include "ring_buffer.h"
|
#include "ring_buffer.h"
|
||||||
#include "analysis_model.h"
|
#include "analysis_model.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Hauptklasse für das Sensornetzwerk
|
|
||||||
* @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>
|
template <size_t N>
|
||||||
class SensorNetwork {
|
class SensorNetwork {
|
||||||
RingBuffer<N> buffer; // Gemeinsamer Datenpuffer
|
RingBuffer<N> buffer;
|
||||||
AnalysisModel model; // Geteiltes Analysemodell
|
AnalysisModel model;
|
||||||
std::atomic<bool> running = false; // Steuerflag für Threads
|
std::atomic<bool> running{false};
|
||||||
std::vector<std::thread> threads; // Alle Threads
|
|
||||||
|
std::vector<std::thread> sensors;
|
||||||
|
std::vector<std::thread> analysers;
|
||||||
|
std::thread controller;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~SensorNetwork() { if (running) stop(); }
|
~SensorNetwork() {
|
||||||
|
if (running) stop();
|
||||||
|
}
|
||||||
|
|
||||||
void start(size_t sensors, size_t analysers);
|
void start(size_t num_sensors, size_t num_analysers);
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sensor_thread(int id);
|
||||||
|
void analyser_thread(int id);
|
||||||
|
void controller_thread();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue