aktueller Stand
This commit is contained in:
@@ -5,15 +5,18 @@ namespace FasdDesktopUi.Basics.Services.Models
|
||||
{
|
||||
public sealed class TicketOverviewCountsChangedEventArgs : EventArgs
|
||||
{
|
||||
public TicketOverviewCountsChangedEventArgs(IReadOnlyList<TileCountChange> changes, IReadOnlyDictionary<string, TileCounts> currentCounts)
|
||||
public TicketOverviewCountsChangedEventArgs(IReadOnlyList<TileCountChange> changes, IReadOnlyDictionary<string, TileCounts> currentCounts, TileScope? initializedScope = null)
|
||||
{
|
||||
Changes = changes;
|
||||
Changes = changes ?? Array.Empty<TileCountChange>();
|
||||
CurrentCounts = currentCounts;
|
||||
InitializedScope = initializedScope;
|
||||
}
|
||||
|
||||
public IReadOnlyList<TileCountChange> Changes { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, TileCounts> CurrentCounts { get; }
|
||||
|
||||
public TileScope? InitializedScope { get; }
|
||||
}
|
||||
|
||||
public readonly struct TileCountChange
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using C4IT.F4SD.SupportCaseProtocoll.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
@@ -8,14 +9,14 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
internal class F4SDProtocoll
|
||||
{
|
||||
private readonly ICollection<IProtocollEntry> _protocollEntries = new List<IProtocollEntry>();
|
||||
private readonly ICollection<ProtocollEntryBase> _protocollEntries = new List<ProtocollEntryBase>();
|
||||
|
||||
public static F4SDProtocoll Instance { get; private set; } = new F4SDProtocoll();
|
||||
|
||||
private F4SDProtocoll() { }
|
||||
|
||||
internal void Add(IProtocollEntry entry) => _protocollEntries.Add(entry);
|
||||
internal void Add(IEnumerable<IProtocollEntry> entries)
|
||||
internal void Add(ProtocollEntryBase entry) => _protocollEntries.Add(entry);
|
||||
internal void Add(IEnumerable<ProtocollEntryBase> entries)
|
||||
{
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
@@ -25,34 +26,34 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
|
||||
internal void Clear() => _protocollEntries.Clear();
|
||||
|
||||
internal T GetLatestOfType<T>() where T : IProtocollEntry => _protocollEntries.OfType<T>().LastOrDefault();
|
||||
internal T GetLatestOfType<T>() where T : ProtocollEntryBase => _protocollEntries.OfType<T>().LastOrDefault();
|
||||
|
||||
internal IEnumerable<T> GetOfType<T>(int? count = null) where T : IProtocollEntry
|
||||
internal IEnumerable<T> GetOfType<T>(int? count = null) where T : ProtocollEntryBase
|
||||
{
|
||||
if(count.HasValue)
|
||||
return _protocollEntries.OfType<T>().Reverse().Take(count.Value).Reverse();
|
||||
return _protocollEntries.OfType<T>();
|
||||
}
|
||||
|
||||
internal IEnumerable<IProtocollEntry> GetAll() => _protocollEntries;
|
||||
internal IEnumerable<ProtocollEntryBase> GetAll() => _protocollEntries;
|
||||
|
||||
internal DataObject GetLatestOfTypeAsDataObject<T>(bool skipHtmlFrame) where T : IProtocollEntry
|
||||
internal DataObject GetLatestOfTypeAsDataObject<T>(bool skipHtmlFrame) where T : ProtocollEntryBase
|
||||
{
|
||||
IProtocollEntry entry = _protocollEntries.OfType<T>().LastOrDefault();
|
||||
ProtocollEntryBase entry = _protocollEntries.OfType<T>().LastOrDefault();
|
||||
|
||||
if (entry is null)
|
||||
return new DataObject();
|
||||
|
||||
return GetDataObjectOf(new IProtocollEntry[] { entry }, skipHtmlFrame);
|
||||
return GetDataObjectOf(new ProtocollEntryBase[] { entry }, skipHtmlFrame);
|
||||
}
|
||||
|
||||
internal DataObject GetOfTypeAsDataObject<T>(bool skipHtmlFrame, int? count = null) where T : IProtocollEntry
|
||||
=> GetDataObjectOf(GetOfType<T>(count).Cast<IProtocollEntry>(), skipHtmlFrame);
|
||||
internal DataObject GetOfTypeAsDataObject<T>(bool skipHtmlFrame, int? count = null) where T : ProtocollEntryBase
|
||||
=> GetDataObjectOf(GetOfType<T>(count).Cast<ProtocollEntryBase>(), skipHtmlFrame);
|
||||
|
||||
internal DataObject GetAllAsDataObject(bool skipHtmlFrame)
|
||||
=> GetDataObjectOf(_protocollEntries, skipHtmlFrame);
|
||||
|
||||
private DataObject GetDataObjectOf(IEnumerable<IProtocollEntry> entries, bool skipHtmlFrame, int? count = null)
|
||||
private DataObject GetDataObjectOf(IEnumerable<ProtocollEntryBase> entries, bool skipHtmlFrame, int? count = null)
|
||||
{
|
||||
DataObject dataObject = new DataObject();
|
||||
|
||||
@@ -67,16 +68,16 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
if (count.HasValue)
|
||||
entries = entries.Reverse().Take(count.Value).Reverse();
|
||||
|
||||
foreach (IProtocollEntry entry in entries)
|
||||
foreach (ProtocollEntryBase entry in entries)
|
||||
{
|
||||
string entryAscii = entry.GetAscii();
|
||||
string entryAscii = entry.AsciiContent;
|
||||
if (!string.IsNullOrEmpty(entryAscii))
|
||||
{
|
||||
ascii += entryAscii;
|
||||
ascii += asciiSeparator;
|
||||
}
|
||||
|
||||
string entryHtml = entry.GetHtml();
|
||||
string entryHtml = entry.HtmlContent;
|
||||
if (!string.IsNullOrEmpty(entryHtml))
|
||||
{
|
||||
html += entryHtml;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
internal interface IProtocollEntry
|
||||
{
|
||||
string GetAscii();
|
||||
string GetHtml();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using C4IT.F4SD.DisplayFormatting;
|
||||
using C4IT.F4SD.SupportCaseProtocoll.Models;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.MultiLanguage;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
@@ -11,17 +11,26 @@ using static FasdDesktopUi.Basics.UserControls.QuickActionStatusMonitor;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
internal class QuickActionProtocollEntry : IProtocollEntry
|
||||
internal static class QuickActionProtocollEntryOutput
|
||||
{
|
||||
private readonly cFasdQuickAction _quickActionDefinition;
|
||||
private readonly cQuickActionCopyData _quickActionCopyData;
|
||||
|
||||
const string AsciiSeperator = "\n\n";
|
||||
|
||||
public QuickActionProtocollEntry(cFasdQuickAction quickActionDefinition, cQuickActionCopyData quickActionCopyData)
|
||||
public static QuickActionProtocollEntry GetQuickActionProtocollEntry(cFasdQuickAction quickActionDefinition, cQuickActionCopyData quickActionCopyData)
|
||||
{
|
||||
_quickActionDefinition = quickActionDefinition;
|
||||
_quickActionCopyData = quickActionCopyData;
|
||||
string ascii = GetAscii(quickActionDefinition, quickActionCopyData);
|
||||
string html = GetHtml(quickActionDefinition, quickActionCopyData);
|
||||
|
||||
return new QuickActionProtocollEntry(ascii, html)
|
||||
{
|
||||
Id = quickActionDefinition.Id,
|
||||
Name = quickActionDefinition.Name,
|
||||
ExecutionTypeId = (int)quickActionDefinition.ExecutionType,
|
||||
WasRunningOnAffectedDevice = quickActionCopyData.WasRunningOnAffectedDevice,
|
||||
AffectedDeviceName = quickActionCopyData.AffectedDeviceName,
|
||||
ResultCode = (int?)quickActionCopyData.QuickActionOutput?.ResultCode,
|
||||
ErrorMessage = quickActionCopyData.QuickActionOutput?.ErrorDescription,
|
||||
MeasureValues = GetQuickActionHtmlValueComparison(quickActionCopyData.MeasureValues)
|
||||
};
|
||||
}
|
||||
|
||||
internal static cQuickActionCopyData GetCopyData(cFasdQuickAction quickActionDefinition, cSupportCaseDataProvider dataProvider, bool wasRunningOnAffectedDevice, cQuickActionOutput quickActionOutput, List<cQuickActionMeasureValue> measureValues)
|
||||
@@ -51,38 +60,21 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return quickActionCopyData;
|
||||
}
|
||||
|
||||
internal cQuickActionResult GetResult()
|
||||
{
|
||||
return new cQuickActionResult()
|
||||
{
|
||||
QuickActionId = _quickActionDefinition.Id,
|
||||
QuickActionName = _quickActionDefinition.Name,
|
||||
QuickActionExecutionType = (int)_quickActionDefinition.ExecutionType,
|
||||
wasRunningOnAffectedDevice = _quickActionCopyData.WasRunningOnAffectedDevice,
|
||||
AffectedDeviceName = _quickActionCopyData.AffectedDeviceName,
|
||||
ExecutionTime = DateTime.UtcNow,
|
||||
ResultCode = (int?)(_quickActionCopyData.QuickActionOutput?.ResultCode),
|
||||
ErrorMessage = _quickActionCopyData.QuickActionOutput?.ErrorDescription,
|
||||
Output = GetQuickActionHtmlOutput(_quickActionCopyData.QuickActionOutput),
|
||||
MeasureValues = GetQuickActionHtmlValueComparison(_quickActionCopyData.MeasureValues)
|
||||
};
|
||||
}
|
||||
private static string GetAscii(cFasdQuickAction quickActionDefinition, cQuickActionCopyData quickActionCopyData) => GetQuickActionAscii(quickActionDefinition, quickActionCopyData);
|
||||
|
||||
public string GetAscii() => GetQuickActionAscii(_quickActionCopyData);
|
||||
private static string GetHtml(cFasdQuickAction quickActionDefinition, cQuickActionCopyData quickActionCopyData) => GetQuickActionHtml(quickActionDefinition, quickActionCopyData);
|
||||
|
||||
public string GetHtml() => GetQuickActionHtml(_quickActionCopyData);
|
||||
|
||||
private bool ShouldHideQuickActionOutput(string outputValueKey)
|
||||
private static bool ShouldHideQuickActionOutput(cFasdQuickAction quickActionDefinition, string outputValueKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_quickActionDefinition.ColumnOutputFormattings is null)
|
||||
if (quickActionDefinition.ColumnOutputFormattings is null)
|
||||
return false;
|
||||
|
||||
if (!_quickActionDefinition.ShowAllOutputContent && !_quickActionDefinition.ColumnOutputFormattings.ContainsKey(outputValueKey))
|
||||
if (!quickActionDefinition.ShowAllOutputContent && !quickActionDefinition.ColumnOutputFormattings.ContainsKey(outputValueKey))
|
||||
return true;
|
||||
|
||||
if (_quickActionDefinition.ColumnOutputFormattings.TryGetValue(outputValueKey, out var columnFormatting))
|
||||
if (quickActionDefinition.ColumnOutputFormattings.TryGetValue(outputValueKey, out var columnFormatting))
|
||||
return columnFormatting.Hidden;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -95,19 +87,19 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
|
||||
#region Ascii
|
||||
|
||||
private string GetQuickActionAscii(cQuickActionCopyData copyData)
|
||||
private static string GetQuickActionAscii(cFasdQuickAction quickActionDefinition, cQuickActionCopyData copyData)
|
||||
{
|
||||
string ascii = string.Empty;
|
||||
|
||||
ascii += GetQuickActionAsciiDescription(copyData.Name, copyData.AffectedDeviceName, copyData.WasRunningOnAffectedDevice, copyData.ExecutionTime, copyData.QuickActionOutput?.ResultCode);
|
||||
ascii += GetQuickActionAsciiError(copyData.QuickActionOutput?.ErrorDescription);
|
||||
ascii += GetQuickActionAsciiOutput(copyData.QuickActionOutput);
|
||||
ascii += GetQuickActionAsciiOutput(quickActionDefinition, copyData.QuickActionOutput);
|
||||
ascii += GetQuickActionAsciiValueComparisonString(copyData.MeasureValues);
|
||||
|
||||
return ascii;
|
||||
}
|
||||
|
||||
private string GetQuickActionAsciiDescription(string quickActionName, string deviceName, bool wasRunningOnAffectedDevice, DateTime executionTime, enumQuickActionSuccess? quickActionStatus)
|
||||
private static string GetQuickActionAsciiDescription(string quickActionName, string deviceName, bool wasRunningOnAffectedDevice, DateTime executionTime, enumQuickActionSuccess? quickActionStatus)
|
||||
{
|
||||
string asciiDescription = string.Empty;
|
||||
try
|
||||
@@ -136,7 +128,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return asciiDescription;
|
||||
}
|
||||
|
||||
private string GetQuickActionAsciiError(string errorMessage)
|
||||
private static string GetQuickActionAsciiError(string errorMessage)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(errorMessage))
|
||||
errorMessage.Insert(0, AsciiSeperator);
|
||||
@@ -144,7 +136,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
private string GetQuickActionAsciiOutput(QuickActionStatusMonitor.cQuickActionOutput quickActionOutput)
|
||||
private static string GetQuickActionAsciiOutput(cFasdQuickAction quickActionDefinition, QuickActionStatusMonitor.cQuickActionOutput quickActionOutput)
|
||||
{
|
||||
string output = string.Empty;
|
||||
|
||||
@@ -162,7 +154,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
|
||||
output += AsciiSeperator;
|
||||
output += cMultiLanguageSupport.GetItem("QuickAction.Copy.Output") + " ";
|
||||
output += singleOutput.GetDisplayValue(_quickActionDefinition.ColumnOutputFormattings);
|
||||
output += singleOutput.GetDisplayValue(quickActionDefinition.ColumnOutputFormattings);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -174,10 +166,10 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
|
||||
foreach (var value in listOutput.Values[0])
|
||||
{
|
||||
if (ShouldHideQuickActionOutput(value.Key))
|
||||
if (ShouldHideQuickActionOutput(quickActionDefinition, value.Key))
|
||||
continue;
|
||||
|
||||
if (_quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
if (quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
output += outputFormatting.Names.GetValue();
|
||||
else
|
||||
output += value.Key;
|
||||
@@ -193,10 +185,10 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
string valueKey = listOutput.Values[0][j].Key;
|
||||
|
||||
if (ShouldHideQuickActionOutput(valueKey))
|
||||
if (ShouldHideQuickActionOutput(quickActionDefinition, valueKey))
|
||||
continue;
|
||||
|
||||
string displayValue = listOutput.GetDisplayValue(i, j, _quickActionDefinition.ColumnOutputFormattings);
|
||||
string displayValue = listOutput.GetDisplayValue(i, j, quickActionDefinition.ColumnOutputFormattings);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(displayValue))
|
||||
continue;
|
||||
@@ -217,12 +209,12 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
var value = objectOutput.Values[i];
|
||||
|
||||
if (ShouldHideQuickActionOutput(value.Key))
|
||||
if (ShouldHideQuickActionOutput(quickActionDefinition, value.Key))
|
||||
continue;
|
||||
|
||||
string columnTitle = string.Empty;
|
||||
|
||||
if (_quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
if (quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
columnTitle = outputFormatting.Names.GetValue();
|
||||
else
|
||||
columnTitle = value.Key;
|
||||
@@ -230,7 +222,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
if (!string.IsNullOrEmpty(columnTitle))
|
||||
output += $"{columnTitle}: ";
|
||||
|
||||
string displayValue = objectOutput.GetDisplayValue(i, _quickActionDefinition.ColumnOutputFormattings);
|
||||
string displayValue = objectOutput.GetDisplayValue(i, quickActionDefinition.ColumnOutputFormattings);
|
||||
output += !string.IsNullOrWhiteSpace(displayValue) ? displayValue : null;
|
||||
|
||||
output += "\n";
|
||||
@@ -248,7 +240,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return output;
|
||||
}
|
||||
|
||||
private string GetQuickActionAsciiValueComparisonString(List<QuickActionStatusMonitor.cQuickActionMeasureValue> measureValues)
|
||||
private static string GetQuickActionAsciiValueComparisonString(List<QuickActionStatusMonitor.cQuickActionMeasureValue> measureValues)
|
||||
{
|
||||
string output = string.Empty;
|
||||
|
||||
@@ -260,6 +252,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
output += AsciiSeperator + cMultiLanguageSupport.GetItem("QuickAction.Copy.Measure");
|
||||
|
||||
cUtility.RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
|
||||
cUtility.RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
|
||||
|
||||
foreach (var measureValue in measureValues)
|
||||
{
|
||||
@@ -292,7 +285,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
|
||||
#region Html
|
||||
|
||||
private string GetQuickActionHtml(cQuickActionCopyData copyData)
|
||||
private static string GetQuickActionHtml(cFasdQuickAction quickActionDefinition, cQuickActionCopyData copyData)
|
||||
{
|
||||
string output = string.Empty;
|
||||
|
||||
@@ -300,7 +293,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
output += GetQuickActionHtmlDescription(copyData.Name, copyData.AffectedDeviceName, copyData.WasRunningOnAffectedDevice, copyData.ExecutionTime, copyData.QuickActionOutput?.ResultCode);
|
||||
output += GetQuickActionHtmlError(copyData.QuickActionOutput?.ErrorDescription);
|
||||
output += GetQuickActionHtmlOutput(copyData.QuickActionOutput);
|
||||
output += GetQuickActionHtmlOutput(quickActionDefinition, copyData.QuickActionOutput);
|
||||
output += GetQuickActionHtmlValueComparison(copyData.MeasureValues);
|
||||
}
|
||||
catch (Exception E)
|
||||
@@ -311,7 +304,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return output;
|
||||
}
|
||||
|
||||
private string GetQuickActionHtmlDescription(string quickActionName, string deviceName, bool wasRunningOnAffectedDevice, DateTime executionTime, enumQuickActionSuccess? quickActionStatus)
|
||||
private static string GetQuickActionHtmlDescription(string quickActionName, string deviceName, bool wasRunningOnAffectedDevice, DateTime executionTime, enumQuickActionSuccess? quickActionStatus)
|
||||
{
|
||||
string output = string.Empty;
|
||||
try
|
||||
@@ -348,7 +341,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
private string GetQuickActionHtmlOutput(QuickActionStatusMonitor.cQuickActionOutput quickActionOutput)
|
||||
private static string GetQuickActionHtmlOutput(cFasdQuickAction quickActionDefinition, QuickActionStatusMonitor.cQuickActionOutput quickActionOutput)
|
||||
{
|
||||
string output = string.Empty;
|
||||
|
||||
@@ -366,7 +359,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
if (singleOutput.Value is null)
|
||||
return output;
|
||||
|
||||
var displayValue = singleOutput.GetDisplayValue(_quickActionDefinition?.ColumnOutputFormattings);
|
||||
var displayValue = singleOutput.GetDisplayValue(quickActionDefinition?.ColumnOutputFormattings);
|
||||
output += "<p>" + cMultiLanguageSupport.GetItem("QuickAction.Copy.Output.Html") + " " + displayValue + "</p>";
|
||||
break;
|
||||
}
|
||||
@@ -379,11 +372,11 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
|
||||
foreach (var value in listOutput.Values[0])
|
||||
{
|
||||
if (ShouldHideQuickActionOutput(value.Key))
|
||||
if (ShouldHideQuickActionOutput(quickActionDefinition, value.Key))
|
||||
continue;
|
||||
|
||||
string headingValue = value.Key;
|
||||
if (_quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
if (quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
headingValue = outputFormatting.Names.GetValue();
|
||||
|
||||
output += "<th align=\"left\">";
|
||||
@@ -400,10 +393,10 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
for (int j = 0; j < listOutput.Values[i].Count; j++)
|
||||
{
|
||||
string valueKey = listOutput.Values[0][j].Key;
|
||||
if (ShouldHideQuickActionOutput(valueKey))
|
||||
if (ShouldHideQuickActionOutput(quickActionDefinition, valueKey))
|
||||
continue;
|
||||
|
||||
string displayValue = listOutput.GetDisplayValue(i, j, _quickActionDefinition.ColumnOutputFormattings);
|
||||
string displayValue = listOutput.GetDisplayValue(i, j, quickActionDefinition.ColumnOutputFormattings);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(displayValue))
|
||||
continue;
|
||||
@@ -429,11 +422,11 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
var value = objectOutput.Values[i];
|
||||
|
||||
if (ShouldHideQuickActionOutput(value.Key))
|
||||
if (ShouldHideQuickActionOutput(quickActionDefinition, value.Key))
|
||||
continue;
|
||||
|
||||
string headingValue = value.Key;
|
||||
if (_quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
if (quickActionDefinition.ColumnOutputFormattings?.TryGetValue(value.Key, out var outputFormatting) ?? false)
|
||||
headingValue = outputFormatting.Names.GetValue();
|
||||
|
||||
output += "<tr>";
|
||||
@@ -442,7 +435,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
output += "</td>";
|
||||
|
||||
output += "<td>";
|
||||
string displayValue = objectOutput.GetDisplayValue(i, _quickActionDefinition.ColumnOutputFormattings);
|
||||
string displayValue = objectOutput.GetDisplayValue(i, quickActionDefinition.ColumnOutputFormattings);
|
||||
output += !string.IsNullOrWhiteSpace(displayValue) ? displayValue : null;
|
||||
output += "</td>";
|
||||
|
||||
@@ -462,7 +455,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
return output;
|
||||
}
|
||||
|
||||
private string GetQuickActionHtmlValueComparison(List<QuickActionStatusMonitor.cQuickActionMeasureValue> measureValues)
|
||||
private static string GetQuickActionHtmlValueComparison(List<QuickActionStatusMonitor.cQuickActionMeasureValue> measureValues)
|
||||
{
|
||||
string output = string.Empty;
|
||||
|
||||
@@ -474,6 +467,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
output += "<p>" + cMultiLanguageSupport.GetItem("QuickAction.Copy.Measure.Html") + "</p>";
|
||||
|
||||
cUtility.RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
|
||||
cUtility.RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
|
||||
|
||||
foreach (var measureValue in measureValues)
|
||||
{
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.F4SD.SupportCaseProtocoll.Models;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.MultiLanguage;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
internal class QuickTipStepProtocollEntry : IProtocollEntry
|
||||
internal static class QuickTipStepProtocollEntryOutput
|
||||
{
|
||||
private readonly cQuickTipElement _quickTipElementDefinition;
|
||||
private readonly bool _wasSuccessfull;
|
||||
|
||||
public QuickTipStepProtocollEntry(cQuickTipElement quickTipElementDefinition, bool wasSuccessfull)
|
||||
public static QuickTipStepProtocollEntry GetQuickTipStepProtocollEntry(cQuickTipElement quickTipElementDefinition, bool wasSuccessfull)
|
||||
{
|
||||
_quickTipElementDefinition = quickTipElementDefinition;
|
||||
_wasSuccessfull = wasSuccessfull;
|
||||
string ascii = GetAsciiOutput(quickTipElementDefinition, wasSuccessfull);
|
||||
string html = GetHtmlOutput(quickTipElementDefinition, wasSuccessfull);
|
||||
return new QuickTipStepProtocollEntry(ascii, html)
|
||||
{
|
||||
Name = quickTipElementDefinition.Name,
|
||||
WasSuccessfull = wasSuccessfull
|
||||
};
|
||||
}
|
||||
|
||||
public string GetAscii()
|
||||
private static string GetAsciiOutput(cQuickTipElement quickTipElementDefinition, bool wasSuccessfull)
|
||||
{
|
||||
string currentLanguage = cMultiLanguageSupport.CurrentLanguage;
|
||||
try
|
||||
{
|
||||
cMultiLanguageSupport.CurrentLanguage = cF4SDCockpitXmlConfig.Instance.HealthCardConfig.ProtocollLanguage ?? currentLanguage;
|
||||
string ascii = _wasSuccessfull ? cMultiLanguageSupport.GetItem("QuickTips.Copy.ManualStep.Successfull") : cMultiLanguageSupport.GetItem("QuickTips.Copy.ManualStep.Unsuccessfull");
|
||||
return string.Format(ascii, _quickTipElementDefinition?.Names?.GetValue());
|
||||
string ascii = wasSuccessfull ? cMultiLanguageSupport.GetItem("QuickTips.Copy.ManualStep.Successfull") : cMultiLanguageSupport.GetItem("QuickTips.Copy.ManualStep.Unsuccessfull");
|
||||
return string.Format(ascii, quickTipElementDefinition?.Names?.GetValue());
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -29,7 +32,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
}
|
||||
}
|
||||
|
||||
public string GetHtml()
|
||||
=> GetAscii();
|
||||
private static string GetHtmlOutput(cQuickTipElement quickTipElementDefinition, bool wasSuccessfull)
|
||||
=> GetAsciiOutput(quickTipElementDefinition, wasSuccessfull);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace FasdDesktopUi.Basics.Services.ProtocollService
|
||||
{
|
||||
internal class TextualProtocollEntry : IProtocollEntry
|
||||
{
|
||||
private readonly string _asciiText;
|
||||
private readonly string _htmlText;
|
||||
|
||||
public TextualProtocollEntry(string asciiText, string htmlText)
|
||||
{
|
||||
_asciiText = asciiText;
|
||||
_htmlText = htmlText;
|
||||
}
|
||||
|
||||
public string GetAscii() => _asciiText;
|
||||
|
||||
public string GetHtml() => _htmlText;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using C4IT.FASD.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.RelationService
|
||||
{
|
||||
public class RelationEventArgs : EventArgs
|
||||
{
|
||||
public ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> Relations { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
[assembly: InternalsVisibleTo("F4SD.Cockpit.Client.Test")]
|
||||
@@ -29,7 +27,7 @@ namespace FasdDesktopUi.Basics.Services.RelationService
|
||||
{
|
||||
try
|
||||
{
|
||||
_relations = new List<cF4sdApiSearchResultRelation>();
|
||||
_relations = relatedTo?.Select(searchResult => new cF4sdApiSearchResultRelation(searchResult)).ToList() ?? new List<cF4sdApiSearchResultRelation>();
|
||||
cF4sdStagedSearchResultRelationTaskId gatherRelationTask = await cFasdCockpitCommunicationBase.Instance.StartGatheringRelations(relatedTo, token);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.FASD.Cockpit.Communication;
|
||||
using FasdDesktopUi.Basics.CustomEvents;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using FasdDesktopUi.Pages.DetailsPage.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to manage the <see cref="ISupportCase"/> for the UI via the <see cref="SupportCaseProcessor"/>"
|
||||
/// </summary>
|
||||
public class SupportCaseController
|
||||
{
|
||||
private SupportCaseProcessor _supportCaseProcessor;
|
||||
private cF4sdApiSearchResultRelation _focusedRelation;
|
||||
private readonly Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation> _selectedRelations = new Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation>();
|
||||
private cHealthCard _selectedHealthcard = null;
|
||||
private bool _hasDirectionConnection = false;
|
||||
public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCaseProcessor?.SupportCaseDataProviderArtifact; }
|
||||
|
||||
internal void SetSupportCaseProcessor(SupportCaseProcessor supportCaseProcessor, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
|
||||
{
|
||||
IEnumerable<cF4sdApiSearchResultRelation> preselectedRelations = GetPreselectedRelations(supportCaseProcessor.GetCaseRelations(), preselectedIdentities);
|
||||
|
||||
ResetSelectedRelations(preselectedRelations);
|
||||
_hasDirectionConnection = false;
|
||||
|
||||
if (_supportCaseProcessor != null)
|
||||
{
|
||||
_supportCaseProcessor.AvailableCaseRelationsAdded -= HandleAvailableCaseRelationsAdded;
|
||||
_supportCaseProcessor.CaseDataChanged -= HandleCaseDataChanged;
|
||||
_supportCaseProcessor.SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionChanged -= HandleDirectConnectionChanged;
|
||||
}
|
||||
|
||||
_supportCaseProcessor = supportCaseProcessor;
|
||||
|
||||
_supportCaseProcessor.AvailableCaseRelationsAdded += HandleAvailableCaseRelationsAdded;
|
||||
_supportCaseProcessor.CaseDataChanged += HandleCaseDataChanged;
|
||||
_supportCaseProcessor.SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionChanged += HandleDirectConnectionChanged;
|
||||
}
|
||||
|
||||
private IEnumerable<cF4sdApiSearchResultRelation> GetPreselectedRelations
|
||||
(ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> loadedRelations, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
|
||||
{
|
||||
List<cF4sdApiSearchResultRelation> preselectedRelations = new List<cF4sdApiSearchResultRelation>();
|
||||
|
||||
foreach (var relationType in loadedRelations)
|
||||
{
|
||||
foreach (var relation in relationType)
|
||||
{
|
||||
if (relation.Identities.All(ri => preselectedIdentities.Any(i => i.Id == ri.Id)))
|
||||
preselectedRelations.Add(relation);
|
||||
}
|
||||
}
|
||||
|
||||
return preselectedRelations;
|
||||
}
|
||||
|
||||
private void ResetSelectedRelations(IEnumerable<cF4sdApiSearchResultRelation> preselectedRelations)
|
||||
{
|
||||
try
|
||||
{
|
||||
_selectedRelations.Clear();
|
||||
|
||||
if (preselectedRelations is null)
|
||||
return;
|
||||
|
||||
foreach (var relation in preselectedRelations)
|
||||
{
|
||||
_selectedRelations[cF4sdIdentityEntry.GetFromSearchResult(relation.Type)] = relation;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAvailableCaseRelationsAdded(object sender, RelationEventArgs e)
|
||||
=> AvailableCaseRelationsAdded?.Invoke(this, e);
|
||||
|
||||
private void HandleCaseDataChanged(object sender, SupportCaseDataEventArgs e)
|
||||
{
|
||||
CaseDataChanged?.Invoke(this, e);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await UpdateStatusOfSelectedRelations();
|
||||
|
||||
if (!_hasDirectionConnection)
|
||||
await SupportCaseDataProviderArtifact.DirectConnectionHelper.DirectConnectionStartAsync();
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleDirectConnectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
_hasDirectionConnection = SupportCaseDataProviderArtifact.DirectConnectionHelper.IsDirectConnectionActive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the currently for a support case relevant and shown relation.
|
||||
/// </summary>
|
||||
/// <remarks>Raises <see cref="FocusedRelationsChanged"/></remarks>
|
||||
internal void UpdateFocusedCaseRelation(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
try
|
||||
{
|
||||
_selectedHealthcard = _supportCaseProcessor.GetHealthcardFor(relation);
|
||||
HashSet<string> requiredTables = cHealthCard.GetRequiredTables(_selectedHealthcard);
|
||||
_ = Task.Run(async () => await _supportCaseProcessor.LoadSupportCaseDataAsync(relation, requiredTables));
|
||||
|
||||
List<enumFasdInformationClass> requiredInformationClasses = relation.Identities
|
||||
.Where(i => i.Class != enumFasdInformationClass.User)
|
||||
.Select(i => i.Class)
|
||||
.ToList();
|
||||
|
||||
if (relation.Type == enumF4sdSearchResultClass.User)
|
||||
requiredInformationClasses = new List<enumFasdInformationClass>() { enumFasdInformationClass.User };
|
||||
|
||||
if (requiredInformationClasses.Count == 0)
|
||||
requiredInformationClasses.Add(enumFasdInformationClass.User);
|
||||
|
||||
SupportCaseDataProviderArtifact.HealthCardDataHelper.TrySetSelectedHealthcard(requiredInformationClasses);
|
||||
|
||||
_focusedRelation = relation;
|
||||
|
||||
if (!_selectedRelations.Values.Contains(relation))
|
||||
_hasDirectionConnection = false;
|
||||
|
||||
_selectedRelations[cF4sdIdentityEntry.GetFromSearchResult(relation.Type)] = relation;
|
||||
_ = Task.Run(async () => await UpdateStatusOfSelectedRelations());
|
||||
|
||||
var focusedRelations = new cF4sdApiSearchResultRelation[1] { _focusedRelation };
|
||||
FocusedRelationsChanged?.Invoke(this, new RelationEventArgs()
|
||||
{
|
||||
Relations = focusedRelations.ToLookup(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type), r => r)
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateStatusOfSelectedRelations()
|
||||
{
|
||||
const string StatusString = "Status";
|
||||
|
||||
try
|
||||
{
|
||||
int? agentUserId = null;
|
||||
int? agentDeviceId = null;
|
||||
|
||||
// todo these values should not be recieved from the NamedParameters, instead from the relation it self
|
||||
if (SupportCaseDataProviderArtifact.NamedParameterEntries.TryGetValue("AgentUserId", out var userNamedParameter))
|
||||
{
|
||||
var value = userNamedParameter.GetValue();
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
agentUserId = int.Parse(value);
|
||||
}
|
||||
|
||||
|
||||
// todo these values should not be recieved from the NamedParameters, instead from the relation it self
|
||||
if (SupportCaseDataProviderArtifact.NamedParameterEntries.TryGetValue("AgentDeviceId", out var deviceNamedParameter))
|
||||
{
|
||||
var value = deviceNamedParameter.GetValue();
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
agentDeviceId = int.Parse(deviceNamedParameter.GetValue());
|
||||
}
|
||||
|
||||
foreach (var relationEntry in _selectedRelations)
|
||||
{
|
||||
if (relationEntry.Value is null)
|
||||
continue;
|
||||
|
||||
string statusValue = "Unknown";
|
||||
|
||||
switch (relationEntry.Key)
|
||||
{
|
||||
case enumFasdInformationClass.Computer:
|
||||
if (!agentDeviceId.HasValue)
|
||||
continue;
|
||||
|
||||
bool isDeviceOnline = await cFasdCockpitCommunicationBase.Instance.GetAgentOnlineStatus(agentDeviceId.Value);
|
||||
statusValue = isDeviceOnline ? "Online" : "Offline";
|
||||
break;
|
||||
case enumFasdInformationClass.User:
|
||||
if (!agentDeviceId.HasValue || !agentUserId.HasValue)
|
||||
continue;
|
||||
|
||||
bool isUserOnline = await cFasdCockpitCommunicationBase.Instance.GetAgentOnlineStatus(agentDeviceId.Value, agentUserId.Value);
|
||||
statusValue = isUserOnline ? "Online" : "Offline";
|
||||
break;
|
||||
case enumFasdInformationClass.Ticket:
|
||||
case enumFasdInformationClass.VirtualSession:
|
||||
case enumFasdInformationClass.MobileDevice:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (relationEntry.Value.Infos is null)
|
||||
relationEntry.Value.Infos = new Dictionary<string, string>();
|
||||
|
||||
relationEntry.Value.Infos[StatusString] = statusValue;
|
||||
}
|
||||
|
||||
IEnumerable<cHeadingDataModel> newHeadingData = SupportCaseHeadingController.GetHeadingData(_selectedRelations);
|
||||
HeadingDataChanged?.Invoke(this, new HeadingDataEventArgs(newHeadingData));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RefreshDataForCurrentlyFocusedRelationAsync()
|
||||
{
|
||||
await _supportCaseProcessor.UpdateLatestCaseDataFor(_focusedRelation);
|
||||
}
|
||||
|
||||
public List<List<cWidgetValueModel>> GetWidgetData()
|
||||
=> _supportCaseProcessor.GetWidgetData(_focusedRelation);
|
||||
|
||||
public cDetailsPageDataHistoryDataModel GetHistoryData()
|
||||
=> _supportCaseProcessor.GetHistoryData(_focusedRelation);
|
||||
|
||||
public List<cContainerCollectionData> GetContainerData()
|
||||
=> _supportCaseProcessor.GetContainerData(_focusedRelation);
|
||||
|
||||
public List<cMenuDataBase> GetMenuBarData()
|
||||
=> _supportCaseProcessor.GetMenuBarData(_focusedRelation);
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the currently for a support case relevant and shown relations have been updated.
|
||||
/// </summary>
|
||||
public event EventHandler<RelationEventArgs> FocusedRelationsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when newly available relations for a support case were added.
|
||||
/// </summary>
|
||||
public event EventHandler<RelationEventArgs> AvailableCaseRelationsAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the data set of a support case has changed.
|
||||
/// </summary>
|
||||
public event EventHandler<SupportCaseDataEventArgs> CaseDataChanged;
|
||||
|
||||
public event EventHandler<HeadingDataEventArgs> HeadingDataChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.MultiLanguage;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
|
||||
{
|
||||
internal class SupportCaseHeadingController
|
||||
{
|
||||
internal static IEnumerable<cHeadingDataModel> GetHeadingData(Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation> relations)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Enum.GetValues(typeof(enumFasdInformationClass))
|
||||
.Cast<enumFasdInformationClass>()
|
||||
.Select(informationClass =>
|
||||
{
|
||||
if (relations.TryGetValue(informationClass, out var relation) && relation != null)
|
||||
return GetHeadingDataFor(relation);
|
||||
else
|
||||
return GetEmptyHeadingDataFor(informationClass);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<cHeadingDataModel>();
|
||||
}
|
||||
|
||||
private static cHeadingDataModel GetEmptyHeadingDataFor(enumFasdInformationClass informationClass)
|
||||
{
|
||||
string headingText = string.Empty;
|
||||
|
||||
switch (informationClass)
|
||||
{
|
||||
case enumFasdInformationClass.Computer:
|
||||
headingText = cMultiLanguageSupport.GetItem("Header.Select.Computer");
|
||||
break;
|
||||
case enumFasdInformationClass.Ticket:
|
||||
headingText = cMultiLanguageSupport.GetItem("Header.Select.Ticket");
|
||||
break;
|
||||
case enumFasdInformationClass.VirtualSession:
|
||||
headingText = cMultiLanguageSupport.GetItem("Header.Select.VirtualSession");
|
||||
break;
|
||||
case enumFasdInformationClass.MobileDevice:
|
||||
headingText = cMultiLanguageSupport.GetItem("Header.Select.MobileDevice");
|
||||
break;
|
||||
}
|
||||
|
||||
return new cHeadingDataModel() { InformationClass = informationClass, HeadingText = headingText };
|
||||
}
|
||||
|
||||
private static cHeadingDataModel GetHeadingDataFor(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
const string StatusString = "Status";
|
||||
bool isOnline = false;
|
||||
|
||||
string statusValue = "Unknown";
|
||||
if (relation?.Infos?.TryGetValue(StatusString, out var statusValueFromDictionary) ?? false)
|
||||
statusValue = statusValueFromDictionary;
|
||||
|
||||
switch (relation.Type)
|
||||
{
|
||||
case enumF4sdSearchResultClass.Computer:
|
||||
case enumF4sdSearchResultClass.User:
|
||||
isOnline = string.Equals(statusValue, "Online", StringComparison.InvariantCultureIgnoreCase);
|
||||
break;
|
||||
case enumF4sdSearchResultClass.Ticket:
|
||||
isOnline = !string.Equals(statusValue, nameof(enumTicketStatus.Closed));
|
||||
break;
|
||||
case enumF4sdSearchResultClass.VirtualSession:
|
||||
isOnline = string.Equals(statusValue, nameof(enumCitrixSessionStatus.Active));
|
||||
break;
|
||||
case enumF4sdSearchResultClass.MobileDevice:
|
||||
break;
|
||||
}
|
||||
|
||||
return new cHeadingDataModel()
|
||||
{
|
||||
HeadingText = string.IsNullOrEmpty(relation.Name) ? relation.DisplayName : relation.Name,
|
||||
InformationClass = cF4sdIdentityEntry.GetFromSearchResult(relation.Type),
|
||||
IsOnline = isOnline,
|
||||
Identities = relation.Identities,
|
||||
Realtion = relation
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using C4IT.FASD.Base;
|
||||
using FasdDesktopUi.Basics.Services.RelationService;
|
||||
using FasdDesktopUi.Basics.CustomEvents;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
{
|
||||
@@ -12,10 +13,13 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
void Initialize();
|
||||
void AddCaseRelations(ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> relations);
|
||||
ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations();
|
||||
IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> GetSupportCaseHealthcardData(object identities, object valueAddress);
|
||||
void UpdateSupportCaseDataCache();
|
||||
Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad);
|
||||
IEnumerable<object> GetSupportCaseHealthcardData(cF4sdApiSearchResultRelation relation, cValueAddress valueAddress);
|
||||
void UpdateSupportCaseDataCache(cF4sdApiSearchResultRelation relation, IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> tables);
|
||||
void InvalidateCaseDataCacheFor(cF4sdApiSearchResultRelation relation);
|
||||
void InvalidateLatestCaseDataCacheFor(cF4sdApiSearchResultRelation relation, out ICollection<cF4SDHealthCardRawData.cHealthCardTable> invalidatedTables);
|
||||
|
||||
event EventHandler<RelationEventArgs> CaseRelationsAdded;
|
||||
event EventHandler<object> SupportCaseDataCacheHasChanged;
|
||||
event EventHandler<SupportCaseDataEventArgs> SupportCaseDataCacheHasChanged;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.FASD.Cockpit.Communication;
|
||||
using FasdDesktopUi.Basics.CustomEvents;
|
||||
using FasdDesktopUi.Basics.Services.RelationService;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
public class SupportCase : ISupportCase
|
||||
{
|
||||
private readonly Dictionary<enumFasdInformationClass, IList<cF4sdApiSearchResultRelation>> _caseRelations = new Dictionary<enumFasdInformationClass, IList<cF4sdApiSearchResultRelation>>();
|
||||
//private readonly Lookup<IdentitySet, cF4SDHealthCardRawData.cHealthCardTable> _supportCaseDataCache;
|
||||
private readonly Dictionary<string, Dictionary<cF4sdApiSearchResultRelation, cF4SDHealthCardRawData.cHealthCardTable>> _supportCaseDataCache = new Dictionary<string, Dictionary<cF4sdApiSearchResultRelation, cF4SDHealthCardRawData.cHealthCardTable>>();
|
||||
|
||||
internal readonly Guid Id;
|
||||
private readonly IRelationService _relationService;
|
||||
@@ -67,12 +67,12 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
foreach (var relationType in relations)
|
||||
{
|
||||
if (_caseRelations.TryGetValue(relationType.Key, out var caseRelation))
|
||||
caseRelation = caseRelation.Union(relationType, new SearchResultRelationEqualityComparer()).ToList();
|
||||
caseRelation = caseRelation.Union(relationType).ToList();
|
||||
else
|
||||
_caseRelations.Add(relationType.Key, relationType.ToList());
|
||||
|
||||
if (SupportCaseDataProviderArtifact?.CaseRelations?.TryGetValue(relationType.Key, out var caseRelations) ?? false)
|
||||
caseRelations = caseRelations.Union(relationType, new SearchResultRelationEqualityComparer()).ToList();
|
||||
caseRelations = caseRelations.Union(relationType).ToList();
|
||||
else
|
||||
SupportCaseDataProviderArtifact?.CaseRelations?.Add(relationType.Key, relationType.ToList());
|
||||
}
|
||||
@@ -85,14 +85,158 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSupportCaseDataCache()
|
||||
public async Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
try
|
||||
{
|
||||
cF4SDHealthCardRawData rawData = null;
|
||||
|
||||
// todo this is only a temporary fix. Currently the tablesToLoad contain also detail tables
|
||||
// and tables won't be loaded e.g. the QuickActionHistory
|
||||
bool isDataComplete = tablesToLoad.Any(t =>
|
||||
_supportCaseDataCache.TryGetValue(t, out var cachedTables)
|
||||
&& cachedTables.TryGetValue(relation, out var table)
|
||||
&& !table.IsIncomplete && !table.Columns.Values.Any(c => c.IsIncomplete)
|
||||
);
|
||||
|
||||
var rawDataRequest = new cF4sdHealthCardRawDataRequest()
|
||||
{
|
||||
Identities = relation.Identities,
|
||||
Tables = tablesToLoad.ToList(),
|
||||
MaxAge = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.SearchResultAge ?? 14
|
||||
};
|
||||
|
||||
while (!isDataComplete)
|
||||
{
|
||||
if (rawData is null)
|
||||
{
|
||||
rawData = await cFasdCockpitCommunicationBase.Instance.GetHealthCardData(rawDataRequest);
|
||||
await SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.SetHealthCardRawData(rawData, rawDataRequest.Identities);
|
||||
}
|
||||
else
|
||||
{
|
||||
rawData = await cFasdCockpitCommunicationBase.Instance.GetHealthCardData(rawData.Id);
|
||||
await SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.UpdateHealthcardRawData(rawData);
|
||||
}
|
||||
|
||||
UpdateSupportCaseDataCache(relation, rawData?.Tables?.Values);
|
||||
|
||||
isDataComplete = rawData?.Tables?
|
||||
.Where(table => table.Key.StartsWith("Computation_") == false)
|
||||
.All(table => !table.Value.IsIncomplete && !table.Value.Columns.Values.Any(c => c.IsIncomplete)) ?? false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> GetSupportCaseHealthcardData(object identities, object valueAddress)
|
||||
public void UpdateSupportCaseDataCache(cF4sdApiSearchResultRelation relation, IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> tables)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
try
|
||||
{
|
||||
if (tables is null)
|
||||
return;
|
||||
|
||||
List<cF4SDHealthCardRawData.cHealthCardTable> dataTables = tables?.ToList();
|
||||
|
||||
foreach (var table in dataTables)
|
||||
{
|
||||
if (string.IsNullOrEmpty(table.Name))
|
||||
continue;
|
||||
|
||||
if (!_supportCaseDataCache.ContainsKey(table.Name))
|
||||
_supportCaseDataCache.Add(table.Name, new Dictionary<cF4sdApiSearchResultRelation, cF4SDHealthCardRawData.cHealthCardTable>());
|
||||
|
||||
if (!_supportCaseDataCache[table.Name].ContainsKey(relation))
|
||||
_supportCaseDataCache[table.Name][relation] = table;
|
||||
else
|
||||
MergeTable(_supportCaseDataCache[table.Name][relation], table);
|
||||
}
|
||||
|
||||
SupportCaseDataCacheHasChanged?.Invoke(this, new SupportCaseDataEventArgs() { Relation = relation, DataTables = dataTables });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
|
||||
void MergeTable(cF4SDHealthCardRawData.cHealthCardTable existingTable, cF4SDHealthCardRawData.cHealthCardTable newTable)
|
||||
{
|
||||
foreach (var newColumn in newTable.Columns)
|
||||
{
|
||||
existingTable.Columns[newColumn.Key] = newColumn.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateCaseDataCacheFor(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var tableCache in _supportCaseDataCache.Values)
|
||||
{
|
||||
tableCache.Remove(relation);
|
||||
}
|
||||
|
||||
// todo: invoke SupportCaseDataChache with empty tables
|
||||
// SupportCaseDataCacheHasChanged?.Invoke(this, new SupportCaseDataEventArgs() { Relation = relation });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateLatestCaseDataCacheFor(cF4sdApiSearchResultRelation relation, out ICollection<cF4SDHealthCardRawData.cHealthCardTable> invalidatedTables)
|
||||
{
|
||||
invalidatedTables = new HashSet<cF4SDHealthCardRawData.cHealthCardTable>();
|
||||
try
|
||||
{
|
||||
foreach (var tableCache in _supportCaseDataCache.Values)
|
||||
{
|
||||
if (!tableCache.TryGetValue(relation, out cF4SDHealthCardRawData.cHealthCardTable table))
|
||||
continue;
|
||||
|
||||
table.IsIncomplete = true;
|
||||
|
||||
foreach (var column in table.Columns.Values)
|
||||
{
|
||||
column.Values[0] = null;
|
||||
column.IsIncomplete = true;
|
||||
}
|
||||
|
||||
invalidatedTables.Add(table);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetSupportCaseHealthcardData(cF4sdApiSearchResultRelation relation, cValueAddress valueAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_supportCaseDataCache.TryGetValue(valueAddress.ValueTable, out var tables))
|
||||
return null;
|
||||
|
||||
if (!tables.TryGetValue(relation, out var table))
|
||||
return null;
|
||||
|
||||
if (!table.Columns.TryGetValue(valueAddress.ValueColumn, out var column))
|
||||
return null;
|
||||
|
||||
return column.Values;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void HandleRelationsFound(object sender, StagedSearchResultRelationsEventArgs e)
|
||||
@@ -107,19 +251,6 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
=> AddCaseRelations(relations?.ToLookup(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type), r => r));
|
||||
|
||||
public event EventHandler<RelationEventArgs> CaseRelationsAdded;
|
||||
public event EventHandler<object> SupportCaseDataCacheHasChanged; // Lookup for IdentitySet and tables which has been updated
|
||||
|
||||
private class SearchResultRelationEqualityComparer : IEqualityComparer<cF4sdApiSearchResultRelation>
|
||||
{
|
||||
public bool Equals(cF4sdApiSearchResultRelation x, cF4sdApiSearchResultRelation y)
|
||||
{
|
||||
return x.isEqual(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(cF4sdApiSearchResultRelation obj)
|
||||
{
|
||||
return obj.GetHashCode();
|
||||
}
|
||||
}
|
||||
public event EventHandler<SupportCaseDataEventArgs> SupportCaseDataCacheHasChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
if (primaryIdentity is null)
|
||||
throw new InvalidEnumArgumentException($"{nameof(primaryIdentity)} must not be null.");
|
||||
|
||||
if (primaryIdentity.Class != enumFasdInformationClass.User)
|
||||
throw new InvalidEnumArgumentException($"{nameof(primaryIdentity)} must be of class {nameof(enumFasdInformationClass.User)}.");
|
||||
|
||||
if (_supportCases.TryGetValue(primaryIdentity.Id, out var supportCase))
|
||||
{
|
||||
supportCase.Initialize();
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.FASD.Cockpit.Communication;
|
||||
using C4IT.Logging;
|
||||
using FasdDesktopUi.Basics.CustomEvents;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using FasdDesktopUi.Pages.DetailsPage.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for processing raw data of a <see cref="ISupportCase"/> for the UI in a certain way.
|
||||
/// </summary>
|
||||
internal class SupportCaseProcessor
|
||||
{
|
||||
private ISupportCase _supportCase;
|
||||
public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCase?.SupportCaseDataProviderArtifact; }
|
||||
|
||||
private readonly Dictionary<cF4sdApiSearchResultRelation, cDetailsPageData> _detailsPageDataCache = new Dictionary<cF4sdApiSearchResultRelation, cDetailsPageData>();
|
||||
|
||||
private readonly Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, object>> _namedParameterCache = new Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, object>>();
|
||||
|
||||
internal void SetSupportCase(ISupportCase supportCase)
|
||||
{
|
||||
if (_supportCase != null)
|
||||
{
|
||||
_supportCase.CaseRelationsAdded -= HandleSupportCaseRelationsAdded;
|
||||
_supportCase.SupportCaseDataCacheHasChanged -= HandleSupportCaseDataCacheHasChanged;
|
||||
}
|
||||
|
||||
_supportCase = supportCase;
|
||||
|
||||
_supportCase.CaseRelationsAdded += HandleSupportCaseRelationsAdded;
|
||||
_supportCase.SupportCaseDataCacheHasChanged += HandleSupportCaseDataCacheHasChanged;
|
||||
}
|
||||
|
||||
private void HandleSupportCaseRelationsAdded(object sender, RelationEventArgs e)
|
||||
=> AvailableCaseRelationsAdded?.Invoke(this, e);
|
||||
|
||||
private async void HandleSupportCaseDataCacheHasChanged(object sender, SupportCaseDataEventArgs e)
|
||||
{
|
||||
bool isArtifactShowingCorrectHealthCard
|
||||
= SupportCaseDataProviderArtifact.HealthCardDataHelper.SelectedHealthCard == GetHealthcardFor(e.Relation);
|
||||
|
||||
if (!isArtifactShowingCorrectHealthCard)
|
||||
{
|
||||
// todo this can probably be removed, as soon as the last dependency of the SupportCaseDataProviderArtifact is gone.
|
||||
// till then the detailspageData gets overriden with the detailspageData of the new relation.
|
||||
// However, the removal shouldn't be much of a problem, due to the fact the Artifact also stores the raw data
|
||||
_detailsPageDataCache.Remove(e.Relation);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_detailsPageDataCache.TryGetValue(e.Relation, out var cachedData))
|
||||
{
|
||||
cDetailsPageData detailData = _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataWithoutHeading();
|
||||
cachedData.WidgetData = detailData.WidgetData;
|
||||
cachedData.DataHistoryList = detailData.DataHistoryList;
|
||||
cachedData.MenuBarData = detailData.MenuBarData;
|
||||
cachedData.DataContainerCollectionList = detailData.DataContainerCollectionList;
|
||||
}
|
||||
else
|
||||
{
|
||||
_detailsPageDataCache[e.Relation] = await _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataAsync();
|
||||
}
|
||||
|
||||
UpdateNamedParameters(e.Relation, e.DataTables);
|
||||
CaseDataChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void UpdateNamedParameters(cF4sdApiSearchResultRelation relation, IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> dataTables)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_namedParameterCache.ContainsKey(relation))
|
||||
_namedParameterCache.Add(relation, new Dictionary<string, object>());
|
||||
|
||||
var healthcard = GetHealthcardFor(relation);
|
||||
|
||||
foreach (var namedParameter in cHealthCardPrerequisites.GetNamedParameters(healthcard).Values)
|
||||
{
|
||||
var table = dataTables.FirstOrDefault(t => t.Name == namedParameter.DatabaseInfo.ValueTable);
|
||||
|
||||
if (table is null)
|
||||
continue;
|
||||
|
||||
if (!table.Columns.TryGetValue(namedParameter.DatabaseInfo.ValueColumn, out var column))
|
||||
continue;
|
||||
|
||||
string value = cUtility.RawValueFormatter.GetDisplayValue(column.Values.FirstOrDefault(), namedParameter.Display);
|
||||
_namedParameterCache[relation][namedParameter.ParameterName] = value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad)
|
||||
{
|
||||
_ = Task.Run(async () => await _supportCase.LoadSupportCaseDataAsync(relation, tablesToLoad.Where(t => !t.Contains("-details-"))));
|
||||
|
||||
if (!_detailsPageDataCache.TryGetValue(relation, out var detailsData))
|
||||
{
|
||||
detailsData = await _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataAsync();
|
||||
_detailsPageDataCache.Add(relation, detailsData);
|
||||
}
|
||||
|
||||
CaseDataChanged?.Invoke(this, new SupportCaseDataEventArgs());
|
||||
}
|
||||
|
||||
public async Task UpdateLatestCaseDataFor(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
try
|
||||
{
|
||||
int? agentUserId = relation.Identities.FirstOrDefault(i => i.Class == enumFasdInformationClass.User)?.agentId;
|
||||
int? agentDeviceId = relation.Identities.FirstOrDefault(i => i.Class == enumFasdInformationClass.Computer)?.agentId;
|
||||
|
||||
await ActualizeDataAsync(agentUserId, agentDeviceId);
|
||||
_supportCase.InvalidateLatestCaseDataCacheFor(relation, out var invalidatedTables);
|
||||
_detailsPageDataCache.Remove(relation);
|
||||
await _supportCase.LoadSupportCaseDataAsync(relation, invalidatedTables.Select(t => t.Name));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<enumActualizeStatus> ActualizeDataAsync(int? agentUserId, int? agentDeviceId)
|
||||
{
|
||||
var status = enumActualizeStatus.unknown;
|
||||
|
||||
try
|
||||
{
|
||||
TimeSpan refreshDelay = TimeSpan.FromMilliseconds(500);
|
||||
const int maxPollCount = 20;
|
||||
|
||||
if (!agentDeviceId.HasValue)
|
||||
{
|
||||
LogEntry("Coudldn't acutalize data. There was no valid AgentDeviceId found.", LogLevels.Error);
|
||||
return status;
|
||||
}
|
||||
|
||||
var taskId = await cFasdCockpitCommunicationBase.Instance.ActualizeAgentData(agentDeviceId.Value, agentUserId);
|
||||
|
||||
if (taskId == Guid.Empty)
|
||||
return enumActualizeStatus.failed;
|
||||
|
||||
enumFasdInformationClass informationClass = agentUserId != null ? enumFasdInformationClass.User : enumFasdInformationClass.Computer;
|
||||
int pollCount = 0;
|
||||
|
||||
do
|
||||
{
|
||||
status = await cFasdCockpitCommunicationBase.Instance.GetActualizeAgentDataStatus(taskId, informationClass);
|
||||
|
||||
if (status == enumActualizeStatus.unknown)
|
||||
{
|
||||
pollCount++;
|
||||
if (pollCount >= maxPollCount)
|
||||
return status;
|
||||
|
||||
await Task.Delay(refreshDelay);
|
||||
}
|
||||
} while (status == enumActualizeStatus.unknown);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
internal ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations()
|
||||
=> _supportCase.GetCaseRelations();
|
||||
|
||||
internal cHealthCard GetHealthcardFor(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
var availableHealthCards = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.HealthCards?.Values;
|
||||
|
||||
if (availableHealthCards is null || availableHealthCards.Count == 0)
|
||||
return null;
|
||||
|
||||
return availableHealthCards
|
||||
.FirstOrDefault(hc =>
|
||||
hc.InformationClasses.All(i => i == cF4sdIdentityEntry.GetFromSearchResult(relation.Type))
|
||||
&& HasCockpitUserRequiredRoles(hc.RequiredRoles));
|
||||
}
|
||||
|
||||
private static bool HasCockpitUserRequiredRoles(List<string> requiredRoles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (requiredRoles is null || requiredRoles.Count == 0)
|
||||
return true;
|
||||
|
||||
List<string> roles = null;
|
||||
lock (cFasdCockpitCommunicationBase.CockpitUserInfoLock)
|
||||
{
|
||||
roles = cFasdCockpitCommunicationBase.CockpitUserInfo?.Roles;
|
||||
}
|
||||
if (roles is null || roles.Count == 0)
|
||||
return false;
|
||||
|
||||
foreach (var requiredRole in requiredRoles)
|
||||
{
|
||||
if (roles.Contains(requiredRole, StringComparer.InvariantCultureIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<List<cWidgetValueModel>> GetWidgetData(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
List<List<cWidgetValueModel>> widgetData = null;
|
||||
|
||||
if (_detailsPageDataCache.TryGetValue(relation, out var detailsData))
|
||||
widgetData = detailsData?.WidgetData;
|
||||
|
||||
return widgetData ?? new List<List<cWidgetValueModel>>();
|
||||
}
|
||||
|
||||
public cDetailsPageDataHistoryDataModel GetHistoryData(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
cDetailsPageDataHistoryDataModel historyData = null;
|
||||
|
||||
if (_detailsPageDataCache.TryGetValue(relation, out var detailsData))
|
||||
historyData = detailsData?.DataHistoryList;
|
||||
|
||||
return historyData ?? new cDetailsPageDataHistoryDataModel();
|
||||
}
|
||||
|
||||
public List<cContainerCollectionData> GetContainerData(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
List<cContainerCollectionData> containerData = null;
|
||||
|
||||
if (_detailsPageDataCache.TryGetValue(relation, out var detailsData))
|
||||
containerData = detailsData?.DataContainerCollectionList;
|
||||
|
||||
return containerData ?? new List<cContainerCollectionData>();
|
||||
}
|
||||
|
||||
public List<cMenuDataBase> GetMenuBarData(cF4sdApiSearchResultRelation relation)
|
||||
{
|
||||
List<cMenuDataBase> menuData = null;
|
||||
|
||||
if (_detailsPageDataCache.TryGetValue(relation, out var detailsData))
|
||||
menuData = detailsData?.MenuBarData;
|
||||
|
||||
return menuData ?? new List<cMenuDataBase>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when newly available relations for a support case were added.
|
||||
/// </summary>
|
||||
public event EventHandler<RelationEventArgs> AvailableCaseRelationsAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the data set of a support case has changed.
|
||||
/// </summary>
|
||||
public event EventHandler<SupportCaseDataEventArgs> CaseDataChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FasdDesktopUi.Basics.Services.SupportCase
|
||||
{
|
||||
internal class SupportCaseProcessorFactory
|
||||
{
|
||||
private readonly static Dictionary<Guid, SupportCaseProcessor> _supportCaseProccesors = new Dictionary<Guid, SupportCaseProcessor>();
|
||||
|
||||
internal static SupportCaseProcessor Get(Guid id)
|
||||
{
|
||||
if (!_supportCaseProccesors.ContainsKey(id))
|
||||
_supportCaseProccesors.Add(id, new SupportCaseProcessor());
|
||||
|
||||
return _supportCaseProccesors[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,9 @@ using System.Windows.Threading;
|
||||
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.FASD.Cockpit.Communication;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using FasdDesktopUi.Basics.Services.Models;
|
||||
using FasdDesktopUi;
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
using FasdDesktopUi.Basics.Services.Models;
|
||||
#if isDemo
|
||||
using System.Net;
|
||||
using FasdCockpitCommunicationDemo;
|
||||
@@ -19,11 +20,10 @@ namespace FasdDesktopUi.Basics.Services
|
||||
{
|
||||
public sealed class TicketOverviewUpdateService
|
||||
{
|
||||
private static readonly TimeSpan RefreshInterval = TimeSpan.FromMinutes(5);
|
||||
private static readonly string[] OverviewKeys = new[]
|
||||
{
|
||||
"TicketsNew",
|
||||
"TicketsActive",
|
||||
private static readonly string[] OverviewKeys = new[]
|
||||
{
|
||||
"TicketsNew",
|
||||
"TicketsActive",
|
||||
"TicketsCritical",
|
||||
"TicketsNewInfo",
|
||||
"IncidentNew",
|
||||
@@ -33,16 +33,22 @@ namespace FasdDesktopUi.Basics.Services
|
||||
"UnassignedTickets",
|
||||
"UnassignedTicketsCritical"
|
||||
};
|
||||
private const string DemoTicketDetailsKey = "Demo.HasTicketDetails";
|
||||
private readonly Dispatcher _dispatcher;
|
||||
private readonly Dictionary<string, TileCounts> _currentCounts = new Dictionary<string, TileCounts>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<(string Key, bool UseRoleScope), List<cF4sdApiSearchResultRelation>> _demoRelations = new Dictionary<(string, bool), List<cF4sdApiSearchResultRelation>>();
|
||||
private DispatcherTimer _timer;
|
||||
private bool _isFetching;
|
||||
private bool _fetchRetryPending;
|
||||
private bool _isDemo;
|
||||
private bool _initialized;
|
||||
private readonly Random _random = new Random();
|
||||
private const string DemoTicketDetailsKey = "Demo.HasTicketDetails";
|
||||
private readonly Dispatcher _dispatcher;
|
||||
private readonly Dictionary<string, TileCounts> _currentCounts = new Dictionary<string, TileCounts>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<(string Key, bool UseRoleScope), List<cF4sdApiSearchResultRelation>> _demoRelations = new Dictionary<(string, bool), List<cF4sdApiSearchResultRelation>>();
|
||||
private readonly HashSet<TileScope> _pendingScopes = new HashSet<TileScope>();
|
||||
private readonly HashSet<TileScope> _initializedScopes = new HashSet<TileScope>();
|
||||
private readonly object _fetchLock = new object();
|
||||
private readonly HashSet<TileScope> _retryScopes = new HashSet<TileScope>();
|
||||
private DispatcherTimer _personalTimer;
|
||||
private DispatcherTimer _roleTimer;
|
||||
private Task _fetchWorker;
|
||||
private bool _retryScheduled;
|
||||
private bool _isDemo;
|
||||
private bool _initialized;
|
||||
private bool _isEnabled;
|
||||
private readonly Random _random = new Random();
|
||||
#if isDemo
|
||||
private readonly List<DemoTicketRecord> _persistedDemoTickets = new List<DemoTicketRecord>();
|
||||
private readonly List<DemoTicketTemplate> _demoTemplates = new List<DemoTicketTemplate>();
|
||||
@@ -67,138 +73,338 @@ namespace FasdDesktopUi.Basics.Services
|
||||
#endif
|
||||
}
|
||||
|
||||
static TicketOverviewUpdateService()
|
||||
{
|
||||
#if isDemo
|
||||
Instance = new TicketOverviewUpdateService();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static TicketOverviewUpdateService Instance { get; } = null;
|
||||
static TicketOverviewUpdateService()
|
||||
{
|
||||
Instance = new TicketOverviewUpdateService();
|
||||
}
|
||||
|
||||
public static TicketOverviewUpdateService Instance { get; }
|
||||
|
||||
public event EventHandler<TicketOverviewCountsChangedEventArgs> OverviewCountsChanged;
|
||||
|
||||
public IReadOnlyDictionary<string, TileCounts> CurrentCounts => _currentCounts;
|
||||
public IReadOnlyDictionary<string, TileCounts> CurrentCounts => _currentCounts;
|
||||
|
||||
public bool IsScopeInitialized(TileScope scope)
|
||||
{
|
||||
lock (_fetchLock)
|
||||
{
|
||||
return _initializedScopes.Contains(scope);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AreAllScopesInitialized
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_fetchLock)
|
||||
{
|
||||
return _initializedScopes.Contains(TileScope.Personal)
|
||||
&& _initializedScopes.Contains(TileScope.Role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_initialized)
|
||||
return;
|
||||
public void Start()
|
||||
{
|
||||
UpdateAvailability(true);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
UpdateAvailability(false);
|
||||
}
|
||||
|
||||
public void UpdateAvailability(bool isEnabled)
|
||||
{
|
||||
if (isEnabled)
|
||||
{
|
||||
if (!_isEnabled)
|
||||
{
|
||||
_isEnabled = true;
|
||||
StartInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
RefreshTimerIntervals();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isEnabled)
|
||||
StopInternal();
|
||||
|
||||
_isEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartInternal()
|
||||
{
|
||||
if (_initialized)
|
||||
return;
|
||||
|
||||
_initialized = true;
|
||||
#if isDemo
|
||||
_isDemo = true;
|
||||
LoadPersistedDemoTickets();
|
||||
#else
|
||||
_isDemo = cFasdCockpitCommunicationBase.Instance?.IsDemo() == true;
|
||||
#endif
|
||||
|
||||
InitializeTimers();
|
||||
_ = FetchAsync();
|
||||
}
|
||||
|
||||
private void StopInternal()
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
_initialized = false;
|
||||
lock (_fetchLock)
|
||||
{
|
||||
_pendingScopes.Clear();
|
||||
_initializedScopes.Clear();
|
||||
}
|
||||
|
||||
lock (_retryScopes)
|
||||
{
|
||||
_retryScheduled = false;
|
||||
_retryScopes.Clear();
|
||||
}
|
||||
|
||||
_dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_personalTimer?.Stop();
|
||||
_roleTimer?.Stop();
|
||||
_personalTimer = null;
|
||||
_roleTimer = null;
|
||||
|
||||
foreach (var key in OverviewKeys)
|
||||
{
|
||||
_currentCounts[key] = TileCounts.Empty;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeTimers()
|
||||
{
|
||||
_personalTimer = CreateScopeTimer(TileScope.Personal);
|
||||
_roleTimer = CreateScopeTimer(TileScope.Role);
|
||||
|
||||
_personalTimer?.Start();
|
||||
_roleTimer?.Start();
|
||||
}
|
||||
|
||||
private DispatcherTimer CreateScopeTimer(TileScope scope)
|
||||
{
|
||||
var interval = GetPollingInterval(scope);
|
||||
var timer = new DispatcherTimer(interval, DispatcherPriority.Background, async (s, e) => await FetchAsync(scope).ConfigureAwait(false), _dispatcher)
|
||||
{
|
||||
IsEnabled = false
|
||||
};
|
||||
return timer;
|
||||
}
|
||||
|
||||
private TimeSpan GetPollingInterval(TileScope scope)
|
||||
{
|
||||
var ticketConfig = cFasdCockpitConfig.Instance?.Global?.TicketConfiguration;
|
||||
int minutes = scope == TileScope.Role
|
||||
? cF4sdTicketConfig.DefaultOverviewPollingRole
|
||||
: cF4sdTicketConfig.DefaultOverviewPollingPersonal;
|
||||
|
||||
if (ticketConfig != null)
|
||||
{
|
||||
minutes = scope == TileScope.Role
|
||||
? ticketConfig.OverviewPollingRole
|
||||
: ticketConfig.OverviewPollingPersonal;
|
||||
}
|
||||
|
||||
if (minutes < 1)
|
||||
minutes = 1;
|
||||
|
||||
return TimeSpan.FromMinutes(minutes);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
#if isDemo
|
||||
_isDemo = true;
|
||||
LoadPersistedDemoTickets();
|
||||
#else
|
||||
_isDemo = cFasdCockpitCommunicationBase.Instance?.IsDemo() == true;
|
||||
#endif
|
||||
|
||||
if (!_isDemo)
|
||||
{
|
||||
_timer = new DispatcherTimer(RefreshInterval, DispatcherPriority.Background, async (s, e) => await FetchAsync().ConfigureAwait(false), _dispatcher);
|
||||
_timer.Start();
|
||||
_ = FetchAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = FetchAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task FetchAsync()
|
||||
{
|
||||
if (_isFetching)
|
||||
return;
|
||||
|
||||
var communication = cFasdCockpitCommunicationBase.Instance;
|
||||
if (communication == null)
|
||||
{
|
||||
ScheduleFetchRetry();
|
||||
return;
|
||||
}
|
||||
|
||||
_isFetching = true;
|
||||
try
|
||||
{
|
||||
_isDemo = communication?.IsDemo() == true;
|
||||
if (_isDemo && _timer != null)
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer = null;
|
||||
}
|
||||
var counts = await Task.Run(() => RetrieveCountsAsync()).ConfigureAwait(false);
|
||||
if (counts != null)
|
||||
{
|
||||
await _dispatcher.InvokeAsync(() => ProcessCounts(counts));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isFetching = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, TileCounts> RetrieveCountsAsync()
|
||||
{
|
||||
var communication = cFasdCockpitCommunicationBase.Instance;
|
||||
if (communication == null)
|
||||
return null;
|
||||
|
||||
var result = new Dictionary<string, TileCounts>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var key in OverviewKeys)
|
||||
{
|
||||
var personalTask = communication.GetTicketOverviewRelations(key, useRoleScope: false, count: 0);
|
||||
var roleTask = communication.GetTicketOverviewRelations(key, useRoleScope: true, count: 0);
|
||||
Task.WaitAll(personalTask, roleTask);
|
||||
|
||||
int personalCount = personalTask.Result?.Count ?? 0;
|
||||
int roleCount = roleTask.Result?.Count ?? 0;
|
||||
|
||||
if (_isDemo)
|
||||
{
|
||||
personalCount += GetDemoRelationCount(key, useRoleScope: false);
|
||||
roleCount += GetDemoRelationCount(key, useRoleScope: true);
|
||||
}
|
||||
|
||||
result[key] = new TileCounts(personalCount, roleCount);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ProcessCounts(Dictionary<string, TileCounts> newCounts)
|
||||
{
|
||||
var changes = new List<TileCountChange>();
|
||||
bool hasPrevious = _currentCounts.Values.Any(tc => tc.Personal > 0 || tc.Role > 0);
|
||||
|
||||
foreach (var key in OverviewKeys)
|
||||
{
|
||||
var previous = _currentCounts[key];
|
||||
var current = newCounts.TryGetValue(key, out var value) ? value : TileCounts.Empty;
|
||||
|
||||
if (previous.Personal != current.Personal)
|
||||
{
|
||||
changes.Add(new TileCountChange(key, TileScope.Personal, previous.Personal, current.Personal));
|
||||
}
|
||||
|
||||
if (previous.Role != current.Role)
|
||||
{
|
||||
changes.Add(new TileCountChange(key, TileScope.Role, previous.Role, current.Role));
|
||||
}
|
||||
|
||||
_currentCounts[key] = current;
|
||||
}
|
||||
|
||||
if (!hasPrevious)
|
||||
return;
|
||||
|
||||
if (changes.Count == 0)
|
||||
return;
|
||||
|
||||
var args = new TicketOverviewCountsChangedEventArgs(changes, new Dictionary<string, TileCounts>(_currentCounts));
|
||||
OverviewCountsChanged?.Invoke(this, args);
|
||||
}
|
||||
public Task FetchAsync()
|
||||
{
|
||||
if (!_isEnabled)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return QueueFetchAsync(new[] { TileScope.Personal, TileScope.Role });
|
||||
}
|
||||
|
||||
public Task FetchAsync(TileScope scope)
|
||||
{
|
||||
if (!_isEnabled)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return QueueFetchAsync(new[] { scope });
|
||||
}
|
||||
|
||||
private Task QueueFetchAsync(IEnumerable<TileScope> scopes)
|
||||
{
|
||||
if (!_isEnabled)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (scopes == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
lock (_fetchLock)
|
||||
{
|
||||
foreach (var scope in scopes)
|
||||
{
|
||||
_pendingScopes.Add(scope);
|
||||
}
|
||||
|
||||
if (_fetchWorker == null || _fetchWorker.IsCompleted)
|
||||
{
|
||||
_fetchWorker = Task.Run(ProcessFetchQueueAsync);
|
||||
}
|
||||
|
||||
return _fetchWorker;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessFetchQueueAsync()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TileScope scope;
|
||||
|
||||
lock (_fetchLock)
|
||||
{
|
||||
if (_pendingScopes.Count == 0)
|
||||
{
|
||||
_fetchWorker = null;
|
||||
return;
|
||||
}
|
||||
|
||||
scope = _pendingScopes.First();
|
||||
_pendingScopes.Remove(scope);
|
||||
}
|
||||
|
||||
await FetchScopeAsync(scope).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FetchScopeAsync(TileScope scope)
|
||||
{
|
||||
if (!_isEnabled)
|
||||
return;
|
||||
|
||||
var communication = cFasdCockpitCommunicationBase.Instance;
|
||||
if (communication == null)
|
||||
{
|
||||
ScheduleFetchRetry(scope);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_isDemo = communication.IsDemo();
|
||||
|
||||
var rawCounts = await communication.GetTicketOverviewCounts(OverviewKeys, scope == TileScope.Role).ConfigureAwait(false);
|
||||
var counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (rawCounts != null)
|
||||
{
|
||||
foreach (var kvp in rawCounts)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(kvp.Key))
|
||||
continue;
|
||||
|
||||
counts[kvp.Key] = Math.Max(0, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (_isDemo)
|
||||
{
|
||||
foreach (var key in OverviewKeys)
|
||||
{
|
||||
var extras = GetDemoRelationCount(key, scope == TileScope.Role);
|
||||
if (counts.ContainsKey(key))
|
||||
counts[key] += extras;
|
||||
else
|
||||
counts[key] = extras;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isEnabled)
|
||||
return;
|
||||
|
||||
await _dispatcher.InvokeAsync(() => ProcessScopeCounts(scope, counts));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[TicketOverview] Fetch {scope} failed: {ex}");
|
||||
ScheduleFetchRetry(scope);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshTimerIntervals()
|
||||
{
|
||||
_dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (_personalTimer != null)
|
||||
_personalTimer.Interval = GetPollingInterval(TileScope.Personal);
|
||||
|
||||
if (_roleTimer != null)
|
||||
_roleTimer.Interval = GetPollingInterval(TileScope.Role);
|
||||
});
|
||||
}
|
||||
|
||||
private void ProcessScopeCounts(TileScope scope, IDictionary<string, int> newCounts)
|
||||
{
|
||||
if (newCounts == null)
|
||||
return;
|
||||
|
||||
var hasInitializedScope = _initializedScopes.Contains(scope);
|
||||
var changes = new List<TileCountChange>();
|
||||
|
||||
foreach (var key in OverviewKeys)
|
||||
{
|
||||
var previous = _currentCounts.TryGetValue(key, out var counts) ? counts : TileCounts.Empty;
|
||||
var incoming = newCounts.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;
|
||||
}
|
||||
|
||||
_currentCounts[key] = updated;
|
||||
|
||||
if (hasInitializedScope && oldValue != incoming)
|
||||
{
|
||||
changes.Add(new TileCountChange(key, scope, oldValue, incoming));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInitializedScope)
|
||||
{
|
||||
_initializedScopes.Add(scope);
|
||||
var initArgs = new TicketOverviewCountsChangedEventArgs(
|
||||
Array.Empty<TileCountChange>(),
|
||||
new Dictionary<string, TileCounts>(_currentCounts, StringComparer.OrdinalIgnoreCase),
|
||||
scope);
|
||||
OverviewCountsChanged?.Invoke(this, initArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (changes.Count == 0)
|
||||
return;
|
||||
|
||||
var args = new TicketOverviewCountsChangedEventArgs(changes, new Dictionary<string, TileCounts>(_currentCounts, StringComparer.OrdinalIgnoreCase));
|
||||
OverviewCountsChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
public void SimulateDemoTicket()
|
||||
{
|
||||
@@ -318,9 +524,9 @@ namespace FasdDesktopUi.Basics.Services
|
||||
if (appliedChanges.Count == 0)
|
||||
return;
|
||||
|
||||
var args = new TicketOverviewCountsChangedEventArgs(appliedChanges, new Dictionary<string, TileCounts>(_currentCounts));
|
||||
OverviewCountsChanged?.Invoke(this, args);
|
||||
}
|
||||
var args = new TicketOverviewCountsChangedEventArgs(appliedChanges, new Dictionary<string, TileCounts>(_currentCounts));
|
||||
OverviewCountsChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
private void AddRelationForRecord(DemoTicketRecord record)
|
||||
{
|
||||
@@ -577,24 +783,43 @@ namespace FasdDesktopUi.Basics.Services
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void ScheduleFetchRetry()
|
||||
{
|
||||
if (_fetchRetryPending)
|
||||
return;
|
||||
|
||||
_fetchRetryPending = true;
|
||||
_ = _dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(250).ConfigureAwait(false);
|
||||
await FetchAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_fetchRetryPending = false;
|
||||
}
|
||||
}, DispatcherPriority.Background);
|
||||
}
|
||||
private void ScheduleFetchRetry(TileScope scope)
|
||||
{
|
||||
if (!_isEnabled)
|
||||
return;
|
||||
|
||||
lock (_retryScopes)
|
||||
{
|
||||
_retryScopes.Add(scope);
|
||||
if (_retryScheduled)
|
||||
return;
|
||||
|
||||
_retryScheduled = true;
|
||||
}
|
||||
|
||||
_ = _dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
|
||||
TileScope[] scopes;
|
||||
lock (_retryScopes)
|
||||
{
|
||||
scopes = _retryScopes.ToArray();
|
||||
_retryScopes.Clear();
|
||||
_retryScheduled = false;
|
||||
}
|
||||
|
||||
foreach (var pendingScope in scopes)
|
||||
{
|
||||
await FetchAsync(pendingScope).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[TicketOverview] Retry scheduling failed: {ex}");
|
||||
}
|
||||
}, DispatcherPriority.Background);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user