From 70aad3fe1c8b38d4198aa5f32a0facddd125ee8b Mon Sep 17 00:00:00 2001 From: portnoytmy Date: Tue, 3 Jun 2025 01:25:44 +0200 Subject: [PATCH] simple and functional --- analysis_model.h | 73 +++-------------------- main.cpp | 93 +++--------------------------- ring_buffer.h | 107 ++++++---------------------------- sensor_network | Bin 85008 -> 131280 bytes sensor_network.cpp | 141 +++++++++------------------------------------ sensor_network.h | 36 +++--------- 6 files changed, 69 insertions(+), 381 deletions(-) diff --git a/analysis_model.h b/analysis_model.h index 4b4be10..e17e11b 100644 --- a/analysis_model.h +++ b/analysis_model.h @@ -1,75 +1,18 @@ #pragma once #include -#include -/** - * 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 + int value = 0; + std::mutex mtx; 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 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; + std::lock_guard lock(mtx); + return value; } - /** - * 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 lock(model_mutex); - value = new_value; - - // Verhindert Writer-Starvation - no_writer.wait(lock, [this]() { - return reader_count == 0; - }); + void write(int new_val) { + std::lock_guard lock(mtx); + value = new_val; } -}; \ No newline at end of file +}; diff --git a/main.cpp b/main.cpp index b608ad5..d13d9c2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,90 +1,13 @@ #include "sensor_network.h" #include -#include -#include -#include - -// 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 -void run_simulation(size_t num_sensors, size_t num_analysers, int run_time) { - SensorNetwork 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 << "=== 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( - 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( - num_sensors, num_analysers, run_time - ); - } - - std::cout << "Simulation erfolgreich abgeschlossen.\n"; + SensorNetwork<8> network; + std::cout << "Starting simulation...\n"; + network.start(2, 2); // 2 sensors, 2 analysers + + std::this_thread::sleep_for(std::chrono::seconds(30)); + network.stop(); + std::cout << "Simulation finished\n"; return 0; -} \ No newline at end of file +} diff --git a/ring_buffer.h b/ring_buffer.h index ee992c8..4e7a0ff 100644 --- a/ring_buffer.h +++ b/ring_buffer.h @@ -1,104 +1,33 @@ #pragma once -#include -#include +#include #include #include -/** - * 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 class RingBuffer { - static_assert(N > 1, "Buffer size must be greater than 1"); - -private: - std::vector 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; - } + std::array data; + size_t read = 0; + size_t write = 0; + bool full = false; + std::mutex mtx; + std::condition_variable cv; 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 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(); + std::lock_guard lock(mtx); + data[write] = value; + write = (write + 1) % N; + if (full) read = (read + 1) % N; + full = (write == read); + cv.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 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); + cv.wait(lock, [this]{ return full || write != read; }); + int val = data[read]; + read = (read + 1) % N; full = false; - - return value; + return val; } - - // 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; - } -}; \ No newline at end of file +}; diff --git a/sensor_network b/sensor_network index a918fbc171ebbd4db0f619bcea1b551b07d61c64..9d99c4256d2f0ea811196ebf2e42a31732cfb6f5 100755 GIT binary patch literal 131280 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

