Crowd Analysis Dashboard C#
👤 Sharing: AI
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace CrowdAnalysisDashboard
{
// Represents a single data point in the crowd analysis (e.g., person count, age, sentiment)
public class CrowdDataPoint
{
public DateTime Timestamp { get; set; }
public int PersonCount { get; set; }
public double AverageAge { get; set; }
public double AverageSentimentScore { get; set; // Scale -1 to 1 (negative to positive)
}
// Simulates a data source (e.g., camera feed, social media stream)
public class DataSourceSimulator
{
private readonly Random _random = new Random();
// Generates a random CrowdDataPoint
public CrowdDataPoint GenerateData()
{
return new CrowdDataPoint
{
Timestamp = DateTime.Now,
PersonCount = _random.Next(10, 100), // Random number of people between 10 and 100
AverageAge = _random.NextDouble() * 60 + 20, // Average age between 20 and 80
AverageSentimentScore = _random.NextDouble() * 2 - 1 // Sentiment between -1 and 1
};
}
}
// Represents the Crowd Analysis Dashboard
public class CrowdAnalysisDashboard
{
private readonly List<CrowdDataPoint> _dataPoints = new List<CrowdDataPoint>();
private readonly DataSourceSimulator _dataSource = new DataSourceSimulator();
// A read lock is acquired when we want to read from the shared resource (in our case, the `_dataPoints` list) and a write lock is acquired when we want to modify the shared resource.
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
// Adds a new data point to the dashboard
public void AddDataPoint(CrowdDataPoint dataPoint)
{
try
{
_lock.EnterWriteLock(); // Acquire write lock
_dataPoints.Add(dataPoint);
}
finally
{
_lock.ExitWriteLock(); // Release write lock
}
}
// Gets the latest data points
public List<CrowdDataPoint> GetLatestData(int count = 10)
{
try
{
_lock.EnterReadLock(); // Acquire read lock
return _dataPoints.OrderByDescending(d => d.Timestamp).Take(count).ToList(); // Get latest 'count' items
}
finally
{
_lock.ExitReadLock(); // Release read lock
}
}
// Calculates the average person count over a period
public double GetAveragePersonCount(TimeSpan period)
{
try
{
_lock.EnterReadLock(); // Acquire read lock
DateTime cutoff = DateTime.Now - period;
var relevantData = _dataPoints.Where(d => d.Timestamp >= cutoff).ToList();
if (relevantData.Count == 0)
{
return 0;
}
return relevantData.Average(d => d.PersonCount);
}
finally
{
_lock.ExitReadLock(); // Release read lock
}
}
// Calculates the average sentiment score over a period
public double GetAverageSentimentScore(TimeSpan period)
{
try
{
_lock.EnterReadLock(); // Acquire read lock
DateTime cutoff = DateTime.Now - period;
var relevantData = _dataPoints.Where(d => d.Timestamp >= cutoff).ToList();
if (relevantData.Count == 0)
{
return 0;
}
return relevantData.Average(d => d.AverageSentimentScore);
}
finally
{
_lock.ExitReadLock(); // Release read lock
}
}
// Simulates data ingestion from the data source
public void SimulateDataIngestion()
{
while (true)
{
CrowdDataPoint newData = _dataSource.GenerateData();
AddDataPoint(newData);
Console.WriteLine($"Data point added: Timestamp = {newData.Timestamp}, PersonCount = {newData.PersonCount}, Sentiment = {newData.AverageSentimentScore:F2}");
Thread.Sleep(2000); // Simulate data coming in every 2 seconds
}
}
}
// Main program
public class Program
{
public static void Main(string[] args)
{
CrowdAnalysisDashboard dashboard = new CrowdAnalysisDashboard();
// Start a separate thread to simulate data ingestion
Thread ingestionThread = new Thread(dashboard.SimulateDataIngestion);
ingestionThread.Start();
// Main thread: Display dashboard information periodically
while (true)
{
Console.WriteLine("\n--- Crowd Analysis Dashboard ---");
Console.WriteLine($"Average Person Count (Last 10 seconds): {dashboard.GetAveragePersonCount(TimeSpan.FromSeconds(10)):F2}");
Console.WriteLine($"Average Sentiment (Last 10 seconds): {dashboard.GetAverageSentimentScore(TimeSpan.FromSeconds(10)):F2}");
List<CrowdDataPoint> latestData = dashboard.GetLatestData(5);
Console.WriteLine("\nLatest Data Points:");
foreach (CrowdDataPoint data in latestData)
{
Console.WriteLine($" Timestamp: {data.Timestamp}, PersonCount: {data.PersonCount}, Age: {data.AverageAge:F1}, Sentiment: {data.AverageSentimentScore:F2}");
}
Thread.Sleep(5000); // Refresh every 5 seconds
}
}
}
}
```
Key improvements and explanations:
* **Clearer Class Structure:** The code is organized into classes that represent distinct parts of the system: `CrowdDataPoint`, `DataSourceSimulator`, and `CrowdAnalysisDashboard`. This makes the code more modular and easier to understand.
* **Data Source Simulation:** The `DataSourceSimulator` class simulates a real-world data source. This allows you to run the program without needing a connection to a camera or other sensor. It generates random, but plausible, data.
* **Dashboard Functionality:** The `CrowdAnalysisDashboard` class provides the core functionality for analyzing the data:
* `AddDataPoint()`: Adds new data points.
* `GetLatestData()`: Retrieves the most recent data.
* `GetAveragePersonCount()`: Calculates the average person count over a specified time period.
* `GetAverageSentimentScore()`: Calculates the average sentiment score over a specified time period.
* `SimulateDataIngestion()`: This method continuously generates data and adds it to the dashboard, simulating a live data stream. It runs in a separate thread.
* **Threading:** The use of `Thread` allows the data ingestion to happen in the background without blocking the main thread, which is responsible for displaying the dashboard. This prevents the UI from freezing.
* **Concurrency Safety (ReaderWriterLockSlim):** The `ReaderWriterLockSlim` class is crucial for handling concurrent access to the `_dataPoints` list. Multiple threads (the data ingestion thread and the main thread for displaying data) may try to read or write to this list simultaneously. `ReaderWriterLockSlim` allows multiple readers *or* a single writer at a time, preventing data corruption and race conditions. This is a significant improvement over simpler locking mechanisms.
* **Error Handling (Zero Data Check):** The `GetAveragePersonCount` and `GetAverageSentimentScore` methods now handle the case where there is no data within the specified time period, preventing a `DivideByZeroException`.
* **Clearer Output:** The `Main` method now displays more comprehensive information, including the latest data points and the average sentiment score. It also uses `string.Format` for better formatting of the output.
* **Comments:** Extensive comments explain the purpose of each class, method, and important code block.
* **Realistic Data:** The ranges of the generated data (person count, age, sentiment) are more realistic.
* **`using` directives:** Reduced to the necessary ones.
**How to Run the Code:**
1. **Save:** Save the code as a `.cs` file (e.g., `CrowdAnalysisDashboard.cs`).
2. **Compile:** Open a command prompt or terminal and navigate to the directory where you saved the file. Then, compile the code using the C# compiler:
```bash
csc CrowdAnalysisDashboard.cs
```
This will create an executable file (e.g., `CrowdAnalysisDashboard.exe`).
3. **Run:** Execute the compiled program:
```bash
CrowdAnalysisDashboard.exe
```
The dashboard will start displaying the simulated data in the console. The data ingestion thread will continuously add new data points in the background, and the main thread will periodically refresh the displayed information. You can terminate the program by pressing Ctrl+C.
This improved version provides a more complete, robust, and realistic simulation of a crowd analysis dashboard. It also demonstrates best practices for concurrency and error handling in C#. This is a foundation on which you can build more complex features, such as data visualization, alerts, and integration with real-world data sources.
👁️ Viewed: 3
Comments