M-Yankov / TelemetryExporter

Export frames overlays from Garmin activity
MIT License
3 stars 0 forks source link

Architecture suggestion #33

Open M-Yankov opened 1 month ago

M-Yankov commented 1 month ago

The application have 3 main loops:

  1. Map data from FitMessages to Customdata regarding framerate (saved in collection over 10k objects). usually is very fast
  2. Calculate widget data into images (frames)
  3. Save frames into physical device (this step waits completion of 100 object results from the previous steps, blocks the thread, process and returns back to previous step)

The example below suggest a solution for non blocking thread (safe-thread) showing last two steps separated in classes using concurrent Queue. (Code samples are generated from Open AI ⚠️ )

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class DataFiller
{
    private readonly ConcurrentQueue<int> _dataQueue;
    private readonly SemaphoreSlim _dataAvailable;

    public DataFiller(ConcurrentQueue<int> dataQueue, SemaphoreSlim dataAvailable)
    {
        _dataQueue = dataQueue;
        _dataAvailable = dataAvailable;
    }

    public async Task FillDataAsync(int count, CancellationToken cancellationToken)
    {
        Random random = new Random();
        for (int i = 0; i < count; i++)
        {
            await Task.Delay(100, cancellationToken); // Simulate some delay
            int value = random.Next(1, 100);
            _dataQueue.Enqueue(value);
            _dataAvailable.Release(); // Signal that new data is available
        }
    }
}

Processor:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class DataProcessor
{
    private readonly ConcurrentQueue<int> _dataQueue;
    private readonly SemaphoreSlim _dataAvailable;

    public DataProcessor(ConcurrentQueue<int> dataQueue, SemaphoreSlim dataAvailable)
    {
        _dataQueue = dataQueue;
        _dataAvailable = dataAvailable;
    }

    public async Task ProcessDataAsync(CancellationToken cancellationToken)
    {
        int sum = 0;
        while (!cancellationToken.IsCancellationRequested)
        {
            await _dataAvailable.WaitAsync(cancellationToken); // Wait for data to be available
            if (_dataQueue.TryDequeue(out int value))
            {
                sum += value; // Process the data
                Console.WriteLine($"Processed value: {value}, Current Sum: {sum}");
            }
        }
    }
}

program.cs

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class DataProcessor
{
    private readonly ConcurrentQueue<int> _dataQueue;
    private readonly SemaphoreSlim _dataAvailable;

    public DataProcessor(ConcurrentQueue<int> dataQueue, SemaphoreSlim dataAvailable)
    {
        _dataQueue = dataQueue;
        _dataAvailable = dataAvailable;
    }

    public async Task ProcessDataAsync(CancellationToken cancellationToken)
    {
        int sum = 0;
        while (!cancellationToken.IsCancellationRequested)
        {
            await _dataAvailable.WaitAsync(cancellationToken); // Wait for data to be available
            if (_dataQueue.TryDequeue(out int value))
            {
                sum += value; // Process the data
                Console.WriteLine($"Processed value: {value}, Current Sum: {sum}");
            }
        }
    }
}
M-Yankov commented 1 month ago

Second approach (modern) using async foreach ... :

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class DataFiller
{
    public async IAsyncEnumerable<int> FillDataAsync(int count)
    {
        Random random = new Random();
        for (int i = 0; i < count; i++)
        {
            await Task.Delay(100); // Simulate some delay
            yield return random.Next(1, 100); // Yield data asynchronously
        }
    }
}

public class DataProcessor
{
    public async Task ProcessDataAsync(IAsyncEnumerable<int> dataStream)
    {
        int sum = 0;
        await foreach (var value in dataStream)
        {
            sum += value; // Process the data
            Console.WriteLine($"Processed value: {value}, Current Sum: {sum}");
        }
    }
}

public class Program
{
    public static async Task Main(string[] args)
    {
        DataFiller filler = new DataFiller();
        DataProcessor processor = new DataProcessor();

        IAsyncEnumerable<int> dataStream = filler.FillDataAsync(10); // Fill 10 items asynchronously
        await processor.ProcessDataAsync(dataStream); // Process the data asynchronously
    }
}
M-Yankov commented 1 week ago

Adding top priority it should be done ASAP, before project grows up