diff --git a/.gitignore b/.gitignore index a9dfc27..0cee943 100644 --- a/.gitignore +++ b/.gitignore @@ -362,5 +362,5 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd -Data +TrainData prometheus-2.45.0-rc.0.windows-amd64 \ No newline at end of file diff --git a/DeepTrace/Controls/TimeSeriesChart.razor b/DeepTrace/Controls/TimeSeriesChart.razor index 713f12e..8e7e6a5 100644 --- a/DeepTrace/Controls/TimeSeriesChart.razor +++ b/DeepTrace/Controls/TimeSeriesChart.razor @@ -1,33 +1,42 @@ @using PrometheusAPI; -
- - -
+ + @foreach (var ts in _currentData.Series) + { + + } + @code { + public class TimeSeriesDataSet + { + public string Name { get; init; } = "Value"; + public string Color { get; init; } = ""; + public IReadOnlyCollection Data { get; init; } = Array.Empty(); + } + + public class TimeSeriesData + { + public List Series { get; init; } = new List(); + } + + [CascadingParameter] protected bool IsDarkMode { get; set; } - [Parameter] public IList? Data { get; set; } + [Parameter] public TimeSeriesData? Data { get; set; } [Parameter] public DateTime? MinDate { get; set; } [Parameter] public DateTime? MaxDate { get; set; } @@ -36,10 +45,7 @@ private ApexChart? _chart; private ApexChartOptions? _options; - private IList? _currentData; - - private bool IsChartHidden => _currentData == null; - private bool IsChartShown => !IsChartHidden; + private TimeSeriesData _currentData = new() { Series = { new () } }; protected override void OnInitialized() { @@ -60,9 +66,10 @@ if (Data == _currentData) return; - _currentData = Data; + _currentData = Data ?? new() { Series = { new() } }; ; + _options = CreateOptions(); - if (_currentData == null) + if (_chart == null) return; //await InvokeAsync(StateHasChanged); @@ -77,10 +84,7 @@ var backgroundColor = IsDarkMode ? "var(--mud-palette-surface)" : "#f3f3f3"; var gridColor = IsDarkMode ? "var(--mud-palette-drawer-background)" : "#f3f3f3"; var borderColor = IsDarkMode ? "var(--mud-palette-text-primary)" : "#e7e7e7"; - var lineColors = IsDarkMode - ? new List { "#F57F17", "#00838F" } - : new List { "#77B6EA", "#545454" } - ; + var lineColors = _currentData.Series.Select( x => x.Color).ToList(); var mode = IsDarkMode ? Mode.Dark : Mode.Light @@ -139,7 +143,7 @@ } }, Colors = lineColors, - Markers = new() { Shape = ShapeEnum.Circle, Size = 2, FillOpacity = new Opacity(0.8d) }, + //Markers = new() { Shape = ShapeEnum.Circle, Size = 2, FillOpacity = new Opacity(0.8d) }, Stroke = new() { Curve = Curve.Straight, Width = 2 }, Legend = new() { @@ -168,12 +172,12 @@ DateTimeOffset xMax; xMin = zoomedData.XAxis?.Min == null - ? Data!.Min(e => e.TimeStamp.Date) + ? _currentData!.Series.First().Data.Min(e => e.TimeStamp.Date) : DateTimeOffset.FromUnixTimeMilliseconds((long)zoomedData.XAxis.Min) ; xMax = zoomedData.XAxis?.Max == null - ? Data!.Max(e => e.TimeStamp.Date) + ? _currentData!.Series.First().Data.Max(e => e.TimeStamp.Date) : DateTimeOffset.FromUnixTimeMilliseconds((long)zoomedData.XAxis.Max) ; diff --git a/DeepTrace/Data/DataSourceDefinition.cs b/DeepTrace/Data/DataSourceDefinition.cs new file mode 100644 index 0000000..0002a21 --- /dev/null +++ b/DeepTrace/Data/DataSourceDefinition.cs @@ -0,0 +1,29 @@ +namespace DeepTrace.Data; + +public class DataSourceQuery +{ + + public DataSourceQuery(string query, string color) + { + Query = query; + Color = color; + } + + public string Query { get; set; } + public MudBlazor.Utilities.MudColor Color { get; set; } + +} + +public class DataSourceDefinition +{ + private static int _instanceId; + public DataSourceDefinition() + { + var id = Interlocked.Increment(ref _instanceId); + Name = $"Dataset #{id}"; + } + + public string Name { get; set; } + public List Queries { get; set; } = new(); + public string Description { get; set; } = string.Empty; +} diff --git a/DeepTrace/Data/DataViewHelper.cs b/DeepTrace/Data/DataViewHelper.cs new file mode 100644 index 0000000..98b0609 --- /dev/null +++ b/DeepTrace/Data/DataViewHelper.cs @@ -0,0 +1,52 @@ +using Microsoft.ML; +using PrometheusAPI; +using System.Data; + +namespace DeepTrace.Data; +public static class DataViewHelper +{ + public static DataTable? ToDataTable(this IDataView dataView) + { + DataTable? dt = null; + if (dataView != null) + { + dt = new DataTable(); + var preview = dataView.Preview(); + dt.Columns.AddRange(preview.Schema.Select(x => new DataColumn(x.Name)).ToArray()); + foreach (var row in preview.RowView) + { + var r = dt.NewRow(); + foreach (var col in row.Values) + { + r[col.Key] = col.Value; + } + dt.Rows.Add(r); + + } + } + return dt; + } + + public static List ToTimeSeries(this IDataView dataView) + { + var dt = new List(); + if (dataView == null) + { + return dt; + } + + var preview = dataView.Preview(); + var tsCol = preview.Schema["TimeStamp"]; + var valCol = preview.Schema["Value"]; + + foreach (var row in preview.RowView) + { + var r = new TimeSeries( + Convert.ToDateTime(row.Values[tsCol.Index].Value), + (float)Convert.ToDouble(row.Values[valCol.Index].Value) + ); + dt.Add(r); + } + return dt; + } +} diff --git a/DeepTrace/Data/MLModel1.mbconfig b/DeepTrace/Data/MLModel1.mbconfig new file mode 100644 index 0000000..94ebac1 --- /dev/null +++ b/DeepTrace/Data/MLModel1.mbconfig @@ -0,0 +1,6 @@ +{ + "TrainingTime": 0, + "Scenario": "Default", + "Type": "TrainingConfig", + "Version": 2 +} \ No newline at end of file diff --git a/DeepTrace/Data/Prediction.cs b/DeepTrace/Data/Prediction.cs new file mode 100644 index 0000000..97d8502 --- /dev/null +++ b/DeepTrace/Data/Prediction.cs @@ -0,0 +1,15 @@ +using Microsoft.ML.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DeepTrace.Data; + +public class MyPrediction +{ + //vector to hold alert,score,p-value values + [VectorType(3)] + public double[]? Prediction { get; set; } +} diff --git a/DeepTrace/Pages/FetchData.razor b/DeepTrace/Pages/FetchData.razor index 77936fe..1d73a57 100644 --- a/DeepTrace/Pages/FetchData.razor +++ b/DeepTrace/Pages/FetchData.razor @@ -25,12 +25,53 @@ + + + + + @foreach (var source in _dataSources) + { + @source.Name + } + + Add + @if (_dataSources.Count > 1) + { + Delete + } + + + - + + + + + @for ( var i = 0; i < _queryForm.Source.Queries.Count; i++ ) + { + int pos = i; + var def = _queryForm.Source.Queries[pos]; + + + + + + + @if (_queryForm.Source.Queries.Count > 1) + { + + } + + + + + } + + Clear @@ -38,9 +79,11 @@ Ok + @**@ + Submit @@ -51,7 +94,10 @@ - + + @@ -66,7 +112,7 @@ private class PrometheusForm { [Required] - public string? Query { get; set; } = """ec2_cpu_utilization_24ae8d"""; + public DataSourceDefinition Source { get; set; } = new(); public DateRange Dates { get; set; } = new DateRange(DateTime.UtcNow.Date - TimeSpan.FromDays(14), DateTime.UtcNow.Date); @@ -82,8 +128,21 @@ } private PrometheusForm _queryForm = new(); + private List _dataSources = new() + { + new() + { + Queries = + { + new("""ec2_cpu_utilization_24ae8d""", "#F57F17") + } + } + }; private MudDateRangePicker? _picker; + private bool IsChartHidden => DisplayData == null; + private bool IsChartShown => !IsChartHidden; + private DateTime? MinDate { get => _queryForm.Dates.Start?.Date + _queryForm.TimeStart; @@ -120,11 +179,72 @@ } } - private List? DisplayData { get; set; } + private TimeSeriesChart.TimeSeriesData? DisplayData { get; set; } + + private string[] _palette = new[] + { + "#F44336", "#E91E63", "#9C27B0", "#673AB7", "#3F51B5", + "#FFEBEE", "#FCE4EC", "#F3E5F5", "#EDE7F6", "#E8EAF6", + "#FFCDD2", "#F8BBD0", "#E1BEE7", "#D1C4E9", "#C5CAE9", + "#EF9A9A", "#F48FB1", "#CE93D8", "#B39DDB", "#9FA8DA", + "#E57373", "#F06292", "#BA68C8", "#9575CD", "#7986CB", + "#EF5350", "#EC407A", "#AB47BC", "#7E57C2", "#5C6BC0", + "#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB" + }; + + + protected override void OnInitialized() + { + base.OnInitialized(); + _queryForm.Source = _dataSources[0]; + } + + private void AddQuery(int pos) + { + pos += 1; + var color = pos < _palette.Length + ? _palette[pos] + : "#F44336" + ; + _queryForm.Source.Queries.Insert(pos, new("",color)); + StateHasChanged(); + } + + private void DeleteQuery(int pos) + { + _queryForm.Source.Queries.RemoveAt(pos); + StateHasChanged(); + } + + + private void HandleAddSource() + { + _dataSources.Add(new() { Queries = { new("", _palette[0]) } }); + _queryForm.Source = _dataSources[^1]; + StateHasChanged(); + } + + private void HandleDeleteSource() + { + if (_dataSources.Count < 2) + { + return; + } + + var pos = _dataSources.IndexOf(_queryForm.Source); + if (pos < 0) + { + ShowError("Not found"); + return; + } + _dataSources.RemoveAt(pos); + _queryForm.Source = _dataSources[pos<_dataSources.Count ? pos: _dataSources.Count-1]; + StateHasChanged(); + } private async Task HandleSubmit() { - if (string.IsNullOrWhiteSpace(_queryForm.Query) || _queryForm.Dates.End == null || _queryForm.Dates.Start == null) + if (_queryForm.Source.Queries.Count < 1 || string.IsNullOrWhiteSpace(_queryForm.Source.Queries[0].Query) || _queryForm.Dates.End == null || _queryForm.Dates.Start == null) return; var startTime = _queryForm.TimeStart @@ -146,16 +266,8 @@ var startDate = _queryForm.Dates.Start.Value.Date + startTime; var endDate = _queryForm.Dates.End.Value.Date + endTime; - try - { - var formatted = await Prometheus.FormatQuery(_queryForm.Query); - _queryForm.Query = formatted; - } - catch (Exception e) - { - ShowError(e.Message); + if (!await FormatQueries()) return; - } TimeSpan step; @@ -167,36 +279,90 @@ seconds = 1.0; step = TimeSpan.FromSeconds(seconds); - var res = await Prometheus.RangeQuery(_queryForm.Query, startDate, endDate, step, TimeSpan.FromSeconds(2)); + var tasks = _queryForm.Source.Queries + .Select(x => Prometheus.RangeQuery(x.Query, startDate, endDate, step, TimeSpan.FromSeconds(2))) + .ToArray(); - if (res.Status != StatusType.Success) + try { - ShowError(res.Error ?? "Error"); + await Task.WhenAll(tasks); + } + catch ( Exception e ) + { + ShowError(e.Message); return; } - if (res.ResultType != ResultTypeType.Matrix) + var data = new List(); + + foreach ( var (res, def) in tasks.Select((x,i) => (x.Result, _queryForm.Source.Queries[i]) )) { - ShowError($"Got {res.ResultType}, but Matrix expected"); - return; + if (res.Status != StatusType.Success) + { + ShowError(res.Error ?? "Error"); + return; + } + + if (res.ResultType != ResultTypeType.Matrix) + { + ShowError($"Got {res.ResultType}, but Matrix expected for {def.Query}"); + return; + } + + var m = res.AsMatrix().Result; + if (m == null || m.Length != 1) + { + ShowError($"No data returned for {def.Query}"); + return; + } + + data.Add( + new() + { + Name = def.Query, + Color = def.Color.Value, + Data = m[0].Values!.ToList() + } + ); } - var m = res.AsMatrix().Result; - if (m == null || m.Length != 1) - { - ShowError("No data returned"); - return; - } + DisplayData = new() { Series = data }; + await InvokeAsync(StateHasChanged); + + // --------------------- playground ----------------------------- var mlContext = new MLContext(); - DisplayData = m[0].Values!.ToList(); - var dataView = mlContext.Data.LoadFromEnumerable(DisplayData.Select(x => new MyTimeSeries(Time: x.TimeStamp, Value: x.Value))); + + var dataView = mlContext.Data.LoadFromEnumerable(DisplayData.Series[0].Data.Select(x => new MyTimeSeries(Time: x.TimeStamp, Value: x.Value))); //DetectSpike(mlContext, dataView, data); int period = DetectPeriod(mlContext, dataView); DetectAnomaly(mlContext, dataView, period); - await InvokeAsync(StateHasChanged); + } + + private async Task FormatQueries() + { + try + { + var formatTasks = _queryForm.Source.Queries + .Select(x => Prometheus.FormatQuery(x.Query)) + .ToArray(); + + await Task.WhenAll(formatTasks); + + for ( var i = 0; i < formatTasks.Length; i++ ) + { + _queryForm.Source.Queries[i].Query = formatTasks[i].Result; + } + + return true; + } + catch (Exception e) + { + ShowError(e.Message); + return false; + } } private record MyTimeSeries(DateTime Time, double Value);