feat: expand cockpit integrations and support workflows

Add Phoenix remote desktop, action connector, documentation engine, and gamification support. Refactor support-case processing and search/action UI, and update installer assets and dependencies.
This commit is contained in:
Meik
2026-07-13 11:16:53 +02:00
parent bbedbf71c8
commit 0b52999b1d
303 changed files with 11235 additions and 3580 deletions

View File

@@ -0,0 +1,29 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.UiActions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
{
internal interface ISupportCaseProcessor
{
cSupportCaseDataProvider SupportCaseDataProviderArtifact { get; }
event EventHandler<RelationEventArgs> AvailableCaseRelationsAdded;
event EventHandler<SupportCaseDataEventArgs> CaseDataChanged;
void SetSupportCase(ISupportCase supportCase);
CockpitValueDisplayData GetCockpitValueDisplayData(cHealthCardStateBase displayValueDefinition, cF4sdApiSearchResultRelation relation, bool getStatic);
Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad);
void ProcessComputations(cF4sdApiSearchResultRelation relation, IEnumerable<cHealthCardComputationBase> computations);
Task UpdateLatestCaseDataFor(cF4sdApiSearchResultRelation relation);
ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations();
cHealthCard GetHealthcardFor(cF4sdApiSearchResultRelation relation);
cUiActionBase GetUiAction(cHealthCardStateBase stateDefinition);
bool TryGetNamedParameterValue<T>(cF4sdApiSearchResultRelation relation, string parameterName, out T value);
}
}

View File

@@ -0,0 +1,145 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
{
internal class SupportCaseDetailsValueProcessesor
{
/// <summary>
/// Transform a value to a <seealso cref="cF4SDHealthCardRawData.cHealthCardDetailsTable"/>
/// </summary>
/// <param name="rawValue">Raw value in form of CSV or JSON</param>
/// <param name="stateDetailsValued">Details definition the transformation is based on</param>
/// <returns></returns>
internal static cF4SDHealthCardRawData.cHealthCardDetailsTable GetDetailsTable(object rawValue, cHealthCardDetailsValued stateDetailsValued)
{
try
{
if (rawValue is null)
return null;
var stringValue = cUtility.RawValueFormatter.GetDisplayValue(rawValue, RawValueType.STRING);
if (stringValue is null)
return null;
List<object[]> tableValues = stateDetailsValued.Format == cHealthCardDetailsValued.ValuedFormat.json
? ParseJson(stringValue, stateDetailsValued)
: ParseCsv(stringValue, stateDetailsValued);
tableValues = tableValues ?? new List<object[]>();
var detailedValueTable = new cF4SDHealthCardRawData.cHealthCardDetailsTable()
{
Name = "Details-" + stateDetailsValued.ParentState.Name,
Columns = stateDetailsValued.Select(v => v.Names.GetValue()).ToList(),
Values = new Dictionary<int, List<object[]>>() { { 0, tableValues } }
};
return detailedValueTable;
}
catch (Exception ex)
{
LogException(ex);
}
return null;
}
private static List<object[]> ParseJson(string text, cHealthCardDetailsValued details)
{
try
{
var jObjects = JsonConvert.DeserializeObject<List<JObject>>(text);
if (jObjects == null || jObjects.Count == 0)
return new List<object[]>();
var values = new List<object[]>();
foreach (JObject jObject in jObjects)
{
var valueRow = new object[details.Count];
for (int i = 0; i < details.Count; i++)
valueRow[i] = null;
foreach (var jProp in jObject.Properties())
{
var name = jProp.Name;
var index = details.FindIndex(v => v.Column.Equals(name, StringComparison.InvariantCultureIgnoreCase));
if (index < 0)
continue;
var column = details[index];
if (jProp.Value is JValue jValue)
{
var value = jValue.Value;
valueRow[index] = cUtility.RawValueFormatter.GetDisplayValue(value, column.DisplayType);
}
}
values.Add(valueRow);
}
return values;
}
catch (Exception)
{
return new List<object[]>();
}
}
private static List<object[]> ParseCsv(string text, cHealthCardDetailsValued details)
{
var values = new List<object[]>();
var rows = text.Split(details.RowSeparator);
foreach (var row in rows)
{
if (string.IsNullOrWhiteSpace(row))
continue;
var entry = new List<object>();
if (details.ColSeparator == null)
{
entry.Add(row.Trim());
}
else
{
var columns = row.Split((char)details.ColSeparator);
foreach (var column in columns)
entry.Add(column?.Trim());
}
while (entry.Count < details.Count)
entry.Add(null);
values.Add(entry.ToArray());
}
return values;
}
/// <summary>
/// Get a display string based on StateDetails and its values
/// </summary>
internal static string GetDetailStringValue(cHealthCardDetailsBase details, cF4SDHealthCardRawData.cHealthCardDetailsTable detailTableValueTable)
{
if (detailTableValueTable?.Values is null || detailTableValueTable.Values.Count == 0)
return null;
var tableValues = detailTableValueTable.Values.First().Value;
if (detailTableValueTable.Columns.Count >= 1 && tableValues.Count == 1)
return cUtility.RawValueFormatter.GetDisplayValue(detailTableValueTable.Values.First().Value.First()?.First(), details.First().DisplayType);
else
return cUtility.RawValueFormatter.GetDisplayValue(detailTableValueTable.Values.First().Value.Count, RawValueType.STRING);
}
}
}

