using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Maui.Graphics;
namespace StuckPixelRestore.Services
{
/// <summary>
/// 컬러 노이즈를 생성하여 이벤트를 통해 색상을 전달하는 서비스
/// 주어진 FPS로 랜덤 색상을 생성하며, 지정된 시간 후 자동으로 중단될 수 있음.
/// </summary>
public class NoiseService : INoiseService
{
private CancellationTokenSource cancellationTokenSource;
private Action? onStoppedCallback;
private Random random;
private double targetFps = 60.0;
private TimeSpan? autoStopDuration;
private DateTime startTime;
private bool isRunning;
/// <summary>
/// 노이즈 생성 실행 상태 변경 이벤트.
/// </summary>
public event EventHandler<bool> IsRunningChanged;
/// <summary>
/// 랜덤 색상 생성 이벤트.
/// </summary>
public event EventHandler<Color> ColorGenerated;
/// <summary>
/// 현재 노이즈 생성이 실행 중인지 여부를 반환.
/// </summary>
public bool IsRunning => isRunning;
/// <summary>
/// NoiseService 생성자.
/// </summary>
public NoiseService()
{
random = new Random();
}
/// <summary>
/// 노이즈 생성을 시작.
/// </summary>
/// <param name="onStoppedCallback">노이즈 생성이 종료될 때 호출할 선택적 콜백 함수.</param>
/// <exception cref="InvalidOperationException">이미 실행 중인 경우 발생.</exception>
public void StartNoise(Action? onStoppedCallback = null)
{
if (isRunning)
{
throw new InvalidOperationException("[NoiseService] 이미 노이즈가 실행 중입니다.");
}
StopNoise(); // 안전하게 이전 세션 정리
isRunning = true;
this.onStoppedCallback = onStoppedCallback;
cancellationTokenSource = new CancellationTokenSource();
startTime = DateTime.UtcNow;
IsRunningChanged?.Invoke(this, true);
Task.Run(async () =>
{
double frameDelayMs = 1000.0 / targetFps;
while (!cancellationTokenSource.Token.IsCancellationRequested)
{
try
{
// 랜덤 컬러 생성
Color randomColor = Color.FromRgb(
random.NextDouble(),
random.NextDouble(),
random.NextDouble()
);
ColorGenerated?.Invoke(this, randomColor);
// 자동 종료 시간 체크
if (autoStopDuration.HasValue)
{
TimeSpan elapsed = DateTime.UtcNow - startTime;
if (elapsed >= autoStopDuration.Value)
{
StopNoise();
break;
}
}
await Task.Delay(TimeSpan.FromMilliseconds(frameDelayMs), cancellationTokenSource.Token);
}
catch (TaskCanceledException)
{
// 정상 종료 상황 - 무시
break;
}
catch (Exception ex)
{
Console.WriteLine($"[NoiseService] 오류 발생: {ex.Message}");
StopNoise();
break;
}
}
});
}
/// <summary>
/// 노이즈 생성을 강제로 중단.
/// </summary>
public void StopNoise()
{
if (!isRunning)
{
return;
}
cancellationTokenSource?.Cancel();
cancellationTokenSource?.Dispose();
cancellationTokenSource = null;
isRunning = false;
IsRunningChanged?.Invoke(this, false);
onStoppedCallback?.Invoke();
onStoppedCallback = null;
}
/// <summary>
/// 목표 FPS(Frame Per Second)를 설정.
/// </summary>
/// <param name="fps">초당 프레임 수 (60.0 등).</param>
/// <exception cref="ArgumentOutOfRangeException">FPS가 0 이하인 경우 발생.</exception>
public void SetTargetFps(double fps)
{
if (fps <= 0)
{
throw new ArgumentOutOfRangeException(nameof(fps), "[NoiseService] FPS는 0보다 커야 합니다.");
}
targetFps = fps;
}
/// <summary>
/// 자동으로 노이즈 생성이 중단될 시간을 설정.
/// </summary>
/// <param name="duration">자동 정지까지의 시간. null이면 무제한.</param>
/// <exception cref="ArgumentOutOfRangeException">duration이 0 이하일 경우 발생.</exception>
public void SetAutoStopDuration(TimeSpan? duration)
{
if (duration.HasValue && duration.Value <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(duration), "[NoiseService] duration은 0보다 커야 합니다.");
}
autoStopDuration = duration;
}
}
}
댓글 0