Public/Start-tpcMonitor.ps1

function Start-tpcMonitor {
<#
    .SYNOPSIS
 
    .DESCRIPTION
        Starts real-time performance counter monitoring for a single local or remote system.
 
    .PARAMETER ConfigName
        Name of the configuration template (without 'tpc_' prefix and '.json' extension).
        Cannot be combined with ConfigPath.
 
    .PARAMETER ConfigPath
        Absolute path to a JSON configuration file (pattern 'tpc_*.json').
        Cannot be combined with ConfigName.
 
    .PARAMETER ComputerName
        DNS name of the remote computer to monitor.
 
    .PARAMETER Credential
        PSCredential for remote authentication. Only applicable with ComputerName.
 
    .PARAMETER UpdateInterval
        Seconds between counter updates. Default: 1.
 
    .PARAMETER Tui
        (Beta) Launches an interactive Terminal GUI (Terminal.Gui) instead of the scrolling console output.
        Shows a live table and 3-row sparklines per counter with Pause / Sparklines-toggle / Quit buttons.
 
    .PARAMETER ExportCsv
        Enables CSV export of counter values after each batch cycle (append mode, long format).
 
    .PARAMETER CsvPath
        Directory path for the CSV export file.
        Default: Desktop.
 
    .PARAMETER ExportHtml
        Enables HTML report export after each batch cycle using PSWriteHTML.
        The report contains a counter overview table, a combined chart and individual charts per counter.
 
    .PARAMETER HtmlPath
        Directory path for the HTML report file.
        Default: Desktop.
 
    .PARAMETER HtmlGroupBy
        Controls how individual tabs are grouped in the HTML report.
        'Counter' (default): one tab per counter type, series per host — best for comparing hosts.
        'Host': one tab per host, series per counter — best for viewing a single host's metrics.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigName "CPU"
 
        Starts local CPU monitoring using the built-in CPU template.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigPath "C:\MyConfigs\tpc_CustomCPU.json"
 
        Starts monitoring using a custom configuration file.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigName "Memory" -ComputerName "Server01" -Credential $cred -UpdateInterval 2
 
        Starts remote memory monitoring with 2-second intervals.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigName "CPU" -Tui
 
        (Beta) Starts CPU monitoring in the interactive Terminal GUI.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigName "CPU" -ExportCsv -CsvPath "C:\Exports"
 
        Starts CPU monitoring with CSV export to C:\Exports\psTPC_history.csv.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigName "CPU" -ExportHtml
 
        Starts CPU monitoring with HTML report export to the Desktop after each cycle.
 
    .EXAMPLE
        Start-tpcMonitor -ConfigName "CPU" -ExportHtml -HtmlPath "C:\Reports"
 
        Starts CPU monitoring with HTML report export to C:\Reports\psTPC_CPU_report.html.
    #>


    [CmdletBinding()]
    param(
        [Parameter(ParameterSetName = 'ConfigName',    Mandatory)]
        [Parameter(ParameterSetName = 'RemoteByName',  Mandatory)]
        [string]        $ConfigName,

        [Parameter(ParameterSetName = 'ConfigPath',    Mandatory)]
        [Parameter(ParameterSetName = 'RemoteByPath',  Mandatory)]
        [string]        $ConfigPath,

        [Parameter(ParameterSetName = 'RemoteByName',  Mandatory)]
        [Parameter(ParameterSetName = 'RemoteByPath',  Mandatory)]
        [string]        $ComputerName,

        [Parameter(ParameterSetName = 'RemoteByName')]
        [Parameter(ParameterSetName = 'RemoteByPath')]
        [pscredential]  $Credential = $null,

        [int]           $UpdateInterval = 1,

        [switch]        $Tui,

        [switch]        $ExportCsv,

        [string]        $CsvPath = [Environment]::GetFolderPath('Desktop'),

        [switch]        $ExportHtml,

        [string]        $HtmlPath = [Environment]::GetFolderPath('Desktop'),

        [ValidateSet('Counter', 'Host')]
        [string]        $HtmlGroupBy = 'Counter'
    )

    $configParams       = @{}
    $monitorType        = 'local'

    try {

        if ( ($PSCmdlet.ParameterSetName -in @('RemoteByName', 'RemoteByPath') -and $ComputerName -eq $env:COMPUTERNAME) -or # in case local computername to avoid self remoting
                ($PSCmdlet.ParameterSetName -in @('ConfigPath', 'ConfigName') ) ) {


            if ($PSCmdlet.ParameterSetName -in @('RemoteByName', 'RemoteByPath')) {
                Write-Host "ComputerName '$ComputerName' matches local host. Falling back to local monitoring." -ForegroundColor Yellow
                Start-Sleep -Seconds 1
            }

            $configParams['counterMap'] = $(Get-CounterMap)

            if ( $PSCmdlet.ParameterSetName -in @('ConfigPath', 'RemoteByPath') ) {

                if ( -not (Test-Path $ConfigPath) ) {
                    Write-Warning "Configuration file not found: $ConfigPath"
                    Return
                }

                $fileName = Split-Path $ConfigPath -Leaf
                if ( $fileName -notmatch '^tpc_.+\.json$' ) {
                    throw "Invalid configuration file name. File must follow the pattern 'tpc_*.json'. Found: $fileName"
                }

                Write-Host "Loading configuration from '$ConfigPath'..." -ForegroundColor Yellow

                $configParams['ConfigPath'] = $ConfigPath

            } elseif ( $PSCmdlet.ParameterSetName -in @('ConfigName', 'RemoteByName') ) {

                Write-Host "Loading configuration '$ConfigName'..." -ForegroundColor Yellow

                $configParams['ConfigName'] = $ConfigName

            }

        } elseif ( $PSCmdlet.ParameterSetName -in @('RemoteByName', 'RemoteByPath') -and $ComputerName -ne $env:COMPUTERNAME ) {

            $monitorType = 'remoteSingle'

            $configParams['ComputerName'] = $ComputerName

            Write-Host "Starting remote monitoring for $ComputerName " -ForegroundColor Yellow -NoNewline

            if ( $Credential ) {
                Write-Host "using $($Credential.UserName)." -ForegroundColor Yellow
                $configParams['Credential'] = $Credential
            } else {
                Write-Host "using $env:USERDOMAIN\$env:USERNAME ." -ForegroundColor Yellow
            }

            # Reachability is owned solely by Get-CounterMap (WinRM, TCP 5985): it fails fast and
            # throws if the host is unreachable, which the surrounding try/catch reports.
            $configParams['counterMap'] = $(Get-CounterMap @configParams)

            switch ( $PSCmdlet.ParameterSetName ) {
                'RemoteByName' { $configParams['ConfigName'] = $ConfigName }
                'RemoteByPath' { $configParams['ConfigPath'] = $ConfigPath }
            }
        }

        # Start monitoring
        $MonitoringParams = @{
            MonitorType     = $monitorType
            Config          = Get-CounterConfiguration @configParams
            UpdateInterval  = $UpdateInterval
            Tui             = $Tui
            ExportCsv       = $ExportCsv
            CsvPath         = $CsvPath
            ExportHtml      = $ExportHtml
            HtmlPath        = $HtmlPath
            HtmlGroupBy     = $HtmlGroupBy
        }

        Start-MonitoringLoop @MonitoringParams

    } catch [System.Management.Automation.HaltCommandException] {

        Write-Host "`n=== Monitoring stopped by user ===" -ForegroundColor Green

    } catch {

        Write-Host "`n=== ERROR ===" -ForegroundColor Red
        Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Yellow
        throw

    }

}