using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
 
public class SensorReading
{
    public int SensorId { get; }
    public double Value { get; }
    public DateTime Timestamp { get; }
 
    public SensorReading(int sensorId, double value)
    {
        SensorId = sensorId;
        Value = value;
        Timestamp = DateTime.Now;
    }
}
 
public class SensorDataCollector
{
    // Thread-safe collection for storing sensor readings
    private readonly ConcurrentBag<SensorReading> _readings = new ConcurrentBag<SensorReading>();
    
    // Object for additional synchronization if needed
    private readonly object _lockObject = new object();
 
    public void AddReading(SensorReading reading)
    {
        // Add reading to the thread-safe collection
        _readings.Add(reading);
    }
 
    public double GetAverageValue(int sensorId)
    {
        // Use LINQ for thread-safe filtering and averaging
        var sensorReadings = _readings.Where(r => r.SensorId == sensorId);
        return sensorReadings.Any() ? sensorReadings.Average(r => r.Value) : 0;
    }
 
    public int GetTotalReadingCount()
    {
        // Return total number of readings
        return _readings.Count;
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        // Create a single instance of SensorDataCollector
        SensorDataCollector collector = new SensorDataCollector();
        
        // Random number generator for values and delays
        Random random = new Random();
 
        // Create an array to hold our tasks
        Task[] sensorTasks = new Task[5];
 
        // Create tasks for each sensor
        for (int i = 0; i < 5; i++)
        {
            int sensorId = i + 1; // Sensor IDs from 1 to 5
            sensorTasks[i] = Task.Run(() =>
            {
                for (int j = 0; j < 50; j++)
                {
                    // Generate a random sensor reading
                    double value = random.NextDouble() * 100; // Random value between 0 and 100
                    
                    // Create sensor reading
                    SensorReading reading = new SensorReading(sensorId, value);
                    
                    // Add reading to collector
                    collector.AddReading(reading);
                    
                    // Random delay between 100-300 ms
                    Thread.Sleep(random.Next(100, 301));
                }
            });
        }
 
        // Wait for all sensor tasks to complete
        Task.WaitAll(sensorTasks);
 
        // Display results
        Console.WriteLine($"Total number of readings: {collector.GetTotalReadingCount()}");
 
        // Display average value for each sensor
        for (int i = 1; i <= 5; i++)
        {
            double avgValue = collector.GetAverageValue(i);
            Console.WriteLine($"Average value for Sensor {i}: {avgValue:F2}");
        }
    }
}

A. Vláknově bezpečný systém pro sběr a analýzu dat ze senzorů
Navrhněte systém pro sběr dat z více senzorů, které běžíparalelně a v reálném čase posílají měření do centrálního sběrače dat. Cílem je zajistit vláknově bezpečný zápis a čtenídat, a zároveň umožnit jednoduchou analýzu dat.
Třída SensorReading
Reprezentuje jedno měření ze senzoru. Obsahuje:
• int Sensor Id
double Value
• DateTime Timestamp
Pro získání aktuálního času můžete použít příkaz: DateTime
localDate = DateTime.Now;
Třída SensorDataCollector
Tato třída bude sdílená mezi vlákny a musí být vláknově bezpečná.
Vnitřní struktura:
• Uchovávejte všechna měření v kolekci např.
List nebo v jiné vhodné struktuře.
Požadované metody:
• void AddReading (Sensor Reading reading)
• Přidá nové měření do kolekce.
• Tato metoda bude volána z více vláken současně.
double GetAverageValue(int sensorId)
• Vrátí průměrnou hodnotu všech měření od daného senzoru.
• int GetTotalReadingCount()
• Vrátí celkový počet uložených měření.
Všechny metody musí být správně synchronizované, aby při paralelním čtení a zápisu nedošlo k výjimkám nebo aby nebyla ukládána chybná data.

Všechny metody musí být správně synchronizované, aby při paralelním čtení a zápisu nedošlo k výjimkám nebo aby nebyla ukládána chybná data.
Metoda Main
• Vytvořte instanci třídy Sensor DataCollector.
Spusťte 5 paralelních Tasků nebo Threadů, z nichž každý simuluje 1 senzor:
• Každý senzor každých 100-300 ms generuje nové měření (s náhodnou hodnotou) a přidá jej do collectoru.
• způsob volby ID senzoru je na Vás.
• Každý senzor nechte vygenerovat 50 hodnot.
Po dokončení zobrazte:
Celkový počet měření
Průměrnou hodnotu z každého senzoru

