using System; using System.Collections.Generic; using System.Data.Common; using System.Data.SqlTypes; using System.Dynamic; using System.Linq; using System.Reflection; using System.Security.Principal; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using C4IT.FASD.Base; using C4IT.FASD.Communication.Citrix; using C4IT.HTTP; using C4IT.Logging; using C4IT_DataHistoryProvider_Base.DataSources; using Newtonsoft.Json.Linq; using static C4IT.Logging.cLogManager; using static C4IT_DataHistoryProvider.cDataHistoryConfigCitrix; namespace C4IT.DataHistoryProvider { public class cDataHistoryCollectorCitrix : cDataHistoryCollectorModule, ISearchResultRelationProvider { public const string constConnectorName = "Citrix connector"; public const string constLicenseId = "F2DA5E05-FD3C-4182-9164-9C722140BF85"; public const string constCitrixUserList = "monitorodata/Users?$top={0}&$select=Id,Sid,Upn,Domain&$filter=Domain ne ''"; public const string constCitrixSessionList = "monitorodata/Sessions?$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; public const string constCitrixSessions = "monitorodata/Sessions?$filter=UserId in ({0})&$expand=Machine($select=Id,DnsName,Name)&$select=SessionKey,StartDate,EndDate,ConnectionState"; public const string constCitrixSession = "monitorodata/Sessions?$filter=SessionKey eq {0}"; public const string constCitrixSessionMetrics = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics"; public const string constCitrixSessionMetricsIcaRTT = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaRttMs)"; public const string constCitrixSessionMetricsIcaLatency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,IcaLatency)"; public const string constCitrixSessionMetricsClientL7Latency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ClientL7Latency)"; public const string constCitrixSessionMetricsServerL7Latency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,ServerL7Latency)"; public const string constCitrixSessionMetricsInputBandwidthUsed = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputBandwidthUsed)"; public const string constCitrixSessionMetricsOutputBandwidthUsed = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthUsed)"; public const string constCitrixSessionMetricsOutputBandwidthAvailable = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputBandwidthAvailable)"; public const string constCitrixSessionMetricsFps = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,Fps)"; public const string constCitrixSessionMetricsInputFps = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,InputFps)"; public const string constCitrixSessionMetricsOutputFps = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,OutputFps)"; public const string constCitrixSessionMetricsWanLatency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,WanLatency)"; public const string constCitrixSessionMetricsDcLatency = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=SessionMetrics($select=Id,CollectedDate,DcLatency)"; public const string constCitrixSessionMachine = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Machine($expand=CurrentLoadIndex,Catalog,DesktopGroup($select=Name)),Machine"; public const string constCitrixSessionCurrentConnection = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=CurrentConnection,LogOnMetrics"; public const string constCitrixSessionConnections = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=Connection"; public const string constCitrixUserConnections = "monitorodata/Sessions?$filter=UserId in ({0})&$expand=Connection"; public const string constCitrixSessionUser = "monitorodata/Sessions?$select=SessionKey&$filter=SessionKey eq {0}&$expand=User"; public const string constCitrixSessionLogoff = "cvad/manage/Sessions/{0}/$logoff"; public const string constCitrixSessionHide = "cvad/manage/Sessions/{0}/$hide"; public const string constCitrixSessionSendMessage = "cvad/manage/Sessions/{0}/$sendMessage"; public const string constTableNameSession = "citrix-session"; public const string constTableNameSessionMachine = "citrix-session-machine"; public const string constTableNameSessionConnection = "citrix-session-connection"; public const string constTableNameSessionCurrentConnection = "citrix-session-currentConnection"; public const string constTableNameSessionMetrics = "citrix-session-metrics"; public const string constTableNameSessionUser = "citrix-session-user"; public const string constTableNameSessionDetailsIcaRttMS = "citrix-session-details-icaRttMS"; public const string constTableNameSessionDetailsIcaLatency = "citrix-session-details-icaLatency"; public const string constTableNameSessionDetailsClientL7Latency = "citrix-session-details-clientL7Latency"; public const string constTableNameSessionDetailsServerL7Latency = "citrix-session-details-serverL7Latency"; public const string constTableNameSessionDetailsInputBandwidthUsed = "citrix-session-details-inputBandwidthUsed"; public const string constTableNameSessionDetailsOutputBandwidthUsed = "citrix-session-details-outputBandwidthUsed"; public const string constTableNameSessionDetailsOutputBandwidthAvailable = "citrix-session-details-outputBandwidthAvailable"; public const string constTableNameSessionDetailsFps = "citrix-session-details-fps"; public const string constTableNameSessionDetailsInputFps = "citrix-session-details-inputFps"; public const string constTableNameSessionDetailsOutputFps = "citrix-session-details-outputFps"; public const string constTableNameSessionDetailsWanLatency = "citrix-session-details-wanLatency"; public const string constTableNameSessionDetailsDcLatency = "citrix-session-details-dcLatency"; public const int constCitrixUserPaging = 100; public readonly List UserMainProperties = new List() { new cLdapPropertyInfo() { Name="objectSid", LdapType= enumLdapPropertyType.ldapSid, DbName=null}, new cLdapPropertyInfo() { Name="userPrincipalName", LdapType= enumLdapPropertyType.ldapString, DbName="upn_external"}, }; private Dictionary tenantCitrixCache = new Dictionary(); private readonly cDataHistoryCollector _collector; public cDataHistoryCollectorCitrix(cDataHistoryCollector Collector) : base(Collector, enumDataHistoryOrigin.Citrix, constConnectorName, constLicenseId) { _collector = Collector; } public async Task GetCitrixCommunicationForTenantAsync(string tenant, bool force = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (tenant == null) return null; cDataHistoryCitrixTenant _CitrixTenant = null; var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; if (tenants is null) { return null; } foreach (var Tenant in tenants) { if (Tenant.TenantID == tenant) { _CitrixTenant = Tenant; break; } } if (_CitrixTenant != null) { if (_CitrixTenant != null && tenantCitrixCache.TryGetValue(_CitrixTenant.TenantID, out var cachedGraph)) return cachedGraph; var logon = await LogonCitrixTenantAsync(_CitrixTenant, force: force); if (logon != null) tenantCitrixCache[_CitrixTenant.TenantID] = logon; return logon; } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public async Task LogonCitrixTenantAsync(cDataHistoryCitrixTenant Tenant, bool force = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var CitrixConn = new cCitrixCommunication(); if (Tenant == null) { return null; } var LI = new cOAuthLogonInfo() { Tenant = Tenant?.TenantID.ToString(), ClientID = Tenant.Credential?.User, ClientSecret = Tenant.Credential?.NwCredential.Password, InstanceId = Tenant?.InstanceID.ToString() }; var RetVal = await CitrixConn.LogonAsync(LI); if (RetVal) return CitrixConn; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public async Task GetCitrixUsersAsync() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var strUrl = string.Format(constCitrixUserList, constCitrixUserPaging); var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; if (tenants is null) { Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); return null; } foreach (var Tenant in tenants) { var CitrixConn = await GetCitrixCommunicationForTenantAsync(Tenant.TenantID, true); if (CitrixConn == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } var Result = await CitrixConn.RequestAsync(strUrl, retryForbidden: false); if (Result != null) { var _ty = Result.GetType(); var _props = Result.Result as JObject; if (_props != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{_props}' with {_props.Count} properties could be read."); } } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public async Task> GetUserSessionsAsync(List UserId, CancellationToken Token, cF4sdWebRequestInfo requestInfo) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { string userIds = string.Join(",", UserId.Where(u => !string.IsNullOrWhiteSpace(u?.citrixUserId.ToString())).Select(u => u.citrixUserId)); var strUrl = string.Format(constCitrixSessions, userIds); var tenantId = UserId?.FirstOrDefault(u => !string.IsNullOrWhiteSpace(u?.citrixTenantId))?.citrixTenantId; if (tenantId is null) { Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); return null; } var CitrixConn = await GetCitrixCommunicationForTenantAsync(tenantId, true); if (CitrixConn == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {tenantId}", LogLevels.Error); return null; } // maybe we should think about a concrete implementation of the results instead of a dynamic, what do you think? var Result = await CitrixConn.RequestListAsync(strUrl, retryForbidden: false); if (Result != null == Result.Count > 0) { var sessionResult = new List(Result.Count); double _sumDuration = 0; foreach (var Entry in Result) { var endDate = DateTime.MinValue; if (!DateTime.TryParse(Entry.Result.EndDate?.ToString(), out endDate)) endDate = DateTime.UtcNow; var startDate = DateTime.MinValue; if (!DateTime.TryParse(Entry.Result.StartDate?.ToString(), out startDate)) startDate = DateTime.UtcNow; TimeSpan _duration = endDate - startDate; _sumDuration += _duration.TotalSeconds; Enum.TryParse(Entry.Result.ConnectionState.ToString(), out enumCitrixSessionStatus connectionState); var Infos = new Dictionary() { { "Status",connectionState.ToString() }, { "StatusId", Entry.Result.ConnectionState.ToString() }, { "Domain", Regex.Replace(Entry.Result.Machine.Name?.ToString(), @"\\.*$", "") } }; var sessionInfo = new cF4sdApiSearchResultRelation() { Type = enumF4sdSearchResultClass.VirtualSession, Name = Regex.Replace(Entry.Result.Machine.Name.ToString(), @"^.*\\", ""), DisplayName = Regex.Replace(Entry.Result.Machine.Name.ToString(), @"^.*\\", ""), LastUsed = endDate, // minor thing due to the fact we did it like that in the Agent before, but: // Instead of setting the UsingLevel to a temporary value and calculate the actual value later, // we should maybe calculate the sumvalue beforehand and apply only the calculation here, what do you think? UsingLevel = _duration.TotalSeconds == 0 ? 1 : _duration.TotalSeconds, id = Entry.Result.SessionKey, Status = enumF4sdSearchResultStatus.Active, Infos = Infos, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = UserId[0].Id}, new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.VirtualSession, Id = Entry.Result.SessionKey} } }; sessionResult.Add(sessionInfo); } if (sessionResult != null && sessionResult.Count > 0) { foreach (var Entry in sessionResult) { Entry.UsingLevel /= _sumDuration; } } return sessionResult; } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo,int MaxAge, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionMetrics, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetrics, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameSessionMetrics, InformationClass = enumFasdInformationClass.VirtualSession, Origin = enumDataHistoryOrigin.Citrix, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new List>(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { foreach (var prop in metricsObj.OfType()) { try { var metricDict = new Dictionary(); foreach (var p in prop.Properties()) { if (p.Value is JValue v) metricDict[p.Name] = v.Value ?? null; else metricDict[p.Name] = p.Value; } dicVals.Add(metricDict); } catch (Exception E) { LogEntry($"Error getting session property from citrix", LogLevels.Error); LogException(E); } } } const string collectDateColumnName = "CollectedDate"; var groupedValues = dicVals.Where(v => v.TryGetValue(collectDateColumnName, out var collectDate) && collectDate is DateTime) .ToLookup(v => ((DateTime)v[collectDateColumnName]).Date, v => v); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); foreach (var colInfo in SessionTableDetails.Columns.Values) { try { if (valuesOfDay is null) { AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(null, colInfo.ValueType)); continue; } var valuesOfColumn = valuesOfDay .Select(v => { if (v.TryGetValue(colInfo.SourceName, out var colValue)) return colValue; else return null; }) .Where(v => v != null); if (!valuesOfColumn.Any()) { AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(null, colInfo.ValueType)); continue; } if (colInfo is cDataHistoryConfigColumn configColumn && configColumn.AggregationType != eDataHistoryAggregationType.Unknown) { var aggregatedValue = GetAggregationValue(valuesOfColumn, configColumn.AggregationType, colInfo.ValueType); AddColumnData(retVal, colInfo.SourceName, ConvertToF4sdType(aggregatedValue, colInfo.ValueType)); } } catch (Exception E) { LogException(E); } } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed sessionmetrics information from citrix with id {Sessionid}. Sessionmetrics content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; void AddColumnData(cF4SDHealthCardRawData.cHealthCardTable table, string columnName, object value) { if (table.Columns.TryGetValue(columnName, out var columnValues)) columnValues.Values.Add(value); else table.Columns.Add(columnName, new cF4SDHealthCardRawData.cHealthCardTableColumn(table) { ColumnName = columnName, Values = new List() { value } }); } } private async Task> GetSessionMetricsDetailsIcaRttMSAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo,int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsIcaRttMS, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsIcaRTT, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsIcaRttMS, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsIcaLatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo,int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsIcaLatency, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsIcaLatency, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsIcaLatency, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsClientL7LatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsClientL7Latency, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsClientL7Latency, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsClientL7Latency, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsServerL7LatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsServerL7Latency, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsServerL7Latency, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsServerL7Latency, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsInputBandwidthUsedAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsInputBandwidthUsed, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsInputBandwidthUsed, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsInputBandwidthUsed, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsOutputBandwidthUsedAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsOutputBandwidthUsed, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsOutputBandwidthUsed, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsOutputBandwidthUsed, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsOutputBandwidthAvailableAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsOutputBandwidthAvailable, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsOutputBandwidthAvailable, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsOutputBandwidthAvailable, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = RefTime.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsFpsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsFps, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsFps, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsFps, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsInputFpsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsInputFps, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsInputFps, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsInputFps, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; //var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) // .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => { var utc = DateTime.Parse(v[collectDateColumnName].ToString(), null, System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal); return (utc - RefTime.TimeOfDay).Date.AddDays(1); }); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = RefTime.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsOutputFpsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsOutputFps, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsOutputFps, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsOutputFps, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsWanLatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsWanLatency, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsWanLatency, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsWanLatency, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMetricsDetailsDcLatencyAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int MaxAge, DateTime RefTime, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionDetailsDcLatency, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMetricsDcLatency, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retValList = new List(); var vals = sessionInfos.Children(); var metricsObj = sessionInfos["SessionMetrics"] as JArray; if (metricsObj != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameSessionDetailsDcLatency, Columns = SessionTableDetails.Columns.Values.Select(v => v.SourceName).ToList(), Values = new Dictionary>() }; const string collectDateColumnName = "CollectedDate"; var groupedValues = metricsObj.Where(v => v[collectDateColumnName] != null && DateTime.TryParse(v[collectDateColumnName]?.ToString(), out _)) .GroupBy(v => DateTime.Parse(v[collectDateColumnName].ToString()).Date); for (int i = 0; i < MaxAge; i++) { DateTime referenceDate = DateTime.Now.AddDays(-i).Date; var valuesOfDay = groupedValues.FirstOrDefault(v => v.Key == referenceDate); if (valuesOfDay == null) { retVal.Values[i] = new List(); continue; } int index = 0; foreach (var session in metricsObj) { try { var dicVals = session.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(i)) { retVal.Values[i] = new List(); } retVal.Values[i].Add(row); } catch (Exception e) { LogEntry($"Error processing session entry '{session.Path}'", LogLevels.Error); LogException(e); } index++; } } retVal.Columns = SessionTableDetails.Columns.Values.Select(v => v.Name).ToList(); return new List(1) { retVal }; } else { LogEntry($"Could not get detailed Session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSession, out var SessionTableDetails)) return null; var strUrl = string.Format(constCitrixSession, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameSession, InformationClass = enumFasdInformationClass.VirtualSession, Origin = enumDataHistoryOrigin.Citrix, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = sessionInfos.Children(); foreach (var token in vals) { try { if (token is JProperty prop) if (prop.Value is JValue val) dicVals[prop.Name] = val.Value; } catch (Exception E) { LogEntry($"Error getting session detail property from citrix '{token.Path}'", LogLevels.Error); LogException(E); } } foreach (var colInfo in SessionTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed session information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionUserAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionUser, out var UserTableDetails)) return null; var strUrl = string.Format(constCitrixSessionUser, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameSessionUser, InformationClass = enumFasdInformationClass.VirtualSession, Origin = enumDataHistoryOrigin.Citrix, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = sessionInfos.Children(); var userObj = sessionInfos["User"] as JObject; if (userObj != null) { foreach (var prop in userObj.Properties()) { try { if (prop.Value is JValue val) { dicVals[prop.Name] = val.Value; } else { dicVals[prop.Name] = prop.Value.ToString(Newtonsoft.Json.Formatting.None); } } catch (Exception E) { LogEntry($"Error getting user property from citrix'{prop.Name}'", LogLevels.Error); LogException(E); } } } foreach (var colInfo in UserTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed user information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionMachineAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionMachine, out var MachineTableDetails)) return null; var strUrl = string.Format(constCitrixSessionMachine, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameSessionMachine, InformationClass = enumFasdInformationClass.VirtualSession, Origin = enumDataHistoryOrigin.Citrix, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = sessionInfos.Children(); var machineObj = sessionInfos["Machine"] as JObject; if (machineObj != null) { foreach (var prop in machineObj.Properties()) { try { if (prop.Value is JValue val) { dicVals[prop.Name] = val.Value; } else { dicVals[prop.Name] = prop.Value.ToString(Newtonsoft.Json.Formatting.None); } } catch (Exception E) { LogEntry($"Error getting Machine property from citrix'{prop.Name}'", LogLevels.Error); LogException(E); } } } foreach (var colInfo in MachineTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; if (colInfo is cDataHistoryConfigColumn configColumn && !string.IsNullOrEmpty(configColumn.SourceJsonField)) { var _objVal = JObject.Parse(objVal.ToString()); var prop = _objVal.Property(configColumn.SourceJsonField); objVal = prop?.Value.ToString(); } objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed machine information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionCurrentConnectionAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionCurrentConnection, out var ConnectionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionCurrentConnection, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameSessionCurrentConnection, InformationClass = enumFasdInformationClass.VirtualSession, Origin = enumDataHistoryOrigin.Citrix, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = sessionInfos.Children(); var connectionObj = sessionInfos["CurrentConnection"] as JObject; var logOnMetricsObj = sessionInfos["LogOnMetrics"] as JArray; if (connectionObj != null) { foreach (var prop in connectionObj.Properties()) { try { if (prop.Value is JValue val) { dicVals[prop.Name] = val.Value; } else { dicVals[prop.Name] = prop.Value.ToString(Newtonsoft.Json.Formatting.None); } } catch (Exception E) { LogEntry($"Error getting connection property from citrix '{prop.Name}'", LogLevels.Error); LogException(E); } } } JObject newestMetric = null; if (logOnMetricsObj != null && logOnMetricsObj.Count > 0) { int maxId = logOnMetricsObj.Max(x => (int)x["Id"]); newestMetric = logOnMetricsObj.FirstOrDefault(x => (int)x["Id"] == maxId) as JObject; } if (newestMetric != null) { foreach (var prop in newestMetric.Properties()) { try { if (prop.Value is JValue val) { dicVals[prop.Name] = val.Value; } else { dicVals[prop.Name] = prop.Value.ToString(Newtonsoft.Json.Formatting.None); } } catch (Exception E) { LogEntry($"Error getting connection property from citrix '{prop.Name}'", LogLevels.Error); LogException(E); } } } foreach (var colInfo in ConnectionTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed connection information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetSessionConnectionsAsync(Guid Sessionid, string TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameSessionConnection, out var ConnectionTableDetails)) return null; var strUrl = string.Format(constCitrixSessionConnections, Sessionid); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(TenantId, true); if (citrixCommunication == null) return null; var res = await citrixCommunication.RequestListAsync(strUrl, retryForbidden: false); if (res != null) { JObject sessionInfos = res[0].Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameSessionConnection, InformationClass = enumFasdInformationClass.VirtualSession, Origin = enumDataHistoryOrigin.Citrix, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = sessionInfos.Children(); var connectionObj = sessionInfos["Connection"] as JObject; if (connectionObj != null) { foreach (var prop in connectionObj.Properties()) { try { if (prop.Value is JValue val) { dicVals[prop.Name] = val.Value; } else { dicVals[prop.Name] = prop.Value.ToString(Newtonsoft.Json.Formatting.None); } } catch (Exception E) { LogEntry($"Error getting connection property from citrix '{prop.Name}'", LogLevels.Error); LogException(E); } } } foreach (var colInfo in ConnectionTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed connection information from citrix with id {Sessionid}. Session content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public override async Task> GetDetailsTableResultsVirtualAsync(List Tables, Dictionary Identities, DateTime RefTime, int MaxAge, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var bIcaRttMSDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsIcaRttMS && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bIcaLatencyDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsIcaLatency && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bClientL7LatencyDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsClientL7Latency && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bServerL7LatencyDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsServerL7Latency && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bInputBandwidthUsedDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsInputBandwidthUsed && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bOutputBandwidthUsedDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsOutputBandwidthUsed && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bOutputBandwidthAvailableDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsOutputBandwidthAvailable && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bFpsDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsFps && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bInputFpsDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsInputFps && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bOutputFpsDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsOutputFps && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bWanLatencyDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsWanLatency && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); var bDcLatencyDetailExists = Tables.Exists(v => v.Name == constTableNameSessionDetailsDcLatency && v.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix); if (!Identities.TryGetValue(enumFasdInformationClass.VirtualSession, out var session) || session?.Id is null) return null; if (!Identities.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) return null; var listTasks = new List>>(2); if (bIcaRttMSDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsIcaRttMSAsync(session.Id, userIds.citrixTenantId, requestInfo,MaxAge, RefTime, LogDeep + 1, Token); })); if (bIcaLatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsIcaLatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime,LogDeep + 1, Token); })); if (bClientL7LatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsClientL7LatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bServerL7LatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsServerL7LatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bInputBandwidthUsedDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsInputBandwidthUsedAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bOutputBandwidthUsedDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsOutputBandwidthUsedAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bOutputBandwidthAvailableDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsOutputBandwidthAvailableAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bFpsDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsFpsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bInputFpsDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsInputFpsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bOutputFpsDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsOutputFpsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bWanLatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsWanLatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); if (bDcLatencyDetailExists) listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsDetailsDcLatencyAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, RefTime, LogDeep + 1, Token); })); var arrRes = await Task.WhenAll(listTasks.ToArray()); var retVal = new List(); foreach (var Entry in arrRes) if (Entry != null) retVal.AddRange(Entry); if (retVal.Count == 0) return null; return retVal; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public override async Task> GetQuickActionList() { var quickactions = new List{ "Session Logoff", "Session Hidden", "Send Message to Session" }; return await Task.FromResult(quickactions); } public async Task QuickActionRun(cF4SDServerQuickActionParameters jsonRequest, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { dynamic result = null; switch (jsonRequest.Action) { case "SessionLogoff": result = await SessionLogoff(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "SessionHidden": result = await SessionHidden(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "SendMessageToSession": result = await SendMessageToSession(jsonRequest.Identities, requestInfo, CancellationToken.None); break; } return result; } public async Task SessionLogoff(List Identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(Identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.VirtualSession, out var session) || session?.Id is null) return null; if (!Ids.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) return null; var strUrl = string.Format(constCitrixSessionLogoff, session?.Id); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(userIds?.citrixTenantId, true); if (citrixCommunication == null) { return null; } var Result = await citrixCommunication.RequestAsync(strUrl, httpMethod: eHttpMethod.post, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result as JObject; if (ResultJson != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } return ResultJson; } return null; } public async Task SessionHidden(List Identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(Identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.VirtualSession, out var session) || session?.Id is null) return null; if (!Ids.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) return null; var strUrl = string.Format(constCitrixSessionHide, session?.Id); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(userIds?.citrixTenantId, true); if (citrixCommunication == null) { return null; } var Result = await citrixCommunication.RequestAsync(strUrl, httpMethod: eHttpMethod.post, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result as JObject; if (ResultJson != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } return ResultJson; } return null; } public async Task SendMessageToSession(List Identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(Identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.VirtualSession, out var session) || session?.Id is null) return null; if (!Ids.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) return null; var strUrl = string.Format(constCitrixSessionSendMessage, session?.Id); var citrixCommunication = await GetCitrixCommunicationForTenantAsync(userIds?.citrixTenantId, true); if (citrixCommunication == null) { return null; } var message = new SessionMessage { Style = "Critical", Title = "Abmeldung bevorstehend!", Text = "Eine dringende Wartung muss durchgeführt werden. Sie werden in Kürze abgemeldet. Bitte speichern Sie Ihre Daten." }; var Result = await citrixCommunication.RequestAsync(strUrl, httpMethod: eHttpMethod.post, message, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result as JObject; if (ResultJson != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } return ResultJson; } return null; } public override async Task> GetTableResultsVirtualAsync(List Tables, Dictionary Identities, DateTime RefTime, int MaxAge, bool instantly, Guid? CacheId, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Identities.TryGetValue(enumFasdInformationClass.VirtualSession, out var session) || session?.Id is null) return null; if (!Identities.TryGetValue(enumFasdInformationClass.User, out var userIds) || userIds?.citrixTenantId is null) return null; var citrixTableNames = new HashSet(Tables.Where(t => t?.ParentCluster?.Origin == enumDataHistoryOrigin.Citrix).Select(t => t.Name), StringComparer.Ordinal); bool Has(string tableName) => tableName != null && citrixTableNames.Contains(tableName); var _userExists = Has(constTableNameSessionUser); var _sessionExists = Has(constTableNameSession); var _machineExists = Has(constTableNameSessionMachine); var _connectionExists = Has(constTableNameSessionConnection); var _currentConnectionExists = Has(constTableNameSessionCurrentConnection); var _sessionMetricsExists = Has(constTableNameSessionMetrics); var retVal = new List(); var listTasks = new List>>(2); if (_sessionExists) //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { // Name = constTableNameSessionMetrics, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); if (_sessionMetricsExists) //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { // Name = constTableNameSessionMetrics, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionMetricsAsync(session.Id, userIds.citrixTenantId, requestInfo, MaxAge, LogDeep + 1, Token); })); if (_machineExists) //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { // Name = constTableNameMachineMetrics, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionMachineAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); if (_connectionExists) //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { // Name = constTableNameMachineMetrics, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionConnectionsAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); if (_currentConnectionExists) //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { // Name = constTableNameMachineMetrics, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionCurrentConnectionAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); if (_userExists) //if (CacheId == null && !instantly) //{ // retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() // { // Name = constTableNameMachineMetrics, // InformationClass = enumFasdInformationClass.VirtualSession, // IsIncomplete = true // }); //} //else listTasks.Add(Task.Run>(async () => { return await GetSessionUserAsync(session.Id, userIds.citrixTenantId, requestInfo, LogDeep + 1, Token); })); var arrRes = await Task.WhenAll(listTasks.ToArray()); foreach (var Entry in arrRes) if (Entry != null) retVal.AddRange(Entry); if (retVal.Count == 0) return null; return retVal; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public async Task GetCitrixSessionsAsync() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var strUrl = string.Format(constCitrixSessionList, constCitrixUserPaging); var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; if (tenants is null) { Collector.DoProcessUiMessage(2, $" Could not logon to tenant - Tenant is null", LogLevels.Warning); return null; } foreach (var Tenant in tenants) { var CitrixConn = await GetCitrixCommunicationForTenantAsync(Tenant.TenantID, true); if (CitrixConn == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } var Result = await CitrixConn.RequestAsync(strUrl, retryForbidden: false); if (Result != null) { var _ty = Result.GetType(); var _props = Result.Result as JObject; if (_props != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{_props}' with {_props.Count} properties could be read."); } } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public override async Task DoScanAsync(bool Always, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (Always) { var scanInfo = await Collector.CitrixCollector.GetScanTimeInfoAsync(requestInfo, LogDeep + 1, Token); if (scanInfo == null) { Collector.DoProcessUiMessage(0, "could not get valid scan times info for Citrix connector"); return false; } if (scanInfo.NextScan == null) { Collector.DoProcessUiMessage(0, "not valid next scan time info for Citrix connector"); return false; } var nextScan = (DateTime)scanInfo.NextScan; if (!(scanInfo.LastScan is DateTime lastScan) || (lastScan >= nextScan)) { Collector.DoProcessUiMessage(0, $"currently no scan needed for Citrix connector, lastScan={scanInfo.LastScan} (UTC), nextScan={scanInfo.NextScan} (UTC)"); return false; } } Collector.DoProcessUiMessage(0, ""); if (Collector.InfrastructureConfig.CitrixTenants != null) await DoCitrixScanAsync(requestInfo, LogDeep + 1, Token); await Collector.SetLastScanTime("CitrixScan-all", DateTime.UtcNow, requestInfo, LogDeep + 1, Token); return true; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } public async Task DoCitrixScanAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var tenants = Collector.InfrastructureConfig.CitrixTenants?.Values; if (tenants is null) { Collector.DoProcessUiMessage(2, $" could not logon to tenant - Tenant is null ", LogLevels.Error); return true; } if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateUserCitrix", out var QueryInsert)) return false; using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (!Conn.IsOpen) { LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error); return false; } Collector.DoProcessUiMessage(0, string.Format("doing the Citrix cloud scans:")); foreach (var Tenant in tenants) { try { Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for users ..."); var CitrixConn = await GetCitrixCommunicationForTenantAsync(Tenant.TenantID, true); if (CitrixConn == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } var strUrl = string.Format(constCitrixUserList, constCitrixUserPaging); var Result = await CitrixConn.RequestListAsync(strUrl, retryForbidden: false); var Count = 0; var Updated = 0; while (Result != null == Result.Count > 0) { foreach (var Entry in Result) { Count++; try { var _strId = Entry.Result.Id.ToString(); var _sid = Entry.Result.Sid.ToString(); var sid = new SecurityIdentifier(_sid); var arrSid = new byte[sid.BinaryLength]; sid.GetBinaryForm(arrSid, 0); var SqlParams = new Dictionary(2) { { "sid_bin", arrSid }, { "id", _strId }, { "tenant", Tenant.TenantID } }; var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token); if (_res > 0) Updated++; else { if (DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateUserCitrix2", out var QueryInsert2)) { string upn = Entry.Result.Upn.ToString(); var SqlParams2 = new Dictionary(2) { { "id", _strId }, { "tenant", Tenant.TenantID }, { "upn_internal", upn } }; var _res2 = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert2, SqlParams2, requestInfo, Token); if (_res2 > 0) Updated++; } } } catch (Exception E) { LogException(E); } } Collector.DoProcessUiMessage(2, $"{Count} accounts processed, {Updated} updated."); if (Result.Count < constCitrixUserPaging) break; Result.Clear(); if (!await CitrixConn.RequestNextAsync(Result)) break; } } catch (Exception E) { LogException(E); } Collector.DoProcessUiMessage(1, $"...scanning tenat {Tenant.Domain} finished"); } Collector.DoProcessUiMessage(0, "... Citrix cloud scans finished."); return true; } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } public override async Task> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep) { await Task.CompletedTask; return new List() { IdEntry.sid }; } public override async Task GetScanTimeInfoAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { return await GetScanTimeInfoAsync(Collector.InfrastructureConfig.Citrix.ScanTiming, "CitrixScan", requestInfo, LogDeep, Token); } private static void AddToLdapPropertyList(List list, ref Dictionary dicLdap) { foreach (var Entry in list) { if (!dicLdap.ContainsKey(Entry.Name)) dicLdap.Add(Entry.Name.ToLowerInvariant(), Entry); } } private Dictionary GetPropertyList(enumFasdInformationClass InformationClass) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var dicLdap = new Dictionary(); try { switch (InformationClass) { case enumFasdInformationClass.User: AddToLdapPropertyList(UserMainProperties, ref dicLdap); break; } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return dicLdap; } public HashSet GetSupportedInformationClasses() => new HashSet() { enumFasdInformationClass.VirtualSession }; public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) { try { List sessionsIds = await _collector.getConnectorIdList(ids.ToList(), token, null, 0); List relations = await GetUserSessionsAsync(sessionsIds, token, null); return new cF4sdStagedSearchResultRelations() { Relations = relations }; } catch (Exception ex) { LogException(ex); } return null; } private enumCitrixSessionStatus ConvertSessionStatusCode(int sessionStatus) { try { switch (sessionStatus) { case 0: return enumCitrixSessionStatus.Unknown; case 1: return enumCitrixSessionStatus.Connected; case 2: return enumCitrixSessionStatus.Disconnected; case 3: return enumCitrixSessionStatus.Terminated; case 4: return enumCitrixSessionStatus.PreparingSession; case 5: return enumCitrixSessionStatus.Active; case 6: return enumCitrixSessionStatus.Reconnecting; case 7: return enumCitrixSessionStatus.NonBrokeredSession; case 8: return enumCitrixSessionStatus.Other; case 9: return enumCitrixSessionStatus.Pending; } } catch (Exception E) { LogException(E); } return enumCitrixSessionStatus.Unknown; } private object GetAggregationValue(IEnumerable objVal, eDataHistoryAggregationType AggregationType, enumFasdValueType ValueType) { double aggregatedValue; switch (ValueType) { case enumFasdValueType.INT: case enumFasdValueType.BIGINT: case enumFasdValueType.FLOAT: switch (AggregationType) { case eDataHistoryAggregationType.average: return aggregatedValue = objVal.Where(v => v != null) .Select(v =>Convert.ToDouble(v)) .DefaultIfEmpty(0) .Average(); case eDataHistoryAggregationType.sum: return aggregatedValue = objVal.Where(v => v != null) .Select(v => Convert.ToDouble(v)) .DefaultIfEmpty(0) .Sum(); case eDataHistoryAggregationType.max: return aggregatedValue = objVal.Where(v => v != null) .Select(v => v == DBNull.Value ? 0.0 : Convert.ToDouble(v)) .DefaultIfEmpty(0) .Max(); case eDataHistoryAggregationType.min: return aggregatedValue = objVal.Where(v => v != null) .Select(v => v == DBNull.Value ? 0.0 : Convert.ToDouble(v)) .DefaultIfEmpty(0) .Min(); case eDataHistoryAggregationType.Unknown: return null; } break; } return null; } private object ConvertToF4sdType(object objVal, enumFasdValueType ValueType) { try { if (objVal != null) { switch (ValueType) { case enumFasdValueType.INT: return cF4SDHealthCardRawData.GetInteger(objVal); case enumFasdValueType.GUID: return XML.cXmlParser.GetGuidFromString(objVal.ToString()); case enumFasdValueType.BOOLEAN: if (objVal == null) return false; if (objVal is bool) return (bool)objVal; if (objVal is int) return (int)objVal != 0; if (objVal is string) return bool.TryParse((string)objVal, out bool result) ? result : ((string)objVal == "1"); return false; // Standardrückgabewert, falls keine Bedingung zutrifft case enumFasdValueType.DATETIME: DateTime? dt = null; if (objVal is DateTime _dt) dt = _dt; else { if (DateTime.TryParse(objVal.ToString(), out _dt)) dt = _dt; } if (dt is DateTime _dt2) { if (_dt2 == new DateTime(1, 1, 1, 0, 0, 0)) dt = null; } return dt; default: return objVal.ToString(); } } } catch (Exception E) { LogException(E); } finally { } return null; } public class SessionMessage { public string Style { get; set; } = null; public string Title { get; set; } = null; public string Text { get; set; } = null; } } public class cF4SDVirtualSession : cF4sdApiSearchResultRelation { public Guid SessionId { get; set; } public string ConnectionState { get; set; } public string FailureState { get; set; } public string Startdate { get; set; } public string Enddate { get; set; } public string MachineId { get; set; } public string MachineName { get; set; } public Guid UserId { get; set; } } }