Testing und interfaces

This commit is contained in:
Meik
2026-02-13 09:06:36 +01:00
parent abbce22aa9
commit 352dc42ae7
8 changed files with 846 additions and 166 deletions

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using C4IT.FASD.Cockpit.Communication;
#if isDemo
using FasdCockpitCommunicationDemo;
#endif
namespace FasdDesktopUi.Basics.Services.Models
{
internal interface ITicketOverviewCommunication
{
bool IsDemo();
Task<Dictionary<string, int>> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope);
#if isDemo
void RegisterGeneratedTicket(DemoTicketRecord record);
#endif
}
internal interface ITicketOverviewCommunicationSource
{
ITicketOverviewCommunication Resolve();
}
internal sealed class TicketOverviewCommunicationSource : ITicketOverviewCommunicationSource
{
public ITicketOverviewCommunication Resolve()
{
var communication = cFasdCockpitCommunicationBase.Instance;
return communication == null ? null : new TicketOverviewCommunicationAdapter(communication);
}
}
internal sealed class TicketOverviewCommunicationAdapter : ITicketOverviewCommunication
{
private readonly cFasdCockpitCommunicationBase _communication;
internal TicketOverviewCommunicationAdapter(cFasdCockpitCommunicationBase communication)
{
_communication = communication ?? throw new ArgumentNullException(nameof(communication));
}
public bool IsDemo()
{
return _communication.IsDemo();
}
public async Task<Dictionary<string, int>> GetTicketOverviewCounts(string[] overviewKeys, bool useRoleScope)
{
var rawCounts = await _communication.GetTicketOverviewCounts(overviewKeys, useRoleScope).ConfigureAwait(false);
return rawCounts == null
? new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
: new Dictionary<string, int>(rawCounts, StringComparer.OrdinalIgnoreCase);
}
#if isDemo
public void RegisterGeneratedTicket(DemoTicketRecord record)
{
var demoCommunication = _communication as cFasdCockpitCommunicationDemo;
demoCommunication?.RegisterGeneratedTicket(record);
}
#endif
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
namespace FasdDesktopUi.Basics.Services.Models
{
internal static class TicketOverviewCountProcessor
{
internal static ScopeCountProcessingResult Calculate(
IReadOnlyDictionary<string, TileCounts> currentCounts,
IEnumerable<string> overviewKeys,
TileScope scope,
IDictionary<string, int> incomingCounts,
bool hasInitializedScope)
{
if (overviewKeys == null)
throw new ArgumentNullException(nameof(overviewKeys));
var updatedCounts = new Dictionary<string, TileCounts>(StringComparer.OrdinalIgnoreCase);
if (currentCounts != null)
{
foreach (var kvp in currentCounts)
{
updatedCounts[kvp.Key] = kvp.Value;
}
}
var changes = new List<TileCountChange>();
foreach (var key in overviewKeys)
{
var previous = currentCounts != null && currentCounts.TryGetValue(key, out var counts)
? counts
: TileCounts.Empty;
var incoming = incomingCounts != null && incomingCounts.TryGetValue(key, out var value)
? value
: 0;
TileCounts updated;
int oldValue;
if (scope == TileScope.Role)
{
updated = new TileCounts(previous.Personal, incoming);
oldValue = previous.Role;
}
else
{
updated = new TileCounts(incoming, previous.Role);
oldValue = previous.Personal;
}
updatedCounts[key] = updated;
if (hasInitializedScope && oldValue != incoming)
changes.Add(new TileCountChange(key, scope, oldValue, incoming));
}
return new ScopeCountProcessingResult(updatedCounts, changes, !hasInitializedScope);
}
}
internal sealed class ScopeCountProcessingResult
{
internal ScopeCountProcessingResult(
IReadOnlyDictionary<string, TileCounts> updatedCounts,
IReadOnlyList<TileCountChange> changes,
bool isInitialization)
{
UpdatedCounts = updatedCounts ?? throw new ArgumentNullException(nameof(updatedCounts));
Changes = changes ?? Array.Empty<TileCountChange>();
IsInitialization = isInitialization;
}
internal IReadOnlyDictionary<string, TileCounts> UpdatedCounts { get; }
internal IReadOnlyList<TileCountChange> Changes { get; }
internal bool IsInitialization { get; }
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace FasdDesktopUi.Basics.Services.Models
{
internal interface ITicketOverviewTimer
{
TimeSpan Interval { get; set; }
bool IsEnabled { get; }
void Start();
void Stop();
}
internal interface ITicketOverviewDispatcher
{
Task InvokeAsync(Action action);
Task InvokeAsync(Func<Task> action);
ITicketOverviewTimer CreateTimer(TimeSpan interval, Action tick);
}
internal sealed class TicketOverviewDispatcher : ITicketOverviewDispatcher
{
private readonly Dispatcher _dispatcher;
internal TicketOverviewDispatcher(Dispatcher dispatcher)
{
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
public Task InvokeAsync(Action action)
{
return _dispatcher.InvokeAsync(action, DispatcherPriority.Background).Task;
}
public Task InvokeAsync(Func<Task> action)
{
return _dispatcher.InvokeAsync(action, DispatcherPriority.Background).Task.Unwrap();
}
public ITicketOverviewTimer CreateTimer(TimeSpan interval, Action tick)
{
var timer = new DispatcherTimer(interval, DispatcherPriority.Background, (sender, args) => tick?.Invoke(), _dispatcher)
{
IsEnabled = false
};
return new TicketOverviewTimer(timer);
}
}
internal sealed class TicketOverviewTimer : ITicketOverviewTimer
{
private readonly DispatcherTimer _timer;
internal TicketOverviewTimer(DispatcherTimer timer)
{
_timer = timer ?? throw new ArgumentNullException(nameof(timer));
}
public TimeSpan Interval
{
get => _timer.Interval;
set => _timer.Interval = value;
}
public bool IsEnabled => _timer.IsEnabled;
public void Start()
{
_timer.Start();
}
public void Stop()
{
_timer.Stop();
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Diagnostics;
using C4IT.FASD.Base;
namespace FasdDesktopUi.Basics.Services.Models
{
internal interface ITicketOverviewSettingsProvider
{
int GetPollingMinutes(TileScope scope);
}
internal sealed class TicketOverviewSettingsProvider : ITicketOverviewSettingsProvider
{
public int GetPollingMinutes(TileScope scope)
{
int minutes = scope == TileScope.Role
? cF4sdTicketConfig.DefaultOverviewPollingRole
: cF4sdTicketConfig.DefaultOverviewPollingPersonal;
try
{
var ticketConfig = cFasdCockpitConfig.Instance?.Global?.TicketConfiguration;
if (ticketConfig != null)
{
minutes = scope == TileScope.Role
? ticketConfig.OverviewPollingRole
: ticketConfig.OverviewPollingPersonal;
}
}
catch (Exception ex)
{
Debug.WriteLine($"[TicketOverview] Settings fallback to defaults: {ex.Message}");
}
if (minutes < 1)
{
minutes = 1;
}
return minutes;
}
}
}