B. Vláknově bezpečný Chat server
Navrhněte jednoduchý chat server, do kterého mohou uživatelé posílat zprávy paralelně, a ostatní uživatelé si mohou zobrazit historii zpráv. Hlavním cílem je vytvořit vláknově bezpečnou správu zpráv, aby se žádná zpráva neztratila a nevznikly duplicity.
Třída Message
Reprezentuje jednu zprávu odeslanou uživatelem.
string Sender
string Text
DateTime Timestamp
Pro získání aktuálního času můžete použít příkaz: DateTime
localDate = DateTime.Now;
Třída ChatRoom
Třída, která uchovává zprávy a musí být vláknově bezpečná.

Třída Message
Reprezentuje jednu zprávu odeslanou uživatelem.
string Sender
string Text
DateTime Timestamp
Pro získání aktuálního času můžete použít příkaz: DateTime
localDate = DateTime.Now;
Třída ChatRoom
Třída, která uchovává zprávy a musí být vláknově bezpečná.
Vnitřní struktura:
Zprávy uchovávejte ve vhodné kolekci (List, Queue…).
• Použijte vlastní synchronizaci lock, Monitor, apod.
Povinné metody:
• void AddMessage(Message msg)
• Přidá zprávu do chatu.
• Bude volána z více vláken současně.
• List GetMessages From (string sender)
• Vrátí všechny zprávy od určitého uživatele.
• int GetMessageCount()
• Vrátí počet přijatých zpráv.
Všechny metody musí být správně synchronizované, aby při paralelním čtení a zápisu nedošlo k výjimkám nebo aby nebyla ukládána chybná data.
Metoda Main
Vytvořte sdílenou instanci Chat Room.
Simulujte 5 uživatelů pomocí Task nebo Thread, každý uživatel bude:
• Ve smyčce posílat zprávy do místnosti (10 zpráv s pauzou 100-300 ms).
• Jméno uživatele (sender) bude jedinečné pro každý Task.

Po ukončení simulace vypište:
• Celkový počet zpráv.
• Počet zpráv od každého uživatele.
Všechny zprávy od jednoho z uživatelů.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
 
// Třída reprezentující zprávu v chatu
public class Message
{
    public string Sender { get; }
    public string Text { get; }
    public DateTime Timestamp { get; }
 
    public Message(string sender, string text)
    {
        Sender = sender;
        Text = text;
        Timestamp = DateTime.Now;
    }
}
 
// Vláknově bezpečná třída ChatRoom
public class ChatRoom
{
    // Použití thread-safe kolekce
    private readonly List<Message> _messages;
    private readonly object _lockObject;
 
    public ChatRoom()
    {
        _messages = new List<Message>();
        _lockObject = new object();
    }
 
    // Synchronizovaná metoda pro přidání zprávy
    public void AddMessage(Message msg)
    {
        lock (_lockObject)
        {
            _messages.Add(msg);
        }
    }
 
    // Synchronizovaná metoda pro získání zpráv od konkrétního odesílatele
    public List<Message> GetMessagesFrom(string sender)
    {
        lock (_lockObject)
        {
            return _messages.Where(m => m.Sender == sender).ToList();
        }
    }
 
    // Synchronizovaná metoda pro získání počtu zpráv
    public int GetMessageCount()
    {
        lock (_lockObject)
        {
            return _messages.Count;
        }
    }
}
 
class Program
{
    static void Main()
    {
        // Vytvoření sdílené instance ChatRoom
        ChatRoom chatRoom = new ChatRoom();
 
        // Definice pole jmen uživatelů
        string[] userNames = { "Alice", "Bob", "Charlie", "David", "Eve" };
 
        // Vytvoření úloh pro simulaci uživatelů
        var tasks = userNames.Select(userName => Task.Run(() =>
        {
            Random random = new Random();
            for (int i = 0; i < 10; i++)
            {
                // Generování náhodných zpráv s pauzou
                string message = $"Zpráva {i + 1} od {userName}";
                chatRoom.AddMessage(new Message(userName, message));
 
                // Náhodná pauza mezi 100-300 ms
                Thread.Sleep(random.Next(100, 301));
            }
        })).ToArray();
 
        // Čekání na dokončení všech úloh
        Task.WaitAll(tasks);
 
        // Výpis celkového počtu zpráv
        Console.WriteLine($"Celkový počet zpráv: {chatRoom.GetMessageCount()}");
 
        // Výpis počtu zpráv pro každého uživatele
        foreach (string userName in userNames)
        {
            int userMessageCount = chatRoom.GetMessagesFrom(userName).Count;
            Console.WriteLine($"Počet zpráv od {userName}: {userMessageCount}");
        }
 
        // Výpis všech zpráv od jednoho uživatele (např. Alice)
        Console.WriteLine("\nZprávy od Alice:");
        var aliceMessages = chatRoom.GetMessagesFrom("Alice");
        foreach (var msg in aliceMessages)
        {
            Console.WriteLine($"{msg.Timestamp}: {msg.Text}");
        }
    }
}