?=>O-rIu9(lb64n^`t5xa<_j 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 Q``_|CzH-I5!T4JFf2(xW%K!iX literal 85008 zcmeFa4SZC^^*4SK5{L>UDp;zhi-HXbm`wr%L=7*C8weOlf)v3eBnzx=UQ9M2SkTxI z%No+=XQ4%lmRetEwMy$-Y7v7XiI!@lR#T-K741f-rj}}I(fxnV%$a*Y#sc zImEvvbS;$k3hL!VeRa%lgR{~lUO~Ma*`{}=r1$QNgbr>*9Wl|xddA}`p>{k?NWect z!W3e_rJ%AWiK9;*+tUuSL8B{M{A(EYa?M=M4o5S;f>Z}x1nD#SH+0t*It11u8V!J6ysIQc&r670M|Sv&en{cZ0Ej>xqYC!`yvqTGe}kQ`x>3m%Je z+`l8<*kB}3+u{etB{<@ZgoKp%lX8t4mJ#no_>9MA0zOWB$fYyz$;4+OK9lg7jL#H& zrs6XVpG)z%9G`4_uKB^9&6&4fx_s(wORd=hV&Lu=@w=F3P!o{Fu_gGbg_O z!2SQsyx}hI+}R)f#COB(|1)(`?gxvO-SvmexjjRs%~KX4{-4LNLqZJvQ!(_U*x4&x1U|UuTnJ zzz&4Jko7r-`F|q(FTy{r$sVXz6ex1#kz7lv zb0P?`5waQ=B&1cB`EIE8dMW~yIrH;9s%B6!OS~(Js{H{p)$a{>bMl=Ymn&~}O%17) zADDusm-@<}X3g=b>2yM!;-ZS8Vqaj5s}w>Cav;`QTkNd}_{z(qRA*Lkd0B~%5T2T% zN*`G{b2ZxQT0re*?fvD&*Q4;n(&~V>mh>y}=hcwN`T6DkDwi*|teH+1>+j5{EGjE0 zFGYK6e8t`=&~BwSP*dbr3}3e&oaxlS@-p;Sb#WjE-RyGJ6nOHp(7WkrE6OWZ7gd&c ze6V9tpd7tev{I`1wd+YkJtbaLQ!edz96m#>l|CtY;y5JCN*8uQr$B${+zMXC`Z?UqsX%yW2+3#5hz}TqWZw8 zT7xlHDhCdAYknZJ7^CPoEtl?@PWpLP`7oYmrenk`n46!0Ax^`_<`6WjR`|+%${ixd zs@eQ>r?0%qgOTd>Ah)h8s$7GX`2sM3XJu`zr@~uVRbE!)htGRz(oj(WhF!FAKlkeo zeCH&n2%nWkC#RCS9*-y*oW3d#jAIP|ZgJ*y<-RiR?DVXN&aSL1TH`76s`y6zj?QzU z>97U5-hdS4js)n7kvN|nJn*GF!wmzSq3?O6{3ng%8JO7 zA!BhlMxD2)G^e=0Np~NJ_1X{8u%+} zJWe4rC8dk6uVuY~R zad>T3X;CdA-;L4*j>9t%MG@|ORTbq`QU-;<^6J2Gjv`@dG0bJsK!J)vu(zzl48X^u zCG}vWScC%S6csM~DaLk17xyO_Px5_{xF2Nf<@-NAay?cW1C2lN zeYT({8eM!J7<)TbGzrGXd|xT(1B`#;o~}61u4fwE5A*qk7_i>SqE&{m>3@EN70$`V zFus3G+$S05@_qH0KCF!5jSF#4m)cK8rQ%9CpTF?BPvKk7W4aAr$M+*_c*hNr-(kbg zVSK6$zmM@*HvAU8&$i)fnBQ%~Z)1F(4S$&N8*F$-gVb}I4PVE2;}uC)c2M`K-Ly=n zH8c4aT}od&zM16{9u8(++hgEcUzRNBOxiLquXYWu>Lg-^h97Nm8%C#wPu1{U8h)&X zPvZ8Ho@#ARAOv5FSbsRR*xOZjwSFf&?enTj?Qam?sgMLavH9tV+^Y4~J~f0c$mRl`?kczqwRR>PmJ@z-g1IyX?)1`R)4Apx5;{23a4 zi-!M(hTo>)M`-x%8vdIaeusuXOT)Kl_>mgERm1bqouIaB_;WS>4h=s_!*^{E4q8Eb#xL1^$tI&gbs>!%6PO#1|eh40l6oAU@pTuHTdN zoM>)%;)_6s$Lzzu$>(O{8{MxWT6b3%*O+JNo7J(A?mTgS zBi&Po@6HzYm2^*`y*o|ZUq|;8(z_kvemUJ!DDO@Y_lxPCLU^|!?ibK~GTrxl3B;H= zbe}@^UE+QQ-BSqf?hyA=>7GJ&cdNKhr+W(7-8;nng>+A$x_g_rKacJyM0Yof`!ngD zLUVVWxIc~VDI|ASi2IZ2o?%i%3> z?Gkrm!WhcJ+(B2!+3Id;XohLr!M4!9;OOqQhFaW`i{on@zLNJd(^K?)m!N_D)}e;c z*5C&V8Q9&lnZAOvfF+X;5@-jcVl6w#C_2) zp=a{?XTj6mLX7pF#|0AI!RHR$Ow3!4QcVpTM7_fYE+I*YSCi>w_0zr}`g86^$w4Xdu3mQ=WFw7PYGH%bJ)qzNCBYC( zVIsBO(D;bh2t>0fFIGuW851=Dz^Hob52p}QuxEqo2-TYWMBx!>z$!GpIa22p$se~S zpK2tZnw_#D`L%2Zgh(A&id4HT)DB~zXdjwvGI6zK!yRBcYD#@;5_RaKWMO%qM)yCr z?ibPh@BS@>iZpSFNQ8eBd3j~C(*vI)Jxnz-+-B*6iTEs!CgXspdTj> zB)t}qaqa~%-cHO+ey$OF4*k|D)Dpvs$E;A#uqgAQnhPVMftY>^N1cZVnm{NC(T>YsM> zkLZunWjpC!^v6Y?!+2nz!777FVA7$BX~=O6)WywF2FLz?!5GkB4g`drryDUiRPQ;^ zyb#GnjKWyJC+O;J7Bx{R2jY;~XEc`By)nRxJHQTvG8Sx=ZMRjgvo&-IL_la4Y}17j z?Hnmu1++)gYL(WtwasL;eNV^2d+@F zsIrL)i5U67XW?*tZ@BvOP$|AO8uh&i)q`5lOU9u@G3wX1R_{9$M~&~b%$ykdL2o$B zL(?`Cs1ZE!$X8N@F@x2pun?NeLnD^LxS}yiiJyzc>_#2X{aJVLC5nmT5$VB)XgrT> z6cXsRPypydXNtH=xV8{&wNW4A>y1!Ki*xmd9a>MymVbNefmVWW+63Q6@DG9ukVSA469<` zQzW1sbHivYaOxepIy6@|x?0FKK{#O(IbvJr8AL?c6+0vaZYurnKBA0$&K-P1^m`Nf z(Q>-IV#vV<^@$b@n2H8O^j{iO>D1MLRiK9G?3q-9uhn?*&P$@$XsoQF2USGa==Fb^ zHqwT!n()xNDbH37By$}~z`wm#Px5s26$aV?RmcV}19n0{2Oz?s#I%KePvP$pS4;Bk z6r;d|EQ0)J{hNXF!L0?d#L#$XiiBWSKDl>WsO$*YhI&9tPGGZv#AeRzeMcOP92!N^ zb=paRMiF!{c!W2722{j=Y89bE*m$fdT#t>tBH{(%lVV7;g$$9t(D;R?k$g~)NkHo! zSszNwNHSxl42r0r8s;fPIA7Oc1kz9%DAHC~4LwIbMiR+?=g~b>7UQo%AHm#>TK{Ch zYf!YhRreg}!0pma$r({uTj)!Q7?>~`UGOV%ZG>bQP!AGi%5%wEL5Q^Yrhn)bj}hYC zE$COe&V|qpZc7(n7q<~@!I(6;;3MQQa-6^{qH9E|Ckz_l!>2<5E4N9bF(Pa!4M>$h zfG{Lg#)jh=Otg99KSU(squqrFeOOm$+EQ1xsyYcQg z86<;wDOOx{m{Q48f)vEbFGNQ|r0B>SQG?QhW9P9)OUQ>@Of2>5x{Sa@&`H`<+L(L> zA6L&)r~^GK>iY)Vv6&|}bM}fT$dRqu9TNt6!?#t~xHeXINtxD6iG-?>ikjCqW+%y7 zW%YrxMM1){3>v}0kndTUx{8RMa)>MieCruX{X=QLS%%k$8D34~O3&$6PhU8_aC!myI}a8_pH**z+%0;4cZ#`dyIUsj=pSg?7CMN8 zz}@DeRkv}-jTUWpH?@d7p1z`=bDJh!i+DHv8RE!ARLiC4)-(^DFPx_p%f--b*fl}f z9dM2~6xt!aU*g(U`EVcEVntxe9`gxundwhU*`7uQI9t@w4%gQCFlxs z#o8tb?L;_&k+6%XB=lhrx<3&9vsnmgY9J;}Ua(;YA(}Qz(9mL$pO;c4rY-a*ajV3T z5-g?)gkZyCglKG_rZ#S-8yPy*Q#UjXubE3Aan(;qFA-Bp50QXp8ymM7P=8A|_4vO}{>Vw^4r%yzCtY}~Xk$1tl%&muH^mfp76qc!+ zgNm9G=if>73CrY*I-YHG9h4S$Pu_zmk9Z|=f@12G_q$A{EQ{$R#dK6M-C;6)o?}Wn z*iEe<huyA)7Ee2rq5SO%v}2{QOOFt}#BOfqHv zZh9)Q^mWs0p-o$;1u&E$s-*2owdzQLpY}pBwzyQE?}w$=w+03aEG&LlF8IKq;$1G8 zUzCxdw=ML!oXNnWHp)UKh73nNmnnx@FMU4>U*Lq6mlqf_mM-A92c@7zV(9V@M0yjk zHz@aogrxQ4$dTFgbQHC+WhqrFfgX^O-+#YPQK%?dW~Q)O>4*&eR;vMOwCe+b=1Q@x z&=z`63@T9=Q;KpDIt#&O0W_yc!`x`N9gWpC5cEw3OnRGI<*GTj?r5;O7aefE>nN|I z*?JK>i_#r)psCg04vI_UNtjQ2+gv>$8kpR6g~J#)ZK1p2c0t#X`gKPJpf=71&vQ2o zd0`d|(=_~D0TPQ{7?GdwhXQNEZ=3X%H( zL-Xh+V&7GIF^#84yT5P1Mh61s_QtlUDy>qZ0ZMTbyO5PeA&%Ywz)gVlTb z8V!pN>@HYFyH4)3wl@~;5er1n-HR|V0Lie(u3iq!C+&FIyH@gRiBe=smJq#fOLo5_ zTQUWW2t7tOR!j7V&j%0ZiMGnooQLypI744QW!_IA%^K}#Xn7|_dxze1b5m37)rb^c zE+f!FO7LlUN-K=hXpt;W3_9dpGYApsd~cUFGDKw*YhO)%!u2ZTpytBf4X%&tydTH) zjjC>JE6d>#9~mTDv&q224+xMr1Yp;nIY`#N9WAO3mjxb_1rFfH&AZ;<0$0caagjCr zO%}LO7I=^fyvYU5Kmi;HeoQUBHKmzaVp&_X&~8e!%)oUh(OvKAiQDV?7z0J@W%STK zRg>#u&TMAj$p%br*fn_#!eREvN$yvu*s3WaGLSCh_zzLvTb-LCLOIj!hjhi)61HNQ z@_*q4=#O@|8u}Lf3xV_#S9g#$QrjsJqgefRXaQ;wd!*ILNX_1YaftP%x5!K7X3xzc z_rXp^oaQm};3Fhe#QWPJLv8Ykn#MpS8VHay3k{9%ciC3jSu?lN#7q>Dn>3-lIK%)6 zw{4-DVQKmfTwD}OBtMazq-SXus!>M^@rk|&ocs(r-U$8RFQR#v0L9Y|-#{JWJF3i+ z#~;0L3`1d_qyQuN~$gbX&-3Q$;w=(Pc|W%aIRcjH}>;jL$qs1N?~ zb~v09+%tU_bvT-~0sLFg2z*>rb{pDMy9_sXKpB8a|O_9eItK%Tiiq$bC$&I@nNWcfpq~q`?(jNV@ zE-l530+HkYALwXX#0yp*3Cb7&*2Z~9q@gQdxju2qTr)+Shx`JpsvRo!Kw0Q|tWOqn z;ISw)tc&vm_=X5p1F=;=3wHg=|2>6Hc~I6# z_kR^G+V{zVd9SM{3r@$kV}G(>Vy9_+HCM*Y74-!BG6eC)rF+n$7!@?HE^u(`dtsE| z2qIt>NX2|~u4Kv+IA)qc?WA(S84}%?=MZetg2GyoEYXVGm_34G^(Hz3Yu}KUD#~#3 z_lwuW7!!7Trx)MhZPqF(F_Z(#$f>t){zwkm7J3@0V`h=79W#FDDNpp(hEvvt^B<8f z^Er^nm+vJPp*SF>j|C&R{eOh-HHsyMpb6U`RtWbwTNJ@;BW=?{Pv&SVh=Higo>WKX z-3LD-r|CqM)P8d#A&2aPR_+E2O`{tus3~=~9MrTgM9BCf0Sor;`=15ap7sNtR(l8Cke2g=%yc9_i)}0}n9?>!x$H_xSpjjcMPWVge zVLD(J+TBF<#;N+IR*XVqR20gylic;a$ztWsi*(HktSBhE*m|_84+V$M%C>!J)`=o$ z8XtU%pF)sRO&4@(heJ^V5dOr}1I^9Tb4msJ(LsCg%j#W>WIV8&42fui7}Y^o#q!8y zFjz~plt3;FccA(X6y6TlNu3A{=!A*Jh0rtPz6cMQ;HDi&JWkP@Tyr@ld%A1UPrpB= zg+!3z6lxP4c?b25h?OR48&HQ%l02>+REJS-t;Yun?;_^5(C9CyYq40t5j7n>QWo-Z zXeN9Ehk%s-z$YmTo1)LB0z8YnE^3bS$4mB`{%B>-a_{!(cH$2{-YUT676F=e0O+HG zQmCfkAEi^x4OBv=me5sXWx<9nDp&tB0mUMEq#;BESw=Wyu%So8K)}@^fKduA1dEX- zPz{~L*d&2Ec@<}Jt>}>!(R_6JSghvgfii59HS}tt8#fD`WOvLW+XWj8VPpw{4N2mz zNf>~+8y^=eoV+cfQjHB{4Ae+{fe)LA5@`A~NyAZYO`pew3Qg1gDSJtZi^Bus)#z~g zu)Uv7p~{1gj}U-Y8k@xmBPjEFmEJdGNu?!Vmd#?Z(8z#`H;I);BLf!wc%H0bkpOb7 z5p0l)3|{^;h{Z$WU6Pa65O@4BVhtfn1m%*UQ7%Lp8%mizHV8lK7rWgXb>F%iNKu#N#nxJ=Wq=Vk9nN1;b>*fkLd;RKYA^D(dc&~)3{ zrwT(YH+aa+RbMG0LnjOrk;VV=CF*_bx4w+&`7XM-We<%&^voL_uwzSy7eJ&V zJ##d`{n;CGyQ2%-UEtzeUWG*43gK;t)idj+;g7E&bJKKZ&KoM!Fk_hpJ=K1RKD70AcFV~aM(c*IGlkh&;i&D=rBN$;e8VkiSuOa6UG@2B8z|sqd54fTJ>!CIx z5~Yn?O<%=G`Q{!GjtK-Sj1Y`h-F0Y*C`->MiHSaetRrjK9XiMDCK zw3%eZF&X#tYj4;igy02w!i!>K&wb0fo^qT+*4!zix7F3*+*&*tsQOu*HfgJ}BckVe z;X`usy%&)hEyf--Quv?hM@iepqt2$`4+xN$G662?hY3i%`WH=0)BcV;Ib4TWQeEc` zZmvZ?=tF_>^ETIZvR?~GZG@9hOl5OYS`9&yIChnR;_l65GMwb%oSSQyF-p3?TL1ok7hh8 zrZY-0=hZCGW_n|f(w}F3~dkU1?EWTah>ZoiyLc9#+;8!6=VMFQ)-S<2~~6=xstN%G@G1bPv2hA)xOG ztvY2B8BrYyo9iumR{Sc6wv`yAZT8+4>nCa|h4yx`6a8sNkpeY2)`hlP;0no7i8UD=F`SDYC&Irm?STors>Dht z@%x`W!Z%t-#2=p%-$nWINO8-gd8qlV7^d)2(Mf9vjpoMzLH;G{&LHd*S6Ag}3KOPh z4C$d_dXz%Q{~Iue5^#r7k@LkDLJR$=A%|(aV}C;YbS$Q)LZ?u~2##o$lbu?1 zqzLn{8JbYU95xY@@Asw1?74@xWm4Sr&nAfjb^IdNvVMe+;=V$-V){Y7vOjef&`2k3 zGavo1Qs39ZGncAuOVPH*!h`PQN3caezTxUcst(&<;yPHBg@-CX>lB+fpH&|W79OF; zf}~Bx#4dNF@(mJ0*`3l%p9dR7s_ed0WY6>fov`uy^1(NBog_E8DKvp}8zt7_Yh!|` zB4nR}EK}w1ySoOBsarViE)9_k1^0ezAws9A?@A}-+``XNgvRF9np%prQ}~J&y5N{1 ztdi@tg`N-&676?9TZt0&cRc%^p|GXRE@|*5tbZHLAE?V!tf-(+RIuCq)XSgczjxnH z_Bh zr5T_tbcp(k7Ipjt-=mbFiw=v&IEma@duNwfl^m?Gnps9de686tl1a8bvwn4V^pb8 z`^L6NAme~K4PFFwx1-i=2$owAB%5{jqwP%E%!BT;T5%$r3Lgx8x)Xh%od}Z}(0Lx| z4GEYf<)$kn(xwwW@^ttDwB9@&ZsF76U*K2(nf_USA~3H-)+QpfhpG|3&}a)0mpmON z4d`_E7EsmcFl6DcF@Qxo9aenb249rZ;RT`%*n0pZUxhcSt*XS(L?YJjSrmCXoTA72 zWz>_X^-au{HtR?6JLK;n<{J(P2^N@6Y=7B@8TgOh(B;%u}ceF=}ig5bq8pxStv|WWR&{cYpRS zh2>X52xPP$(g0~a|8(ilIv5{gfzB=xI^1IfPjI*B zbAED@2xCNP|MpplI~EaTu%56iIsH) z2jg!Ax!U8fuhsr8>P)`1nu0NoQr^RzsT2f9D%FAKTrg9UlnABZN(c;1&1aJoOQ{}H zsh+P?3i=79I!LLGGT4%p65{G=DKB92P%EK zh^3-(OAn3Lej}fv$AP~k*h1xz z+Gq8I*t`R*=CjJ{3Ep8-G%Us=bcff6;7zd z2tK?AZ^hdXe)J?~=U$$bWiw@Rg=j0LuD{bLtao+dSXoZ?ov68^`gs&pb9J_WV8^wjV0~oBX`l-6#(;#b&r`o25w|L~MYF4S?XH zeRFyw65r9%wUF10!nf?9CW`Gn)gKPCKV;g_B`t>;NP05OAj;mV=VYvaZE13)a79Op zhC7Jg4_{?HEn#!ZRw+o<%d1^=I21kn-hmR)UA#%BWJekDF!Ztcz_pQDg@k8Dv@ZqC z1lm05Lv7}K!Oe)Seu+?0+4(E9q-+U~T}1A6I3WSGW0yrdLqlEyW0N)_ickes*NE;= zD~4l>Qddd&v7!{6KSRFiu_(6br|%tK;a3Pcn(|R(_p!V3r>5cW(_cCh(T1P6RVrPHBPny9dgAKe)1sm+)0&N}>s4N2~??C6`PYudH zW$-(y!aNudEubtXRSY~itD5zEW(w@o>!CwJmPQ1)QJ2wg{ir6wa| zbI=@UMjD|7Mge9`E35ohM9r4Rz}cojuBlT5B|9dCULIxf>_J+1>RY$GPLa%xxSUmarYQgg&Q<`D1#x!?In3 zp7fZPsZ9jsRh=U39bqf#*Cd$f*mQ)#^b12oOrazGbmD8#G~%cAU_Nj+<#>r=c_m)a=&8iJNbzb#!6GDiJb2GxDPAQ;Z(N+_$tepIR9rT7 zst2$B3yAkh$_it!`OAw+QiUA7maz`;)=tMYt9^l04!rKQ!f~l%d3m}25{D*NqVV*> z#nn}n7yHVJ{naJji}8xDi=7iEI43wSUXGWtUaV<10q?Sm(xjr|8>m?Zzi!pY_m)+` zHio03vb?0a*jpk@IUR2uTZ4BeN_?>w?@=UzW36LeIbP6;uQSWb@CH!7A3W6+c>k-n z#8FBlN1zDy1=D(VB8!Nx#;rLZ5k4Efb8B+>xd`HlsyThm20IYt!FyJ5_cC zy+9RjfOV{>EH8DGRr~!8UzLNL3a@ANl^D{U(ChPHN%6)}PfIJWKPyX`X_LaSr(sPR}qch-PQmL%a#a@cXn zyAwN;_@1{l9Ige-xeI&y2=K4o4T$>>0XqQ){WKiz1w0LK1fEP84MU4|)Q=2{;0eOb)ysdIFvbNWVwh0k{@$=mX*Ky?_OPdjRhNJP7z} zzy$mj^t|oia4O(7KsVsKfb_47hQofqI>3#9&jLOKnDS6K+yR&h_z-^Vb@#8(4#2|?hr>qz59|nsN8&j9sb7b~ z(*W1wu_(I1R7^a6VuUU;r=$ ze{1ekod2W&jt9&G^Z-@>ZliM{!0T|D)B<=b-~qw|9tAuMI1+j~aMn5vFzdJYkqO`e z!1aLfI0Y<$z7qiHFIdh4YzKS_Fa)?2kB%haO(6sEa7`-Ug@A5AdJ@ME_zB=9z|5!7 z&wxt+I{~)?_5ywmIAXYAOvN^Q7T}y#)Caf;cRJOu-v? zZUjsNyaO-~@TTX`ZoslVs2A}2fR6#*_&nMT*a3JH@B%#E@4&CF-GJGEivgDb)&kZ6 zh5)w#p4S!*w*saCb^+D_CX6tQI{`-nJ^?rja6e!P;9G#}0nceiKLcg~?g3m57y_() z0rvVP#xH(ik_q@KU;*GqfC0ekUJ8e|0yYEg1bhPU0N@!fLvO&HfFsXDf8pVSX@J33 zpf})afOUXh0&W8wf#2b^0zL$I5O6PG!dZqf4nIB|4LBQc7GND<31BPWdccE#_X4KB zihc&n1`Gi%156r;I085haO0m~Z@||8D*%rGHUqAE4gCf96krFz*D($N6FS4;VP_*w z0A>Py2XGPK!+^Dbqw%LPwg4^%YyrFzuoLhxz+S+Y07sl-82tfFZyEU9j6Ij90)^z%77ozz+ad z0giqL@dWU4z#V{#0XqN#fIWae1WY*(@&MBSzyBWW4!9k#0`O75X26#KcL4SRb^zXT z5bXy15^%)%c&qCBup3|%;3B}+0BZqLKY-l;uLNuX^Z|AP)&uqez6&@4hf~A;it>QV z0T%)O3a}RN-Vp2txF2vQpwSJx0gePb3YZQ!5|8ra0!{;50k{P4H-L43oq*c_>CZ&A z0{$Ja3vlp1JD6@ z6fhfbtOKr-0i5 zM|^~S2b>QW0$c@{G}bV_3rP9cRzNpk%Eu@Vcp=~hz*&IX0q+592W$ll0lo#8bRqf^ zFct8O9+U@616&12fAxF=U@qWxz{P;=fOH|OiYxBM1x8$LO59n42PHMfNj!$4(ecA@ z7$!pHgD``@2l2_or{#ZOvv_5DA-R-gxgjugS7&$j40f&OH3IE<;uqUT!l z13(`HJ(m2VpuYn;rY@`eJgYpNxcw1yOuZI;jzy=Fvu8mU_EGICvgk`dZwDPA!79Jl zqOWB-LZ(GuXwkQVz6a$Cbb9=if?*_ZJ3;RSJy!VxpdaaneiZbce&{37zlTA;F{1uz zvwlLy-I$jvJZI2PHfI~gS;~LNPv{f89e)o8^(36{u-fRZA28siI8)Ag)bl0kiPewy zf=+s3YO-V$N;^><8}a!N^tS;M-iEbRU%2WA#Q#b1k-ZK>&OPv_@%V1?tg`5X2Owtz zJyw5>1N{-u59;OP-!|Kk3wqU$W9yj-JCuQb6UryJYWXKg{e z(aRTGGPiI)RN_&?~s1N|bMzQQVh z0Q9|}M~fd0l%cGs+T7nHcJ_!fe*oF#@!JPFU9wS`w&TV-^#b@awlTG1D{Km z6`w5_NLP9z<_6H`N6=-55PbvaPk;umy;8~~h#MhcS z90r}%kkfVgTC4ovfre2D`a+!^F9SB|I1cpfpgVQC-zuLA`ct6C@+m*)?V#uA<>P0W z`fUXL@1VyTpAUilWsLeAkhu@^b(N2w$0L$q0Za3p`jNSamG0=$k-4_1-Z3?$Dyo zu;`D0(EIb~@qzm3RnU)up01ZKwaOm>eJJE#rqkD0^kJBw$ANxG4|xgJ?hKf9|LLBcT5Y z^jP*8b`o-U(4&nLiU~*ILrM4H!7qGIxC;v{JtjOO^>lzX8g~9A_-KAk_@2h6&GGb8 z|9ij_ZEmBs(BBc*3i@1pH|vR)Nf6OnL2n10ax#-%E_EPXw?l^mpwr))PZ*@>fN?Qk zs5J*Cos98we>e;dOHP3$rw4NOg1!UfgjcXQ)!Tf(#g_*;0mPtc)MxVK{$RlO6YvN9 z2M#Eq8cA>3KYA8pd7jRPH^h=8(wly>+k$S6?nYmcoJ(zTc3N_hFiHFf?Ta;+q=J4A=$LJ-dN`MJ0F{D%=)}HR1HLDO zFtU|>;_HMi)Mh_qtb>eFe7EQ_0U`QE&|d`ooe25?v;0G#Z+sve_C?TTaH8`2Kz|K% zYPTt0%Nrhpj&FeeEszu3nvOF33A@w&-&^3>Mm(t+55>_R2~|Yyo`rq4h1+AtK?nM! z7Iab9M_2(UdvIN(?^?)M3!b_7Zqha1-U|A5&=*9|Wr9xScY^)~=-E2`J5t9~;O>PE zpM#!)IXt0E(_#Mi2Q(#YOspR`;0y8q;_U$MTffA6e92F)(s*;425d~IPaKe9`AX&x z@IM%P9xDL-bI|AMdTF^+0Cd^|j1^xufj$BBv3hxJ4tfmqtbXXPf<6QEXue8zI|BN2 z&=2bJIgfP!Hw;PRz+Z*KAM14L|M#{22hV=+5Wm@X*GM|~p96hE^uzd`@J(#Oss1N9 z?^<%|Am>L9N1sa^C`)hl-pBNW_pn-3aAa8=}W9S4uWnx5I%@T&g1!LsY@N4YqLm+~X{Zt}(d((JQd&}$!yJ>PYqu1e6?qIkmhw7RHYDlMI}vB232 zzF1><8R+}^q1S=FAM{vjoNb_=_INmaUqm~$neAu=eGBL%5p)^!9l&*gegJfzZc~~= zmss&E0nHorWbAk~8ua;~Qy-YN)Yf{lKwk)YtiCS+JrDFvdU>+rZI&H3fTuNv4C>GA zpg#rr3SCBgk!go^(CJTGMay5Pd#l`{k3GrgrjCeZ0 z(+nBy_-^Vzw%%pg`UrS3e;W?Z(Rt!srY(md%w7w6taz9S`V!EC5#`sLOr%y;`ToSD5Xb2Ksu?FVpGTI%)~%_kccGr=y)z1=YV6^rt|N zHg1T%74*mYq3;C!VbCwq<*%@8cmVX@_EY{S=#Tb8ABi-G7)WvBpN`!|2k1vo{@W4d<-&;CkPZ5Hl#dm^mx1mFJxwovy=9|1(AR=K zU#G`g`$F45|0U?La*I~bp96iSUj8Oaei!H;fF3QTkp2diL`07klPKPY;3KbsJ^}Mb z!jF+1sBxt7Vuv-4)YF&j3Bz`i17tY3K(B=rn&M6rW_ZfyVC5mcML;oLj)R4t!QS zWdNsk>;(NS(4+Y-(GP&W5A>B0<>dmD=tn_69nS(3N6@bo3J`rH8n_Vjt0L$!01|x~ z=o>(%HLj_jw!U2gI{k?%RAbQ#t@f=2J=hO@E9mv0$BHdGL4Ou>r!HUHk39hTtDwh< zEk{9*e=hp^+X0y)(a7mlDv=0zHsb0rY6`fb?Gv`t6{{iu?D1es@3R z_kg~=pYjJme+=|k@iYO1{^+Bb9?M5(fxZLsqtP8`UkT_Bf$r1oC-ZhOI}*4=YT#Nby0PdM)6>52En8863yfV;sL!CkISyw<3a_k>HDlz6GJK;93%B=HAv z#{9&ajNftz;utys)N3wyJI>fZ6#z5D30Ro8+StZy$>~5$e|0Y=vV$&)+|@RH5( zO#_VIGKz}Q=j6E=b#caKt%_5+;uC*7z}PWhx{yC?3d(vDUyL(0#U=hW&iEIz3Qckn zAB{5(XoX2+O8$d!2nq>_e;R1~54~y=xTM0wCB_Z%yQ-StuuFlfPQ(ii?~O~uSx1mt zrFfE-C;E(=xLg8kNozJpH?Ejph<`D^@JHO7_~(QLPFUcC1x{GtgauAm;DiNESm1;O z{+C-I<$S4NE7lnLRrd#Z=MaYkct)S$K8A{S3-i@5ejCHXHa^AwRnXB8oukf%ryQKF zS=dUZOWo(&?w8u`>1{-G;qeH0IV2duw^=pv3$}X2)w@qZu}6WgN%GbZYi;_@?Wc7r zU5ftCFXdwo9RnmDO9*jwa{bsb6W4ZHK;UAuF-dHO8T7M2abe3>TsTgMxLB+adRe05 znM%*y(uE^BaSdXK;|y_Sv*HTh!UU!F1}=}?WpSzc|ILqa04Z3%RzxZC+SqT5X@XrqJFMHLK91=5|;X;Nh7}hYnmEn&V-p}v}hI<*l#_$7% zpE4Xc$`IEX3@>0fiQ#O93mL9pSi|sEhCgC>Kf@;&?q&EI!w(pK%5Wf$^D`JqZSe0a z=C_D1SInF_)sc$T?pTNX%K(n_wDgH-nduWF*!tYW0h3_-J!~hCpGU(JUo({&|2=NUtc3b7fB4GMw9ISJ#toD%U3#y`k-75~H@ zEO68yk~40mWSlF3F$N7L{Jr@Sq2kA-jNiUe;>Fn#zIHOcC0F7}Z@TEWBP3@dS4ihR zbj@M>s*B})js!+E@bn0W{kom`CoPwhQ<>of#t*(kB2?LbGd?g?;xAzSQ!$`PAHUfA z!+tj7{ftjwe(@{-Xg4$dJ&$Bmagv@rB01aGU<+B!OEKiU1N*--NV^&efCh?l29 z&TW(BJw2aF*An1KzQZl=Rez-*@T#^;iu`vH@Wh`}B=2uvBK@9#@b}D<2o;C#XZ+zZ ziRZ9p{F(8$v;S~0<0#`(OC&#?1Jjj>z)$k0UMTMs{|d&Rce%vxkifWw@kbX)gz~F< z7+>p?c%^4M<420pne$3w;cZGq>)8lY!2qv`*>82E=-{-(uJ zNIT2=gz9>${-Oc>%EGH<@Mm&N%wQCyt$&HNvCgX46 zfN=xkR{a?x|T5Boh$FTdgJGe&*He3Bgw{#j92Tla~XgBFe!gM zSE%}{9QbHPWQ7TNsd3+BJJN($!gG_FQsN%}0jRN|H0Di}YMCnzQxKV!Uoes5!Z(tIiB zLe`&tqC@f%*#A{~moeVn-g_9o7C+Qb`yY%pz5zVd`zb3piV2r9{<|E1xSNdD(c@)#9!^Ff@X_?IVg3cSxc3C` z6bIBk7>19yk{nXLJ)U32czZs1HSp2uy^Z-RrWxXTK?39FjNiv`OpUu27=O?&8CARf z&iIsN5|3$ATw~CoWdA+a$onAUS28}09Yo2whw=7x-1Ch89>)zeZw^CZM)K`>(JjD7 zYwr(%r*WNR8`lrAoVAOjf*7{qnveW|OCKuR-ER{I_LG#5;_? zo$;@8Vt5MUDGw%o`@X?p#=pbzf6Dx4!H}-kP;ce_;{A1sicr{)=V!Ya4WpTy~ z30I#^U-e;}?46J z_`iqo_Pi?<9Zhn!bG^!LWsFZ|M>xpx={I&++hA(*FXdF?+li6E932X@5{iGo@E)5aUR>h^fHNeu*1SF#nsJn z0%h`E#j$h8OMUF)C5Q3x>~Bi{p8y}NU*3*^|2xZhc%G~`TLR;9C_w#n+fs?hVSIq` z|6oT@^$xpOw(FrulJPC(Cp$*d=Stv7zCDlfFy5Yj{+RJ!UM>YIe|VVj_VviW8UML$ zo|*%{qvqQf8s+GMS;W=K_)l`>{ZPiA3_l<_ejcYj#%D18ol7JFX@I(KK6Nk zEbz2$vhTA^WPba4t&s8d_^_Sv>q@0QpR!u-F+O>vL@2v0m?-sGwL;=~*ci2pf7ljJ ze#ZFPnUa42%YUBnbr|<_@h~$!Wc;6}Nc3WCp*RsQ^c712El;4W@P1i;VjBdv7we|1pEXf~YJj9CYr;P8JD({v5KgoFe zdcPO=Q;oBX>{ldz3dhNzXg{_0$h8uWuqm!3jNe!&?@Jip4E(A5T?c!Z|7Se!dziQc z`jLG5xyBC0Kf-oUGB3q=CVqQn}-`{>#@SkPW{*e`A(8l~pmrKMq8UHckD|w09rnqin z{7yz7?1<~9G34xHetVvITDFuwlAX1B~TjALc zx!J~O%pcS)yZ@Ilegw~(Y8*Yn__W1Rt{T7pV7y(QOW@zp?70|tsyCkHsDA%3m)+?kB9NMvj3}b@iWFhSuFYC2IBgV@pAy_ zQvPs$j;!}K9`9jEHWo6z=UWn?_%{NtY{IMpJiz?+{i4Z;Dt@h8Kt>7u`-LsuH& z0_lJMHS+!(35;6cqv`)9^V{dUQRpXq+<;G@a$EB<_0;aaZu4~)O+I*C}v zc>1v~$+z!Ir(pm_lRq1H;lbg=cfUZv*e(?>hK5^Y60hzvN0; zZzT^1WzV}9KbOZ#It%LrKAQd?GrwJ*F-R<_UEk*Zg;~V4nDJS*xc5uO+n*Eo3**l$ zk>!W7oYbqN{HKZ}LiNi7jJNLtJi~Y&kGq2`=kx_q&UGG%P;uj0#uu}LAl!*-6XWgY z&f9@Mjd^tNC+2@^oWv?Q1JEC2pC>rZoXUkJGyYxfUxg1Sye%$$pYfMgNCC<|e__0R zA3Lr<)@#p)Co=xg@<=&1Fy0=29$>sZ&pga{!xjg|VjiXT+V>4sGyaQ9WWDKZhb_RX zwlhkA$C=-rx4Z^?wE6dMz&msS{QW4)so*>cVMkoa3#Fb@{PJG;!%d7opXZ}w=Kmh> z4*sr#pE3WmDv3RhiS$Pd$qxA}pWV)A#5f`RC*vifiYJdU-hK}9cgBAJd((BF1jhPB za(%RHzeK3@(Vf7P{EMea{B-8u$$0y@SUTo!;&&{QjH{Xd6UN`f^8%+k#%YTse;yBX zWw(6B8_b`^aw-^aKd&@Oi+p7hiYqFND)CAjPnmoXhCq2dc%aVXa(iYkm_E4h9K(_IVZ=BKF=Q#^%bzA|56mKSeRDPMC%bx~zWPM)vEHJM*V?CkH5V5dU`oXX?4I`>zY(uMhL~lHBXXn6@mV&QF=zHw-UxH!)sAi z<7F^-fs(gurLWAJQ<__vk?zVZos^!5e>0{`rhlFICw=0i$(dOhlbjQA<0MRG`jpHm zlP0H6Aq?@QPs*4yB|U3WW*V^&HZv_feR4+HR;#EbJo_Qh6Lr5Bg;Dt67q$9&OOMx8iuWXWD=W*&$=*fu-kj_!a&uZSc`jb! zQoO3NysSK{q#7@t0ZWb_{UW~RUk;C)TL6+Ulegg<3gtuCr?EyyQ-gbbANiFd`wm&epZe@#r9XAa&IQvioZcbWxI z*NW;g58k8YU2$bWn&-*_r)O5hl?B*<7K4M`2>x@Ws14&K%LDd6kH}4SlWQo*|Bqi<6Zujcw8Qq=9Oq=bSBhVZ<&ke$ zQvQ>kX@5(RdRr2LLa`qW`QI^sTrS^&0y1&|-W8;b%Y)q|9dywmPsW7I3E0Y&JtD@!7A0t#!cM&!NZ#{E*;2AELl<0&5lzCUHp;BInK{?B*EQ!(S z_7;`Mph}LB*H_WpACR<@n|-n2Ru6lUE@8QH5U9TXLc~`J6m> z-hxtBjmveVt=v}*P&jVpjo!*~slPKrzFo~%CElerado9H;C1<^;d7m73w&iOXH>7i zo56B&OS4?A$wehKMR-RT1q(5y^s$WDN6PbQ=z9~q z`+KO;#e1lV{Zar$MLD*zt}FMI6)pFRAnnC#=!z^W(NyNC2vn-qUa2z5m7`b$U94ii zFi|D*MRuoKrfxGe?XwxTiEizX4~a)8!+dBfZR!EnW~7CC1aMx)9^PUWm^R zOhF1ziv9_BYxKAwwVMXcAGu%VYNQXCH8E_WW>9+bCuF3t&eDg|eI@9O z8l$YJth~za^;X!E0FtT?MjFm@@@x|ioNO|3i&cn=&_m6!Ht$Jyl`2<3D5F3oJb)(c zm44rHq)473yy~$s;8}r5IB#yTkBpq=iK2erNR?SpUA0QaF^tcz42sBFCEvX zur8QO3guks!Mg>0Wu9uhbW&O>hE=VZ-kHW`$9Rtt*I+?=@DoFQW_k|h9@I?1ojXOo zLNjN%t0>>KJffq_*@TCDn(P0EArDoNtim(OS7UxFk%J)0@UagDyI)&$8r0T!_}ZCp zI=qVBl_}>=_ylH7q+wZVG0imlF}qkbT44v6(s|_GPM(%qhY# zzwA;b8Du264JbeCx4vC| zZnQ#PI?q#uRSZJp%D^fWc2qnvqom5@BC|AJARMQo=woFP)Tpes(!b-gp3_MSGP&}S z%Ll9g+h&7&)s&WtB#9rd}QVVTftp%R16U8YrpiKmtdwEmRrdo8n@MeL& zexl7N-h;Kan8cm|cZe-;L{G^nU^qQ+{O``Z#?hybsZm#YO#`~-p1U;n}U_zs%mWe6nN(F`c*AoC(_mmrWZC=25rWc zm%{&Re8pa?cVZ4^G+tW-TA`=!3~R;#wLwI?Bwst$eC;gPHGh)S&{~AVY>d@+Q8FC+ zP{DkR**T`yeBG8!_T(dduULcB1A#?lA7ABqlhwHext@p8X{!zLP(ZDlMeK=o$Pq<^ zJSq4;+&@q}4?9=V5!JTQG3A72G>MiHeswz7cZICEapZsyJ~Zp<3D_y0fTcK=;n=pT zDyJ1gV2!7S_6zjcqG}BeS4!n<;fol`B2l6t8ri{751y1Zw3}uwdW&n8%gAX9;LZ#_ z6_eqZw=1Dkf1BFQw2Gq2qEZiSX*pT0Ib1b|iunq9x$h(gL^>_P#4?TcE$BeuIJ%VP zb(!Xi3^>z+Bsd}ihUuo5lXCe=UvYnPvTX`L?ighw8WN-KJ*ds;zSCzvbCkBY%22U2 z`T`E-U4V!!Jja>o@!<53&X~NOB9AjC&+G-n8I?c5qFC3%B*<>D%WHVBWEH#8Fv!ZX zYI%Sbd03Y2y#bpx#VEmj5o-r6!z<6?JhaP%?VTcQG0}OebY7fyWn@YT|2vLA5ZpLY zS|bAnrR^YW_+m>lr&MfK!I$tqxBXalybv{z5?Fg(6bp}OtBeZjn`DdsPe)POhE-8C zMs%TkU0>_^N{tE`EDl&P6_4_}FNYf4?^i!(i%=WbnCAFQ* zD;($7TH&Y!Ky5sMb@3%`Y=4kDQ*YXv9ajV(F*566Kye z%wmNYHJzCQ%J1!a9C=)O75awYaqldq!_Rd=eRmddUS3?W#vC2aOhaV^IElq!inq4d zTS2D|5%I>Ep$=5B*IE_8uBUdy8tqX5bJJaH+eAJ?;6gZ>;uQyK9^ZmHBHz~qs3#My(?**VeF zSeU*l$FH9$iy#^;3pWQEuOYQZjcm~h#|~I2tnwCLkHwu&?7Fy06?=X_JQt$vspHhj z)z@7x_|TNH5)ZZ)=__{hJn&tyVyu#Trbq#<_X;OM@Nwc~xU{^+Yn?FoaGI8bg(DqU z)48#r;!w)BQn;lcnf_O!yblHPui%N;HaqnRE?S_FQT{k*4SkPDJBP4^=cpTsDzDYj zIXSmdE*>ku^@*YrW9H4InpmxiELUXCm>tY>4r{*`)qRb$g+@3(bA*jX`RGO(2EFdf zUw3YR@o^|oR8drnrH;zFzN!o1tL*I7`czJ~lu!NFdKHsu)LnA8rhUT;D0`M~siJmW z_>{@i)aeYYf{ROir--v(4^GsgO&w{r&DUL3@na~q4aH0xdmaDNfRx$N z@=0!mm|0BBXEo6pUVQ1BZ~uijqfRX%lS2w4v{s)`hK_4-DQ82w`sBkt^|%zZ+7`54Z3iFk`LSLeRS1 zX3?)XinWPxB2DZ00e=W=J$+A6%2t=^du1~9 z`p<$H8M_!clG^|HD*K4Z&N|(dD*S)VUCoOeMHFxGD=La0XaoH({8aUER@{%sR71_oN3!1rfoEp1kHF1o0rK7!Th3Itcm?2)TNZTr~dPS9Mi& zbx(Ki3K^2Eo}PZ))zww6-uu1ZtJWF#kPLb1_ISRBX2nOHXlbPnRB|62A!D}^M6@;P zUKo2UkB&OA3Ki#_e@_3&+mFpvohTz!?v0aTCrgDca$5-Icsh8?T{{FJib1SuOyZb_ zN#Fq-tYhgwf`v@eHuUz8$s=3y)OX{nQgD+3liDdKQ<>b#A;04qjm7wq?i-KE<6^9@ zu47>6A*Cj*P6vko;rPb5O_HI-Q(J)|_!4o#$O8`jGgMr_K=fD-TCEAJ(xeE@D~R=uP|ZIVHX7EI%aI82h^4rTjeM% z8`MMtM|G^&!7%7k3W6C6=@@NjXi3bXcka2*##7ulRgcMH0&I3hnq0b? zX9ZZar-TW*Ou(|U%fnelp9hAlgER7na%>q2P&x;&>)jb}*$bH4e7;BWZ16yhONT)) znf6~&hJFipl&u2zf$U+}O_b_PzA`KwcB%TiyNIl;mGDr3HW9n)f}?ji|FN;LL1sma zW}y`i&CGB$8xK?NuQ1H06D|!V<1v`m6jNM#Hb6Vo6+R1;&83qVp&mxslkjsko)&Ae zinMRwOiR^NO{8FY6mf9qUf`27;8ASsZNKVuT;OjK-=j0No9djEv5efYy`pifimSfc z#oU`bvWhwBgOxBjM59eAZclmr`Bksvf z))aB?zmbTzCovu8Yt})>3E!=bnq*!g6ifk}vOuP^48y1s_zcG`utShPED0tGKI^R@@RULrG0K~EaX3`MN`vdr zpU&3cOu|g*0=iMV(NfQ&%<~j4nrw1^>gqTS)Il(M*Nsf=m=(z#b7*lfnvd%SP(a_P z6F{+k+r&JF;&eLA)J}19AcZy`c46aGP>x6)(Y&r$FPUIXR60u&=u%MWEi7d+x+wy0 z_t?1KaazButYAvNReLW5?H#gQ$Q5y(u;%fj)j}TF6*3PT#G%QH>hAv(A*2&h@24oHARhK_*V5N|SLXvWmT68#-bUjudVpA{<>ydvJ}J zj?~bssoh2iCXIMR;zH!)7WI5E9UdAYPz4>WgsTb29i;ePT(73-X~hR?#ysjYw-;CG z@upgg&J|`qH9Z5__hx@0>l5>^@#yNCEm(X=Q?q43nhJXrm>4sNQ0djN3lYp8u;@>) zGx7^@$}G%TFhf%L!`BC~jGUA}^&(@Om$FGP_zNfnLAZ(e2W09+u-q||hf`1v^%Mfx z&Xj;inlo81sPCbz7T<0J01-l=+N^UHtKt3I+KE+5-vO0+0N)quqL5SZe zMd{6ucg{@tN5p9QZ zXIq?wrkw-~QRsfd1h3e&@iMK?QsSQ-3=i|{3)EC;TAm3*ovl<+ky3P=Wb_g9d&?yP zSKQeqlhJH7NWclE)O@L<6c3kF-Z4+*v7Mi2-EdHYQ}5?vS6o6=YHs?hog;Ss6f2n^ zd1#6d)(Cml=VoIZ`Ut84|9xs#wgrMwmCDb1hG#2m`kd`dVE33>ryH5E=n#4m_+ zdNO=09-&%#ZIo3BJHRzTD<(CXbQKeYF-b-53w(iTFQ-nEM^XZ%!wB<9WzLqZO{kOy zREtwdI7(YG7PTKHc{8<(;IzNGokMWas5C7TMmkN4e2k2Wcs5dc8pe4vL?oMC0yd; z5-|QaNClbNL($V|#u%HgOGE?ZrZLYGMZXc%s27(gYuz-O!73zC@5X*eKmtbNg}k|l z*B6qE!t58ni|fp#^SnW{9O%y>+K9m>$WZSk3L-P`Vl)mRtL01r$CV(joYcPHEAt4L zt1Y<_#G%`OtxEHB$t>+^1bRe@$D%~HmVxVhW^1L^WoVmbr*8*Kiix#WY_l07#PHi< zgIFM+EZMT`;p@YbT%K)jZ?Q9pM4JIt0V#I!m}ZBNy`R+I#=^%e3V*%H(q8$Z2TqGr zDoj{`Ju;jmn@YK3Tgax`?jRMrvpbz&fq=dJtmAfVzBU|fJwDvZI*eQI zW_;jIf3}l#w)W5Cq*Crp!K3S7C20KRr0~vk zLid<;ws(3jBEx^Xx3dLV`-YPakT}ozidYejTnwow7J+-5R*2&>_WG0vD`&KAIxAHH z7vSpl$HM{6Ho=AQr^B_8D+4#rI>ZISQkuH5_rVH!f5xr&udjW`x;M)CtUM`S-QOc_ zOEx+veEE-5-b!6o-$~u%wq%3WMZW?6?fPG;yp<0`$J4R?2e04A{=Rl?V)DPLyp_M{ z!8dCe*B{IF^E(Gk{-RdU%EenmkP5oC$sd*78ND8ZE@|?A+%7*@dHfDNP>ZQD8w`s-=4H?cvtZ0GyxKKaGU_w)z5 zzRBDDzpwJAL@Yb03(;G7#go4u$HejouC@P$4L{1FH z{Z-4Kt>Ja|f|gc%jtyV_*LUO>D_^h%SAn?wzK-Mlev5(%tp~F7A`Xe2cWmmtEGP-@&|7`zX zIE>ycVe;8eBA0zzPyQbqMz?74_8!(FDt}Q=XN6tYzTXaE8=JK6l3xFk%G>*RYbYe?OtuKl+Fq xPw(rYerokM{dXJoS>5GG`P;oB<-2(AhX1ka+CF;cN=p9sQ*z`=BoKYg{sUcPuHgUx diff --git a/sensor_network.cpp b/sensor_network.cpp index 038a7ca..ddf33a4 100644 --- a/sensor_network.cpp +++ b/sensor_network.cpp @@ -3,136 +3,51 @@ #include #include -/** - * Startet alle Threads des Netzwerks - * @param num_sensors Anzahl der Sensor-Threads - * @param num_analysers Anzahl der Analyse-Threads - */ template -void SensorNetwork::start(size_t num_sensors, size_t num_analysers) { +void SensorNetwork::start(size_t sensors, size_t analysers) { running = true; - - // Starte Sensor-Threads - for(size_t i = 0; i < num_sensors; ++i) { - sensors.emplace_back([this, i] { - sensor_thread(i); + + // Sensor threads + for (size_t i = 0; i < sensors; ++i) { + threads.emplace_back([this] { + std::mt19937 gen(std::random_device{}()); + std::uniform_int_distribution<> dist(0, 100); + while (running) { + std::this_thread::sleep_for(std::chrono::milliseconds(100 + gen() % 400)); + buffer.push(dist(gen)); + } }); } - // Starte Analyse-Threads - for(size_t i = 0; i < num_analysers; ++i) { - analysers.emplace_back([this, i] { - analyser_thread(i); + // Analyser threads + for (size_t i = 0; i < analysers; ++i) { + threads.emplace_back([this] { + while (running) { + int data = buffer.pop(); + int model_val = model.read(); + std::cout << "Data: " << data << " Model: " << model_val << "\n"; + } }); } - // Starte Controller-Thread - controller = std::thread([this] { - controller_thread(); + // Controller thread + threads.emplace_back([this] { + std::mt19937 gen(std::random_device{}()); + while (running) { + std::this_thread::sleep_for(std::chrono::milliseconds(500 + gen() % 1500)); + model.write(gen() % 100); + } }); } -/** - * Stoppt alle Threads und wartet auf Beendigung - */ template void SensorNetwork::stop() { running = false; - - // Warte auf Thread-Ende - for(auto& t : sensors) { + for (auto& t : threads) { 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 -void SensorNetwork::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 -void SensorNetwork::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 -void SensorNetwork::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>; \ No newline at end of file +template class SensorNetwork<32>; diff --git a/sensor_network.h b/sensor_network.h index 1480dc3..148f2fa 100644 --- a/sensor_network.h +++ b/sensor_network.h @@ -5,37 +5,15 @@ #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 class SensorNetwork { - RingBuffer buffer; // Gemeinsamer Datenpuffer - AnalysisModel model; // Geteiltes Analysemodell - std::atomic running{false}; // Steuerflag für Threads - - // Thread-Container - std::vector sensors; - std::vector analysers; - std::thread controller; + RingBuffer buffer; + AnalysisModel model; + std::atomic running = false; + std::vector threads; public: - ~SensorNetwork() { - if (running) stop(); - } - - void start(size_t num_sensors, size_t num_analysers); + ~SensorNetwork() { if (running) stop(); } + void start(size_t sensors, size_t analysers); void stop(); - -private: - // Thread-Funktionen - void sensor_thread(int id); - void analyser_thread(int id); - void controller_thread(); -}; \ No newline at end of file +};