#requires -Version 5 param($Work) # restart PowerShell with -noexit, the same script, and 1 if ((!$Work) -and ($host.name -eq 'ConsoleHost')) { powershell.exe -noexit -file $MyInvocation.MyCommand.Path 1 return } # Set Variables $SyncHash = [hashtable]::Synchronized(@{}) $SyncHash.Host = $host $SyncHash.IperfFolder = $PSScriptRoot + '\Bin' # UI Runspace $UiRunspace = [runspacefactory]::CreateRunspace() $UiRunspace.ApartmentState = 'STA' $UiRunspace.ThreadOptions = 'ReuseThread' $UiRunspace.Open() $UiRunspace.SessionStateProxy.SetVariable('syncHash',$SyncHash) # UI Script $UiPowerShell = [PowerShell]::Create().AddScript( { $SyncHash.host.ui.WriteVerboseLine(' UI Script Started') trap {$SyncHash.host.ui.WriteErrorLine("$_`nError was in Line {0}`n{1}" -f ($_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.Line))} function Write-Status { [CmdletBinding()] param ( [Parameter(Mandatory=$true, Position=0)] [String]$Text, [Parameter(Mandatory=$true, Position=1)] [String]$Colore ) $syncHash.Form.Dispatcher.invoke([action]{ if (![string]::IsNullOrWhitespace([System.Windows.Documents.TextRange]::new($SyncHash.IperfJobOutputTextBox.Document.ContentStart, $SyncHash.IperfJobOutputTextBox.Document.ContentEnd).Text)) { $SyncHash.IperfJobOutputTextBox.AppendText("`r") } $TextRange = [System.Windows.Documents.TextRange]::new($SyncHash.IperfJobOutputTextBox.Document.ContentEnd, $SyncHash.IperfJobOutputTextBox.Document.ContentEnd) $TextRange.Text = $Text $TextRange.ApplyPropertyValue([System.Windows.Documents.TextElement]::ForegroundProperty, [System.Windows.Media.Brushes]::$Colore) $SyncHash.IperfJobOutputTextBox.ScrollToEnd() }) } function Start-Iperf { $SyncHash.host.ui.WriteVerboseLine('Start-Iperf') if ($SyncHash.IperfJobMonitorRunspace.RunspaceStateInfo.State -eq 'Opened') { Write-Status -Text 'Iperf Already Running' -Colore 'Orange' } else { #Get-Job | Remove-Job -Force #$SyncHash.Remove('IperfJob') # Iperf Job Monitor with Register-ObjectEvent in Runspace $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() $InitialSessionState.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'Stop-Iperf', (Get-Content Function:\Stop-Iperf))) $InitialSessionState.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'Write-Status', (Get-Content Function:\Write-Status))) $SyncHash.IperfJobMonitorRunspace = [runspacefactory]::CreateRunspace($InitialSessionState) $SyncHash.IperfJobMonitorRunspace.ApartmentState = 'STA' $SyncHash.IperfJobMonitorRunspace.ThreadOptions = 'ReuseThread' $SyncHash.IperfJobMonitorRunspace.Open() $SyncHash.IperfJobMonitorRunspace.SessionStateProxy.SetVariable('syncHash',$SyncHash) $SyncHash.IperfJobMonitorRunspace.SessionStateProxy.SetVariable('CsvFilePath',$SyncHash.CsvFilePathTextBox.Text) $SyncHash.IperfJobMonitorRunspace.SessionStateProxy.SetVariable('Command',$SyncHash.CommandTextBox.Text) $SyncHash.IperfJobMonitorRunspace.SessionStateProxy.SetVariable('IperfVersion',$IperfVersion) $SyncHash.IperfJobMonitorRunspace.SessionStateProxy.SetVariable('IperfExe',$IperfExe) $SyncHash.IperfJobMonitorPowerShell = [PowerShell]::Create().AddScript( { trap {$SyncHash.host.ui.WriteErrorLine("$_`nError was in Line {0}`n{1}" -f ($_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.Line))} trap [System.Management.Automation.PipelineStoppedException] {$SyncHash.host.ui.WriteVerboseLine($_)} $SyncHash.host.ui.WriteVerboseLine('Start-Iperf Running') Set-Location -Path $SyncHash.IperfFolder try { $ErrorActionPreferenceOrg = $ErrorActionPreference if ($IperfVersion -eq 2) { 'Time,localIp,localPort,RemoteIp,RemotePort,Id,Interval,Transfer,Bitrate' | Out-File -FilePath $CsvFilePath Write-Status -Text ((Invoke-Expression -Command "$IperfExe -v") 2>&1) -Colore 'Blue' $ErrorActionPreference = 'stop' Invoke-Expression -Command $Command | Out-File -FilePath $CsvFilePath -Append } else { Set-Content -Path $CsvFilePath -Value $null #Write-Status -Text ((Invoke-Expression -Command "$IperfExe -v") -join ' ') -Colore 'Blue' Invoke-Expression -Command $Command if ($ErrorOut = Get-Content -Tail 5 -Path $CsvFilePath | Select-String -Pattern 'iperf3: error') { Write-Error -Message $ErrorOut -ErrorAction Stop } } } catch { Write-Status -Text $_ -Colore 'Red' Stop-Iperf } $ErrorActionPreference = $ErrorActionPreferenceOrg Write-Status -Text 'Iperf Finished' -Colore 'Green' Stop-Iperf #Get-EventSubscriber | Unregister-Event #$SyncHash.Remove('IperfJobMonitor') }) $SyncHash.IperfJobMonitorPowerShell.Runspace = $SyncHash.IperfJobMonitorRunspace $SyncHash.IperfJobMonitorHandle = $SyncHash.IperfJobMonitorPowerShell.BeginInvoke() } } function Stop-Iperf { $SyncHash.host.ui.WriteVerboseLine('Stop-Iperf') if ($SyncHash.IperfJobMonitorRunspace.RunspaceStateInfo.State -eq 'Opened') { $SyncHash.host.ui.WriteVerboseLine('Stop-Iperf Running') #$SyncHash.IperfJobMonitorPowerShell.EndInvoke($SyncHash.IperfJobMonitorHandle) $SyncHash.IperfJobMonitorRunspace.Close() $SyncHash.IperfJobMonitorPowerShell.Dispose() } } function Start-Analyzer { $SyncHash.host.ui.WriteVerboseLine('Start-Analyzer') if (!(Test-Path -Path $SyncHash.CsvFilePathTextBox.Text)) { Write-Status -Text 'File not found' -Colore 'Red' } elseif ($SyncHash.AnalyzerRunspace.RunspaceStateInfo.State -eq 'Opened') { Write-Status -Text 'Analyzer Already Running' -Colore 'Orange' } else { # Analyzer Runspace $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() $InitialSessionState.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'Stop-Analyzer', (Get-Content Function:\Stop-Analyzer))) $InitialSessionState.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'Write-Status', (Get-Content Function:\Write-Status))) $SyncHash.AnalyzerRunspace = [runspacefactory]::CreateRunspace($InitialSessionState) $SyncHash.AnalyzerRunspace.ApartmentState = 'STA' $SyncHash.AnalyzerRunspace.ThreadOptions = 'ReuseThread' $SyncHash.AnalyzerRunspace.Open() $SyncHash.AnalyzerRunspace.SessionStateProxy.SetVariable('syncHash',$SyncHash) $SyncHash.AnalyzerRunspace.SessionStateProxy.SetVariable('CsvFilePath',$SyncHash.CsvFilePathTextBox.Text) $SyncHash.AnalyzerRunspace.SessionStateProxy.SetVariable('LastX',$SyncHash.LastXTextBox.Text) $SyncHash.AnalyzerRunspace.SessionStateProxy.SetVariable('IperfVersion',$IperfVersion) $SyncHash.AnalyzerPowerShell = [powershell]::Create() $SyncHash.AnalyzerPowerShell.Runspace = $SyncHash.AnalyzerRunspace $null = $SyncHash.AnalyzerPowerShell.AddScript($AnalyzerScript) $SyncHash.AnalyzerHandle = $SyncHash.AnalyzerPowerShell.BeginInvoke() } } # Analyzer Runspace Script $AnalyzerScript = { $SyncHash.host.ui.WriteVerboseLine('Start-Analyzer Running') trap {$SyncHash.host.ui.WriteErrorLine("$_`nError was in Line {0}`n{1}" -f ($_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.Line))} trap [System.Management.Automation.PipelineStoppedException] {$SyncHash.host.ui.WriteVerboseLine($_)} $First = $true $Header = $null $AnalyzerDataLength = 0 $ChartDataAction0 = [Action]{ $SyncHash.Chart.Series['Bitrate (Mbits/sec)'].Points.Clear() $SyncHash.Chart.Series['Transfer (MBytes)'].Points.Clear() $SyncHash.host.ui.WriteVerboseLine('Clear Data: ' + ($SyncHash.Chart.Series['Transfer (MBytes)'].Points.Count | Out-String)) } $SyncHash.Chart.Invoke($ChartDataAction0) Get-Content -Path $CsvFilePath -ReadCount 0 -Wait | ForEach-Object { trap {$SyncHash.host.ui.WriteErrorLine("$_`nError was in Line {0}`n{1}" -f ($_.InvocationInfo.ScriptLineNumber, $_.InvocationInfo.Line))} #$SyncHash.host.ui.WriteVerboseLine('Loop Data: ' + ($_ | Out-String)) ### $AnalyzerData = New-Object -TypeName System.Collections.Generic.List[System.Object] if ($IperfVersion -eq 2) { foreach ($Line in $_) { if ($Line -like '*Bitrate*') { $Header = $Line -split ',' } else { if ($First -and !$Header) { Write-Status -Text 'CSV Error' -Colore 'Red' Stop-Analyzer } else { $First = $false } $CsvLine = $Line | ConvertFrom-Csv -Header $Header $CsvLine.Bitrate = $CsvLine.Bitrate /1Mb $CsvLine.Transfer = $CsvLine.Transfer /1Mb if (!($CsvLine.Interval).StartsWith('0.0-') -or ($CsvLine.Interval -eq '0.0-1.0')) { $AnalyzerData.add($CsvLine) } else { $SyncHash.host.ui.WriteVerboseLine('Remove Total Line: ' + $CsvLine.Time) } } } } else { $Csv = $_ | Where-Object {$_ -match '\[...\]'} #$Csv = $a | Select-String -Pattern '\[...\]' foreach ($Line in $Csv) { $Line = $Line -replace '[][]' if ($Line -like ' ID *') { $Header = ($Line = $Line -replace 'Total Datagram','Total-Datagram' -replace 'Lost/Total Datagrams','Lost/Total-Datagrams') -split '\s+' | Where-Object {$_} $HeaderIndex = @() foreach ($Head in $Header) { $HeaderIndex += $Line.IndexOf($Head) } } elseif ($Header -and $Line -notlike '*connected to*' -and $Line -notlike '*sender*' -and $Line -notlike '*receiver*' -and $Line -cnotlike '*datagrams*') { $i=0 $CsvLine = New-Object System.Object foreach ($Head in $Header) { if ($i -lt $HeaderIndex.Length-1) { $Cell = $Line.Substring($HeaderIndex[$i],$HeaderIndex[$i + 1] - $HeaderIndex[$i]) } else { $Cell = $Line.Substring($HeaderIndex[$i]) } if ($Head -eq 'Transfer') { $TransferData = $Cell.Trim() -split '\s+' if ($TransferData[1] -eq 'KBytes') { $Cell = $TransferData[0] /1kb } elseif ($TransferData[1] -eq 'GBytes') { $Cell = $TransferData[0] *1kb } } $i++ Add-Member -InputObject $CsvLine -NotePropertyName $Head -NotePropertyValue ("$Cell".Trim() -split '\s+')[0] } $AnalyzerData.add($CsvLine) } } } if ($AnalyzerData.Count -gt $LastX -and $LastX -gt 0) { $SyncHash.host.ui.WriteVerboseLine('Trim Data 1') $AnalyzerData = $AnalyzerData.GetRange($AnalyzerData.Count - $LastX, $LastX) } $SyncHash.host.ui.WriteVerboseLine('New Points: ' + $AnalyzerData.Count) if ($AnalyzerData.Count -gt 0) { if ($AnalyzerDataLength -eq 0 -and $AnalyzerData.Count -gt 1) { $ChartDataAction1 = [Action]{ $SyncHash.Chart.Series['Bitrate (Mbits/sec)'].Points.DataBindXY($AnalyzerData.Interval, $AnalyzerData.Bitrate) $SyncHash.Chart.Series['Transfer (MBytes)'].Points.DataBindXY($AnalyzerData.Interval, $AnalyzerData.Transfer) $SyncHash.host.ui.WriteVerboseLine('Show Data: ' + ($SyncHash.Chart.Series['Transfer (MBytes)'].Points.Count | Out-String)) } $SyncHash.Chart.Invoke($ChartDataAction1) } else { $ChartDataAction2 = [Action]{ while ($AnalyzerDataLength + $AnalyzerData.Count -gt $LastX -and $LastX -gt 0) { $SyncHash.Chart.Series['Bitrate (Mbits/sec)'].Points.RemoveAt(0) $SyncHash.Chart.Series['Transfer (MBytes)'].Points.RemoveAt(0) $Global:AnalyzerDataLength -- } foreach ($Point in $AnalyzerData) { $SyncHash.Chart.Series['Bitrate (Mbits/sec)'].Points.AddXY($Point.Interval, $Point.Bitrate) $SyncHash.Chart.Series['Transfer (MBytes)'].Points.AddXY($Point.Interval, $Point.Transfer) $SyncHash.host.ui.WriteVerboseLine('Add Data Point: ' + ($SyncHash.Chart.Series['Transfer (MBytes)'].Points.Count | Out-String)) } } $SyncHash.Chart.Invoke($ChartDataAction2) } $AnalyzerDataLength += $AnalyzerData.Count } else { $SyncHash.host.ui.WriteVerboseLine('Point Skipped') } $SyncHash.host.ui.WriteVerboseLine('Analyzer Loop End: ' + ($AnalyzerDataLength | Out-String)) } } function Stop-Analyzer { $SyncHash.host.ui.WriteVerboseLine('Stop-Analyzer') if ($SyncHash.AnalyzerRunspace.RunspaceStateInfo.State -eq 'Opened') { $SyncHash.host.ui.WriteVerboseLine('Stop-Analyzer Running') $SyncHash.AnalyzerRunspace.Close() $SyncHash.AnalyzerPowerShell.Dispose() } } function Set-IperfCommand { if ($SyncHash.ClientRadio.IsChecked) { $IperfMode = ' -c ' + $SyncHash.IpTextBox.Text $SyncHash.IpTextBox.IsEnabled = $true $IperfTime = ' -t ' + $SyncHash.TimeTextBox.Text $SyncHash.TimeTextBox.IsEnabled = $true } else { $IperfMode = ' -s' $SyncHash.IpTextBox.IsEnabled = $false $IperfTime = $null $SyncHash.TimeTextBox.IsEnabled = $false } if ($SyncHash.Version2Radio.IsChecked) { $IperfVersionParams = ' -y c' $Global:IperfVersion = 2 $Global:IperfExe = '.\iperf2.exe' } else { $IperfVersionParams = ' -f m --logfile ' + $SyncHash.CsvFilePathTextBox.Text $Global:IperfVersion = 3 $Global:IperfExe = '.\iperf3.exe' } $SyncHash.CommandTextBox.Text = $IperfExe + $IperfMode + $IperfTime + $IperfVersionParams + ' -i 1' } # UI $InputXml = @"