Last active
July 18, 2024 10:36
-
-
Save JohnLBevan/b37084af0f383b66ed342dda0017dbda to your computer and use it in GitHub Desktop.
Plot a line chart in PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add-Type -AssemblyName System.Windows.Forms | |
Add-Type -AssemblyName System.Windows.Forms.DataVisualization | |
Function Out-LineChart { | |
[CmdletBinding()] | |
Param ( | |
[Parameter(Mandatory, ValueFromPipeline)] | |
[Hashtable]$Data | |
, | |
[Parameter()] | |
[string]$Title = 'Line Chart' | |
, | |
[Parameter()] | |
[int]$Width = 800 | |
, | |
[Parameter()] | |
[int]$Height = 600 | |
, | |
[Parameter()] | |
[System.Windows.Forms.DataVisualization.Charting.Axis]$XAxis = [System.Windows.Forms.DataVisualization.Charting.Axis]::new() | |
, | |
[Parameter()] | |
[System.Windows.Forms.DataVisualization.Charting.Axis]$YAxis = [System.Windows.Forms.DataVisualization.Charting.Axis]::new() | |
, | |
[Parameter()] | |
[System.Windows.Forms.DataVisualization.Charting.ChartValueType]$XType = [System.Windows.Forms.DataVisualization.Charting.ChartValueType]::Auto | |
, | |
[Parameter()] | |
[System.Windows.Forms.DataVisualization.Charting.ChartValueType]$YType = [System.Windows.Forms.DataVisualization.Charting.ChartValueType]::Auto | |
, | |
[Parameter()] | |
[string]$XProperty = 'X' | |
, | |
[Parameter()] | |
[string]$YProperty = 'Y' | |
) | |
Begin { | |
$chartArea = [Windows.Forms.DataVisualization.Charting.ChartArea]::new() | |
$chartArea.AxisX = $XAxis | |
$chartArea.AxisY = $YAxis | |
$chart = [Windows.Forms.DataVisualization.Charting.Chart]::new() | |
$chart.Width = $Width | |
$chart.Height = $Height | |
$chart.ChartAreas.Add($chartArea) | |
} | |
Process { | |
foreach ($key in $Data.Keys) { | |
$series = [Windows.Forms.DataVisualization.Charting.Series]::new() | |
$series.Name = $key | |
$series.ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Line | |
$series.XValueType = $XType | |
$series.YValueType = $YType | |
foreach ($point in ($Data[$key] | Sort-Object $XProperty)) { | |
$series.Points.AddXY($point."$XProperty", $point."$YProperty") | Out-Null | |
} | |
$chart.Series.Add($series) | |
} | |
} | |
End { | |
$form = [Windows.Forms.Form]::new() | |
$form.Text = $Title | |
$form.Width = $Width | |
$form.Height = $Height | |
$form.Controls.Add($chart) | |
# Show the form | |
$form.ShowDialog() | Out-Null | |
} | |
} | |
Function Append-MissingIntervals { | |
[CmdletBinding()] | |
Param ( | |
[Parameter(Mandatory)] | |
[PSCustomObject[]]$Series | |
, | |
[Parameter()] | |
[ValidateScript({($_.TotalMilliseconds -ne 0) -or (throw 'Interval cannot be 0')})] | |
[TimeSpan]$Interval = [TimeSpan]::Parse('01:00:00') | |
, | |
[Parameter()] | |
[Nullable[DateTime]]$Start = $null | |
, | |
[Parameter()] | |
[Nullable[DateTime]]$End = $null | |
, | |
[Parameter()] | |
[string]$XProperty = 'X' | |
, | |
[Parameter()] | |
[string]$YProperty = 'Y' | |
) | |
if (($null -eq $Start) -or ($null -eq $End)) { | |
$maxmin = $Series."$XProperty" | Measure-Object -Maximum -Minimum | |
if ($null -eq $Start) {$Start = $maxmin.Minimum} | |
if ($null -eq $End) {$End = $maxmin.Maximum} | |
} | |
if ($Start -gt $End) { | |
$swap = $Start | |
$Start = $End | |
$End = $swap | |
} | |
if ($Interval.TotalMilliseconds -lt 0) { | |
$Interval = -$Interval | |
} | |
# convert our series to a hashtable for quick lookups | |
$hash = @{} | |
foreach ($item in $Series) { | |
$hash[$item.$XProperty] = $item.$YProperty | |
} | |
$current = $Start | |
while ($current -le $End) { | |
if (!$hash.ContainsKey($current)) { | |
$hash[$current] = 0 | |
} | |
$current += $Interval | |
} | |
foreach ($item in $hash.GetEnumerator()) { | |
([PSCustomObject]@{ | |
$XProperty = $item.Name | |
$YProperty = $item.Value | |
}) | |
} | |
} | |
# todo: improve how we initialise the axis values... want them to be parameterised to the function, but without the caller needing to know / interact with the .net classes directly | |
$x = [System.Windows.Forms.DataVisualization.Charting.Axis]::new() | |
$x.IntervalType = [System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType]::Days | |
$x.LabelStyle.Format = "yyyy-MM-dd HH" | |
$x.LabelStyle.Interval = 1 | |
# We may want to set XType to [System.Windows.Forms.DataVisualization.Charting.ChartValueType]::DateTime.. will see if auto cuts it. | |
$x.Title = "When" | |
$y = [System.Windows.Forms.DataVisualization.Charting.Axis]::new() | |
$y.Title = '# of Timeouts' | |
# Read logs | |
$data = Get-Item './path/to/logs/log_2024*.csv' | % FullName | Import-Csv -Delimiter ';' | Where-Object{$_.type -like 'ERROR*' -and $_.Message -like 'The operation has timed out.*'} | %{[DateTime]::Parse("$($_.Date.Trim()) $($_.Time.Substring(0,2)):00:00")} | Group-Object | Select-Object @{N='Name';E={$_.Name -as [DateTime]}}, Count | |
Out-LineChart -Data @{ | |
Series1 = (Append-MissingIntervals -Series $data -XProperty 'Name' -YProperty 'Count' -End (Get-Date -Minute 0 -Second 0 -Millisecond 0)) | |
Series2 = $data | |
} -XAxis $x -YAxis $y -XProperty 'Name' -YProperty 'Count' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment