/* * Copyright (c) 2022 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ // Copyright (c) 2020 LG Electronics Inc. // SPDX-License-Identifier: LicenseRef-LGE-Proprietary using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Aurora.Api; namespace AuroraUIComponent.Views { internal class TactData { public TactData() { logger = new Logger("TactTimeView"); } private Dictionary _dictLogItems = new Dictionary(); private Regex regex; private readonly Logger logger; private CaseType _caseType; public DateTime StartTime1 => _dictLogItems.Count != 0 ? _dictLogItems.Min(item => item.Value.LogItem1.StartTime) : DateTime.Now; public DateTime EndTime1 => _dictLogItems.Count != 0 ? _dictLogItems.Max(item => item.Value.LogItem1.EndTime) : DateTime.Now; public DateTime StartTime2 => _dictLogItems.Count != 0 ? _dictLogItems.Min(item => item.Value.LogItem2.StartTime) : DateTime.Now; public DateTime EndTime2 => _dictLogItems.Count != 0 ? _dictLogItems.Min(item => item.Value.LogItem2.EndTime) : DateTime.Now; internal void Start(IList files, string filePath, string searchKeyWord, double minFilter, CaseType caseType = CaseType.Case1, DateTime? st = null, DateTime? et = null) { //_dictLogItems.Clear(); _caseType = caseType; regex = new Regex($"{searchKeyWord}(.*), tact\\[(.*)\\]"); for (int i = 0; i < files.Count; i++) { var path = Path.Combine(filePath, files[i]); if (File.Exists(path) == false) continue; var lines = File.ReadAllLines(path, Encoding.Default); for (int j = 0; j < lines.Length; j++) { GetTact(lines[j], minFilter); } } List remList; if (caseType == CaseType.Case1) { remList = _dictLogItems.Where(item => item.Value.LogItem1.TactValues.Count < 2).Select(x => x.Key).ToList(); } else { remList = _dictLogItems.Where(item => item.Value.LogItem2.TactValues.Count < 2).Select(x => x.Key).ToList(); } foreach (var item in remList) { _dictLogItems.Remove(item); } CalcStat(st, et); } internal void ClearTable() { _dictLogItems.Clear(); } internal void CalcStat(DateTime? st = null, DateTime? et = null) { if (_caseType == CaseType.Case1) { Parallel.ForEach(_dictLogItems, (logItem) => { logItem.Value.LogItem1.Calc(st, et); }); } else { Parallel.ForEach(_dictLogItems, (logItem) => { logItem.Value.LogItem2.Calc(st, et); }); } } public List GetLogItems() => _dictLogItems.Values.ToList(); private void GetTact(string line, double minFilter) { try { var log = line.Split(new string[] { " " }, 2, StringSplitOptions.None); if (log.Length < 2) { return; } var match = regex.Match(log[1]); if (match.Success) { //logger.Log(line); var name = match.Groups[1].Value; var tact = match.Groups[2].Value; var tactVal = TimeSpan.Parse(tact); if (tactVal.TotalMilliseconds < minFilter) return; if (_dictLogItems.TryGetValue(name, out LogItemCase item) == false) { item = new LogItemCase(name); _dictLogItems[name] = item; } if (_caseType == CaseType.Case1) { item.LogItem1.TactValues.Add(new TimeValue { Time = Convert.ToDateTime(log[0]), Value = tactVal }); } else { item.LogItem2.TactValues.Add(new TimeValue { Time = Convert.ToDateTime(log[0]), Value = tactVal }); } } } catch (Exception ex) { logger.E(ex.Message); } } } public enum CaseType { Case1, Case2 } public class LogItemCase { public LogItemCase(string name) { Name = name; LogItem1 = new LogItem(); LogItem2 = new LogItem(); } public string Name { get; set; } public LogItem LogItem1 { get; set; } public LogItem LogItem2 { get; set; } public double DiffAve => LogItem2.Ave - LogItem1.Ave; public double DiffStd => LogItem2.Std - LogItem1.Std; public double DiffMin => LogItem2.Min - LogItem1.Min; public double DiffMax => LogItem2.Max - LogItem1.Max; public double DiffSum => LogItem2.Sum - LogItem1.Sum; public double DiffRange => LogItem2.Range - LogItem1.Range; public double DiffMaxAvg => LogItem2.Max_Avg - LogItem1.Max_Avg; } public class LogItem { public List> TactValues { get; set; } = new List>(); public DateTime StartTime { get => new DateTime(MinMs); } public DateTime EndTime { get => new DateTime(MaxMs); } public long MinMs { get; set; } public long MaxMs { get; set; } public double N { get; set; } public double Ave { get; set; } public double Std { get; set; } public double Min { get; set; } public double Max { get; set; } public double Median { get; set; } public double P25 { get; set; } public double P75 { get; set; } public double Skew { get; set; } public double Kur { get; set; } public double Sum { get; set; } public double Range { get; set; } public double Var { get; set; } public double VarN { get; set; } public double StdN { get; set; } public double Mode { get; set; } public double IQR { get; set; } public double IQR_Max { get; set; } public double IQR_Min { get; set; } public double IQR_LowerLimit { get; set; } public double IQR_UpperLimit { get; set; } public List Outlier { get; set; } public double Max_Avg => Max - Ave; public unsafe void Calc(DateTime? st = null, DateTime? et = null) { IEnumerable> temp = TactValues.ToList(); if (st != null) { temp = temp.Where(tv => tv.Time > st); } if (et != null) { temp = temp.Where(tv => tv.Time < et); } var time = temp.Select(tv => tv.Time.Ticks).ToArray(); var val = temp.Select(tv => tv.Value.TotalMilliseconds).ToArray(); BasicStat bs = new BasicStat(); fixed (double* p = val) fixed (long* t = time) { bs.CalcBasicStat(val.Length, p, t); bs.CalcStartEndTime(val.Length, t); } var fis = bs.GetType().GetFields(); foreach (var fi in fis) { var pi = this.GetType().GetProperty(fi.Name); if (pi == null) continue; pi.SetValue(this, fi.GetValue(bs)); } } } public class TimeValue { public DateTime Time { get; set; } public T Value { get; set; } } public class BasicStat { public double N; public double Ave, Std, Min, Max, Median, P25, P75, Skew, Kur; public double Sum, Range, Var, VarN, StdN, Mode; public double IQR, IQR_Max, IQR_Min, IQR_LowerLimit, IQR_UpperLimit; public List Outlier; public List Time; public long MinMs, MaxMs; public BasicStat() { Outlier = new List(); } public unsafe void CalcBasicStat(int NumData, double* pDataBuf, long* pTimeBuf) { int i, N = NumData; double dVal, dDiff, Sum, Sum2, Sum3, Sum4, Ave, Std, Min, Max, LLimit, ULimit; double* pData; long* pTime; QuickSort2(pDataBuf, 0, N - 1, pTimeBuf); Sum = Sum2 = Sum3 = Sum4 = 0.0; pTime = pTimeBuf; for (pData = pDataBuf, i = 0; i < N; i++, pData++) { dVal = *pData; Sum += dVal; dVal *= dVal; Sum2 += dVal; dVal *= dVal; Sum3 += dVal; dVal *= dVal; Sum4 += dVal; } this.Min = pDataBuf[0]; this.Max = pDataBuf[N - 1]; this.Range = this.Max - this.Min; this.Sum = Sum; this.Ave = Ave = (Sum / N); this.Var = Math.Abs(Sum2 / N - Ave * Ave); this.Std = Std = Math.Sqrt(this.Var); this.Skew = ((Sum3 / N) - (3 * Ave * Std * Std) - (Ave * Ave * Ave)) / (Std * Std * Std); this.P25 = pDataBuf[(int)(N * 0.25 + 0.5)]; this.Median = pDataBuf[(int)(N * 0.50 + 0.5)]; this.P75 = pDataBuf[(int)(N * 0.75 + 0.5)]; // Calc IQR for BoxPlot this.IQR = this.P75 - this.P25; this.IQR_UpperLimit = this.P75 + this.IQR * 1.5; this.IQR_LowerLimit = this.P25 - this.IQR * 1.5; Sum4 = Sum2 = dDiff = 0.0; Min = this.P25; Max = this.P75; LLimit = this.IQR_LowerLimit; ULimit = this.IQR_UpperLimit; // dkdk.kim // Collect 15 outliers from the highest and lowest. Outlier.Clear(); double?[] lowOutliers = new double?[15]; double?[] highOutliers = new double?[15]; long?[] lowOutliersTime = new long?[15]; long?[] highOutliersTime = new long?[15]; int numOfHalfOutliers = 15; int lowIdx = 0, highIdx = -1; double lPrev = double.MaxValue, hPrev = double.MinValue; for (pData = pDataBuf, i = 0; i < N; i++, pData++) { dVal = *pData; dDiff = dVal - Ave; dDiff *= dDiff; // (X-Ave)^2 Sum2 += dDiff; dDiff *= dDiff; // (X-Ave)^4 Sum4 += dDiff; if (dVal < Min && dVal > LLimit) Min = dVal; if (dVal > Max && dVal < ULimit) Max = dVal; if (lowIdx < numOfHalfOutliers && dVal < LLimit && dVal != lPrev) { //lowOutliersTime[lowIdx] = lowOutliers[lowIdx++] = dVal; lPrev = dVal; } else if (dVal > ULimit && dVal != hPrev) { highIdx = (highIdx + 1) % numOfHalfOutliers; highOutliers[highIdx] = dVal; hPrev = dVal; } //if (dVal < LLimit || dVal > ULimit) //{ // if (nOut++ < 30) // this.Outlier.Add(dVal); // else // break; //} } this.Outlier.AddRange(lowOutliers.Where(x => x != null).Select(x => (double)x)); this.Outlier.AddRange(highOutliers.Where(x => x != null).Select(x => (double)x)); this.VarN = Sum2 / (N - 1); this.StdN = Std = Math.Sqrt(this.VarN); this.Kur = (Sum4 / N) / ((Sum2 / N) * (Sum2 / N)); // E[ (X-E[X])^4 ] / E[ (X-E[X])^2 ]^2 this.IQR_Min = Min; this.IQR_Max = Max; this.N = N; } public unsafe void CalcStartEndTime(int NumData, long* pTimeBuf) { QuickSort(pTimeBuf, 0, NumData - 1); MinMs = pTimeBuf[0]; MaxMs = pTimeBuf[NumData - 1]; } private unsafe static void QuickSort2(T1* pValue, int left, int right, T2* pTime) where T1 : unmanaged where T2 : unmanaged { int i = left, j = right; T1 pivot = pValue[(left + right) / 2]; T1 temp; T2 tempTime; do { while (Comparer.Default.Compare(pValue[i], pivot) < 0) i++; // pDouble[i] < pivot while (Comparer.Default.Compare(pValue[j], pivot) > 0) j--; // pDouble[j] > pivot if (i <= j) { temp = pValue[i]; pValue[i] = pValue[j]; pValue[j] = temp; tempTime = pTime[i]; pTime[i] = pTime[j]; pTime[j] = tempTime; i++; j--; } } while (i <= j); /* recursion */ if (left < j) QuickSort2(pValue, left, j, pTime); if (i < right) QuickSort2(pValue, i, right, pTime); } private unsafe static void QuickSort(T* pValue, int left, int right) where T : unmanaged { int i = left, j = right; T pivot = pValue[(left + right) / 2]; T temp; do { while (Comparer.Default.Compare(pValue[i], pivot) < 0 ) i++; // pDouble[i] < pivot while (Comparer.Default.Compare(pValue[j], pivot) > 0 ) j--; // pDouble[j] > pivot if (i <= j) { temp = pValue[i]; pValue[i] = pValue[j]; pValue[j] = temp; i++; j--; } } while (i <= j); /* recursion */ if (left < j) QuickSort(pValue, left, j); if (i < right) QuickSort(pValue, i, right); } } }