View File

@@ -0,0 +1,643 @@
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using FasdDesktopUi.Basics.UiActions;
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.Processors
{
/// <summary>
/// Used for processing raw data of a <see cref="ISupportCase"/> for the UI in a certain way.
/// </summary>
internal class SupportCaseProcessor : ISupportCaseProcessor
{
internal const string AgentDeviceIdNamedParameterName = "F4SD_Agent_DeviceId";
internal const string AgentUserIdNamedParameterName = "F4SD_Agent_UserId";
internal const string AgentOrganisationCodeNamedParameterName = "F4SD_Agent_OrganisationId";
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>>();
private readonly Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, IList<object>>> _computationCache = new Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, IList<object>>>();
public 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;
}
await EnsureDetailsPageDataCachedAsync(e.Relation).ConfigureAwait(false);
UpdateNamedParameters(e.Relation, e.DataTables);
CaseDataChanged?.Invoke(this, e);
}
private async Task EnsureDetailsPageDataCachedAsync(cF4sdApiSearchResultRelation relation)
{
if (!_detailsPageDataCache.TryGetValue(relation, out var cachedData))
{
_detailsPageDataCache[relation] = await _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataAsync().ConfigureAwait(false);
return;
}
cDetailsPageData detailData = _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataWithoutHeading();
cachedData.WidgetData = detailData.WidgetData;
cachedData.DataHistoryList = detailData.DataHistoryList;
cachedData.MenuBarData = detailData.MenuBarData;
cachedData.DataContainerCollectionList = detailData.DataContainerCollectionList;
}
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;
}
AddDefaultNamedParameters();
}
catch (Exception ex)
{
LogException(ex);
}
void AddDefaultNamedParameters()
{
var table = dataTables.FirstOrDefault(t => t.Name == "agnt-computer");
if (table != null)
{
if (table.Columns.TryGetValue("id", out var agentDeviceIdColumn))
_namedParameterCache[relation][AgentDeviceIdNamedParameterName] = agentDeviceIdColumn.Values.FirstOrDefault();
}
table = dataTables.FirstOrDefault(t => t.Name == "agnt-user");
if (table != null)
{
if (table.Columns.TryGetValue("id", out var agentUserColumn))
_namedParameterCache[relation][AgentUserIdNamedParameterName] = agentUserColumn.Values.FirstOrDefault();
}
_namedParameterCache[relation][AgentOrganisationCodeNamedParameterName] = cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode;
}
}
public async Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad)
{
_ = Task.Run(async () => await _supportCase.LoadSupportCaseDataAsync(relation, tablesToLoad.Where(t => !t.Contains("-details-"))));
await EnsureDetailsPageDataCachedAsync(relation).ConfigureAwait(false);
_supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.UpdateNamedParameterEntries();
CaseDataChanged?.Invoke(this, new SupportCaseDataEventArgs());
}
public void ProcessComputations(cF4sdApiSearchResultRelation relation, IEnumerable<cHealthCardComputationBase> computations)
{
if (!_computationCache.ContainsKey(relation))
_computationCache.Add(relation, new Dictionary<string, IList<object>>());
var cachedComputations = _computationCache[relation];
var computationTables = new List<cF4SDHealthCardRawData.cHealthCardTable>();
foreach (cHealthCardComputationBase computation in computations)
{
AddComputationTable(computation);
AddComputationTableStatic(computation);
}
CaseDataChanged?.Invoke(this, new SupportCaseDataEventArgs() { Relation = relation, DataTables = computationTables });
void AddComputationTable(cHealthCardComputationBase computation)
{
var computationTable = new cF4SDHealthCardRawData.cHealthCardTable() { Name = $"Computation_{computation.Name}", AlternateStaticTable = $"Computation_{computation.Name}_latest" };
var computationColumn = new cF4SDHealthCardRawData.cHealthCardTableColumn(computationTable);
for (int i = 0; i < cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge; i++)
{
object[] valuesRequiredToCompute = computation.Values
.Where(v => v.ValueTable != null && v.ValueColumn != null)
.Select(v => _supportCase.GetSupportCaseHealthcardData(relation, v, false)?.ElementAtOrDefault(i))
.ToArray();
object computedValue = computation.Compute(valuesRequiredToCompute);
computationColumn.Values.Add(computedValue);
}
computationTable.Columns = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTableColumn>() { ["default"] = computationColumn };
computationTables.Add(computationTable);
cachedComputations[computation.Name] = computationColumn.Values;
}
void AddComputationTableStatic(cHealthCardComputationBase computation)
{
var computationTableStatic = new cF4SDHealthCardRawData.cHealthCardTable() { Name = $"Computation_{computation.Name}_latest" };
var computationColumnStatic = new cF4SDHealthCardRawData.cHealthCardTableColumn(computationTableStatic);
object[] staticValuesRequiredToCompute = computation.Values
.Where(v => v.ValueTable != null && v.ValueColumn != null)
.Select(v => _supportCase.GetSupportCaseHealthcardData(relation, v, true)?.ElementAtOrDefault(0))
.ToArray();
object computedStaticValue = computation.Compute(staticValuesRequiredToCompute);
computationColumnStatic.Values.Add(computedStaticValue);
computationTableStatic.Columns = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTableColumn>() { ["default"] = computationColumnStatic };
computationTables.Add(computationTableStatic);
cachedComputations[$"{computation.Name}_latest"] = computationColumnStatic.Values;
}
}
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.Where(t => !t.Name.StartsWith("Computation_")).Select(t => t.Name));
}
catch (Exception ex)
{
LogException(ex);
}
}
private async Task<enumActualizeStatus> ActualizeDataAsync(int? agentUserId, int? agentDeviceId)
{
var status = enumActualizeStatus.unknown;
if (!agentUserId.HasValue && !agentDeviceId.HasValue)
return status;
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;
}
public ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations()
=> _supportCase.GetCaseRelations();
public 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 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 CockpitValueDisplayData GetCockpitValueDisplayData(cHealthCardStateBase displayValueDefinition, cF4sdApiSearchResultRelation relation, bool getStatic)
{
IList<object> rawValues = GetRawValues(displayValueDefinition, relation, getStatic);
cHealthCardStateBase definitionForLevel = GetDefinitionForLevel(displayValueDefinition);
IList<enumHealthCardStateLevel> predefinedLevel = GetPredefinedLevels(displayValueDefinition, relation, getStatic);
cF4SDHealthCardRawData.cHealthCardDetailsTable valueDetailsTable = null;
if (displayValueDefinition?.Details is cHealthCardDetailsValued detailsValued)
valueDetailsTable = SupportCaseDetailsValueProcessesor.GetDetailsTable(rawValues?.FirstOrDefault(), detailsValued);
return new CockpitValueDisplayData()
{
IsLoading = false,
Title = displayValueDefinition?.Names.GetValue(),
Values = rawValues?.Select(raw => GetDisplayValue(raw, displayValueDefinition, valueDetailsTable)).ToList(),
Levels = predefinedLevel ?? rawValues?.Select(raw => GetLevel(raw, definitionForLevel, 0)).ToList(),
UiActions = GetValueUiActions(displayValueDefinition, valueDetailsTable)
};
}
private IList<object> GetRawValues(cHealthCardStateBase stateDefinition, cF4sdApiSearchResultRelation relation, bool getStatic)
{
const string computationPrefix = "Computation_";
if (stateDefinition?.DatabaseInfo?.ValueTable?.StartsWith(computationPrefix) ?? false)
if (_computationCache.TryGetValue(relation, out var cachedComputationValues))
if (cachedComputationValues.TryGetValue(stateDefinition.DatabaseInfo.ValueTable.Substring(computationPrefix.Length) + (getStatic ? "_latest" : string.Empty), out var computedValues))
return computedValues;
return _supportCase.GetSupportCaseHealthcardData(relation, stateDefinition.DatabaseInfo, getStatic);
}
private IList<cUiActionBase> GetValueUiActions(cHealthCardStateBase stateDefinition, cF4SDHealthCardRawData.cHealthCardDetailsTable detailedValueTable)
{
if (stateDefinition.Details is null)
return null;
List<cUiActionBase> uiActions = new List<cUiActionBase>(cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge);
for (int i = 0; i < cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge; i++)
{
uiActions.Add(new cShowDetailedDataAction(stateDefinition, i, detailedValueTable) { DisplayType = enumActionDisplayType.enabled });
}
return uiActions;
}
private IList<enumHealthCardStateLevel> GetHighestLevels(List<IList<enumHealthCardStateLevel>> allLevels, bool isNotTransparent)
{
var highestLevels = new List<enumHealthCardStateLevel>();
int maxLevelCount = allLevels?.Where(l => l != null).Select(l => l.Count).DefaultIfEmpty(0).Max() ?? 0;
for (int i = 0; i < maxLevelCount; i++)
{
enumHealthCardStateLevel highestLevel = enumHealthCardStateLevel.None;
foreach (var levels in allLevels)
{
if (levels == null || levels.Count <= i)
continue;
highestLevel = (enumHealthCardStateLevel)Math.Max((int)highestLevel, (int)levels[i]);
if (highestLevel == enumHealthCardStateLevel.Error)
break;
}
if (highestLevel <= enumHealthCardStateLevel.Ok)
highestLevel = isNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
highestLevels.Add(highestLevel);
}
return highestLevels;
}
private string GetDisplayValue(object rawValue, cHealthCardStateBase displayValueDefinition, cF4SDHealthCardRawData.cHealthCardDetailsTable detailedValueTable)
{
if (displayValueDefinition?.Details != null && detailedValueTable != null)
return SupportCaseDetailsValueProcessesor.GetDetailStringValue(displayValueDefinition.Details, detailedValueTable);
else if (displayValueDefinition is cHealthCardStateTranslation translationDefinition)
return GetTranslationValue(rawValue, translationDefinition);
else if (displayValueDefinition is cHealthCardStateAggregation)
return "Ø";
else
return cUtility.RawValueFormatter.GetDisplayValue(rawValue, displayValueDefinition.DisplayType);
}
private string GetTranslationValue(object rawValue, cHealthCardStateTranslation translationDefinition)
{
ITranslatorObject abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(translationDefinition, translationDefinition.Translation);
if (!(abstractTranslation is cHealthCardTranslator translation))
return null;
if (rawValue is null)
return translation.DefaultTranslation?.Translation?.GetValue();
string defaultValue = translation.DefaultTranslation?.Translation?.GetValue() ?? rawValue.ToString();
foreach (var translationEntry in translation.Translations)
{
if (translationEntry.Values.Any(v => string.Equals(rawValue.ToString(), v, StringComparison.InvariantCultureIgnoreCase)))
return translationEntry.Translation.GetValue();
}
return defaultValue;
}
private static enumHealthCardStateLevel GetLevel(object rawValue, cHealthCardStateBase stateDefinition, int referenceDays)
{
if (stateDefinition is null || rawValue is null)
return enumHealthCardStateLevel.None;
if (stateDefinition is cHealthCardStateAggregation)
throw new NotImplementedException();
try
{
if (stateDefinition is cHealthCardStateLevel levelDefinition)
{
var valueDouble = cF4SDHealthCardRawData.GetDouble(rawValue);
if (valueDouble != null)
{
if (levelDefinition.IsDirectionUp)
return valueDouble >= levelDefinition.Error ? enumHealthCardStateLevel.Error : valueDouble >= levelDefinition.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
else
return valueDouble <= levelDefinition.Error ? enumHealthCardStateLevel.Error : valueDouble <= levelDefinition.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
}
}
else if (stateDefinition is cHealthCardStateVersion stateVersion)
{
Version valueVersion = cF4SDHealthCardRawData.GetVersion(rawValue);
if (valueVersion is null)
return enumHealthCardStateLevel.None;
if (stateVersion.IsDirectionUp)
return valueVersion >= stateVersion.Error ? enumHealthCardStateLevel.Error : valueVersion >= stateVersion.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
else
return valueVersion <= stateVersion.Error ? enumHealthCardStateLevel.Error : valueVersion <= stateVersion.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
}
else if (stateDefinition is cHealthCardStateDateTime stateDateTime)
{
DateTime? valueDateTime = cF4SDHealthCardRawData.GetDateTime(rawValue);
if (valueDateTime is null)
return enumHealthCardStateLevel.None;
DateTime tempDateTime = valueDateTime.Value;
double differenceHours = Math.Floor((DateTime.UtcNow.AddDays(-referenceDays) - tempDateTime).TotalHours);
if (stateDateTime.IsDirectionUp)
return differenceHours >= stateDateTime.ErrorHours ? enumHealthCardStateLevel.Error : differenceHours >= stateDateTime.WarningHours ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
else
return differenceHours <= stateDateTime.ErrorHours ? enumHealthCardStateLevel.Error : differenceHours <= stateDateTime.WarningHours ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
}
else if (stateDefinition is cHealthCardStateInfo)
{
return stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Info : enumHealthCardStateLevel.None;
}
else if (stateDefinition is cHealthCardStateTranslation stateTranslation)
{
ITranslatorObject abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(stateTranslation, stateTranslation.Translation);
if (!(abstractTranslation is cHealthCardTranslator translation))
return enumHealthCardStateLevel.None;
enumHealthCardStateLevel translationStateLevel = translation.DefaultTranslation?.StateLevel ?? enumHealthCardStateLevel.Info;
foreach (var translationEntry in translation.Translations)
{
if (translationEntry.Values.Any(v => string.Equals(rawValue.ToString(), v, StringComparison.InvariantCultureIgnoreCase)))
translationStateLevel = translationEntry.StateLevel;
}
if (!stateTranslation.IsNotTransparent && (translationStateLevel == enumHealthCardStateLevel.Ok || translationStateLevel == enumHealthCardStateLevel.Info))
return enumHealthCardStateLevel.None;
else
return translationStateLevel;
}
else if (stateDefinition is cHealthCardStateRefLink)
{
throw new NotImplementedException();
}
return enumHealthCardStateLevel.None;
}
catch (Exception e)
{
LogException(e);
return enumHealthCardStateLevel.None;
}
}
public cUiActionBase GetUiAction(cHealthCardStateBase stateDefinition)
{
if (stateDefinition?.QuickActions is null || stateDefinition.QuickActions.Count == 0)
return null;
return stateDefinition.QuickActions.Count == 1
? GetSingleUiAction(stateDefinition)
: GetMultipleUiActions(stateDefinition);
}
private cHealthCardStateBase GetDefinitionForLevel(cHealthCardStateBase displayValueDefinition)
{
if (displayValueDefinition is cHealthCardStateRefLink stateRefLink)
return cF4SDHealthCardConfig.GetReferencableStateWithName(stateRefLink, stateRefLink.Reference);
return displayValueDefinition;
}
private IList<enumHealthCardStateLevel> GetPredefinedLevels(cHealthCardStateBase displayValueDefinition, cF4sdApiSearchResultRelation relation, bool getStatic)
{
if (displayValueDefinition is cHealthCardStateRefLink stateRefLink)
{
cHealthCardStateBase definition = cF4SDHealthCardConfig.GetReferencableStateWithName(stateRefLink, stateRefLink.Reference);
CockpitValueDisplayData displayData = GetCockpitValueDisplayData(definition, relation, getStatic);
return GetHighestLevels(new List<IList<enumHealthCardStateLevel>> { displayData.Levels }, stateRefLink.IsNotTransparent);
}
if (displayValueDefinition is cHealthCardStateAggregation stateAggregation)
{
List<IList<enumHealthCardStateLevel>> allLevels = stateAggregation.States.Select(s => GetCockpitValueDisplayData(s, relation, getStatic).Levels).ToList();
return GetHighestLevels(allLevels, stateAggregation.IsNotTransparent);
}
return null;
}
private cUiActionBase GetSingleUiAction(cHealthCardStateBase stateDefinition)
{
string quickActionName = stateDefinition.QuickActions?.FirstOrDefault();
if (string.IsNullOrWhiteSpace(quickActionName))
return null;
cUiActionBase uiAction = MenuDataFactory.GetByName(quickActionName, SupportCaseDataProviderArtifact.NamedParameterEntries, SupportCaseDataProviderArtifact.CaseRelations.Select(r => r.Key))?.UiAction;
if (uiAction is cUiQuickAction uiQuickAction)
uiQuickAction.QuickActionRecommendation = new cRecommendationDataModel { Category = quickActionName, Recommendation = stateDefinition.Descriptions.GetValue() };
return uiAction;
}
private cUiActionBase GetMultipleUiActions(cHealthCardStateBase stateDefinition)
{
List<cMenuDataBase> menuDatas = stateDefinition.QuickActions
.Select(quickActionName => MenuDataFactory.GetByName(quickActionName, SupportCaseDataProviderArtifact.NamedParameterEntries, SupportCaseDataProviderArtifact.CaseRelations.Select(r => r.Key)))
.Where(menuData => menuData?.UiAction != null)
.Select(menuData =>
{
if (menuData.UiAction is cUiQuickAction uiQuickAction)
uiQuickAction.QuickActionRecommendation = new cRecommendationDataModel
{
Category = stateDefinition.Names.GetValue(),
Recommendation = stateDefinition.Descriptions.GetValue()
};
return menuData;
})
.ToList();
if (menuDatas == null || menuDatas.Count == 0)
return null;
if (menuDatas.Count == 1)
return menuDatas[0].UiAction;
return new cSubMenuAction(false)
{
SubMenuData = menuDatas,
Name = stateDefinition.Names.GetValue(),
Description = stateDefinition.Descriptions.GetValue(),
DisplayType = enumActionDisplayType.enabled
};
}
public bool TryGetNamedParameterValue<T>(cF4sdApiSearchResultRelation relation, string parameterName, out T value)
{
value = default;
if (!_namedParameterCache.TryGetValue(relation, out var namedParameters))
return false;
if (!namedParameters.TryGetValue(parameterName, out var namedParameter))
return false;
try
{
value = (T)Convert.ChangeType(namedParameter, typeof(T));
}
catch
{
LogEntry($"Found named parameter, but can not be converted to type: {typeof(T)}. Value: {namedParameter} Actual type: {namedParameter.GetType()}", LogLevels.Info);
return false;
}
return true;
}
/// <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;
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
{
internal class SupportCaseProcessorFactory
{
private readonly static Dictionary<Guid, ISupportCaseProcessor> _supportCaseProccesors = new Dictionary<Guid, ISupportCaseProcessor>();
internal static ISupportCaseProcessor Get(Guid id)
{
if (!_supportCaseProccesors.ContainsKey(id))
_supportCaseProccesors.Add(id, new SupportCaseProcessor());
return _supportCaseProccesors[id];
}
}
}