VBAF.Center.SignalAcquisition.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS VBAF-Center Phase 3 — Signal Acquisition .DESCRIPTION Connects to customer data sources and retrieves live signals. Supports REST API, WMI, CSV, Manual and Simulated input. Phase 14 — Signal Thresholds (GoodBelow / BadAbove per signal) Phase 15 — Signal Weights (1-5 importance per signal) Functions: New-VBAFCenterSignalConfig — define a signal source Get-VBAFCenterSignal — retrieve one live signal Get-VBAFCenterAllSignals — retrieve all signals for a customer Test-VBAFCenterSignalConfig — test a signal connection Get-VBAFCenterSignalStatus — show signal colours and threshold status Invoke-VBAFCenterThresholdCheck — show only Red and Yellow signals Set-VBAFCenterSignalThreshold — update thresholds without full reconfiguration #> # ============================================================ # SIGNAL CONFIG STORE # ============================================================ $script:SignalStorePath = Join-Path $env:USERPROFILE "VBAFCenter\signals" function Initialize-VBAFCenterSignalStore { if (-not (Test-Path $script:SignalStorePath)) { New-Item -ItemType Directory -Path $script:SignalStorePath -Force | Out-Null } } # ============================================================ # NEW-VBAFCENTERSIGNALCONFIG # ============================================================ function New-VBAFCenterSignalConfig { param( [Parameter(Mandatory)] [string] $CustomerID, [Parameter(Mandatory)] [string] $SignalName, [Parameter(Mandatory)] [string] $SignalIndex, # "Signal1"|"Signal2"|"Signal3" etc [Parameter(Mandatory)] [string] $SourceType, # "REST"|"WMI"|"CSV"|"Manual"|"Simulated" [string] $SourceURL = "", # REST endpoint [string] $JSONPath = "", # dot-notation path e.g. "current.wind_speed_10m" [string] $WMIClass = "", # WMI class name [string] $WMIProperty = "", # WMI property [string] $CSVPath = "", # CSV file path [string] $CSVColumn = "", # CSV column name [double] $RawMin = 0.0, # raw value minimum [double] $RawMax = 100.0, # raw value maximum [string] $Description = "", # Phase 14 — Signal Thresholds [double] $GoodBelow = -1, # raw value below this = Green (-1 = not set) [double] $BadAbove = -1, # raw value above this = Red (-1 = not set) # Phase 15 — Signal Weight [ValidateRange(1,5)] [int] $Weight = 3 # 1=low importance, 3=normal, 5=critical ) Initialize-VBAFCenterSignalStore $config = @{ CustomerID = $CustomerID SignalName = $SignalName SignalIndex = $SignalIndex SourceType = $SourceType SourceURL = $SourceURL JSONPath = $JSONPath WMIClass = $WMIClass WMIProperty = $WMIProperty CSVPath = $CSVPath CSVColumn = $CSVColumn RawMin = $RawMin RawMax = $RawMax Description = $Description GoodBelow = $GoodBelow BadAbove = $BadAbove Weight = $Weight CreatedDate = (Get-Date).ToString("yyyy-MM-dd") } $path = Join-Path $script:SignalStorePath "$CustomerID-$SignalIndex.json" $config | ConvertTo-Json -Depth 5 | Set-Content $path -Encoding UTF8 Write-Host "" Write-Host "Signal config saved!" -ForegroundColor Green Write-Host (" Customer : {0}" -f $CustomerID) -ForegroundColor White Write-Host (" Signal : {0} ({1})" -f $SignalName, $SignalIndex) -ForegroundColor White Write-Host (" Source : {0}" -f $SourceType) -ForegroundColor White Write-Host (" Raw range : {0} - {1}" -f $RawMin, $RawMax) -ForegroundColor White Write-Host (" Weight : {0}/5" -f $Weight) -ForegroundColor White if ($GoodBelow -ge 0 -or $BadAbove -ge 0) { $goodStr = if ($GoodBelow -ge 0) { "below $GoodBelow = Green" } else { "not set" } $badStr = if ($BadAbove -ge 0) { "above $BadAbove = Red" } else { "not set" } Write-Host (" Thresholds : Good {0} | Bad {1}" -f $goodStr, $badStr) -ForegroundColor Cyan } else { Write-Host " Thresholds : not configured (using normalised fallback)" -ForegroundColor DarkGray } Write-Host "" return $config } # ============================================================ # RESOLVE-VBAFCENTERSIGNALCOLOUR (internal helper) # ============================================================ function Resolve-VBAFCenterSignalColour { param( [double] $RawValue, [double] $Normalised, [double] $GoodBelow, [double] $BadAbove ) # Phase 14 — threshold-based colouring (preferred) if ($GoodBelow -ge 0 -or $BadAbove -ge 0) { if ($BadAbove -ge 0 -and $RawValue -gt $BadAbove) { return "Red" } if ($GoodBelow -ge 0 -and $RawValue -lt $GoodBelow) { return "Green" } return "Yellow" } # Fallback — normalised-based colouring (backwards compatible) if ($Normalised -gt 0.7) { return "Red" } if ($Normalised -gt 0.4) { return "Yellow" } return "Green" } # ============================================================ # GET-VBAFCENTERSIGNAL # ============================================================ function Get-VBAFCenterSignal { param( [Parameter(Mandatory)] [string] $CustomerID, [Parameter(Mandatory)] [string] $SignalIndex ) Initialize-VBAFCenterSignalStore $path = Join-Path $script:SignalStorePath "$CustomerID-$SignalIndex.json" if (-not (Test-Path $path)) { Write-Host "Signal config not found: $CustomerID $SignalIndex" -ForegroundColor Red return $null } $config = Get-Content $path -Raw | ConvertFrom-Json [double] $rawValue = 0.0 switch ($config.SourceType) { "REST" { try { $response = Invoke-RestMethod -Uri $config.SourceURL -Method GET -ErrorAction Stop if ($config.JSONPath -and $config.JSONPath -ne "") { $parts = $config.JSONPath -split "\." $value = $response foreach ($part in $parts) { $value = $value.$part } $rawValue = [double] $value } else { $rawValue = [double] $response } } catch { Write-Host "REST call failed: $($_.Exception.Message)" -ForegroundColor Red $rawValue = 0.0 } } "WMI" { try { $wmi = Get-WmiObject -Class $config.WMIClass -ErrorAction Stop $rawValue = [double] ($wmi | Select-Object -First 1).$($config.WMIProperty) } catch { Write-Host "WMI call failed: $($_.Exception.Message)" -ForegroundColor Red $rawValue = 0.0 } } "CSV" { try { $csv = Import-Csv $config.CSVPath -ErrorAction Stop $rawValue = [double] ($csv | Select-Object -Last 1).$($config.CSVColumn) } catch { Write-Host "CSV read failed: $($_.Exception.Message)" -ForegroundColor Red $rawValue = 0.0 } } "Manual" { Write-Host ("Enter value for {0} (raw range {1}-{2}): " -f $config.SignalName, $config.RawMin, $config.RawMax) -NoNewline -ForegroundColor Yellow $rawValue = [double] (Read-Host) } "Simulated" { [double] $range = $config.RawMax - $config.RawMin $rawValue = $config.RawMin + (Get-Random -Minimum 0 -Maximum 100) / 100.0 * $range } } # Normalise to 0.0-1.0 [double] $range = $config.RawMax - $config.RawMin [double] $normalised = if ($range -gt 0) { ($rawValue - $config.RawMin) / $range } else { 0.0 } $normalised = [Math]::Max(0.0, [Math]::Min(1.0, $normalised)) # Phase 14 — resolve signal colour from thresholds $goodBelow = if ($null -ne $config.GoodBelow) { [double] $config.GoodBelow } else { -1 } $badAbove = if ($null -ne $config.BadAbove) { [double] $config.BadAbove } else { -1 } $colour = Resolve-VBAFCenterSignalColour -RawValue $rawValue -Normalised $normalised ` -GoodBelow $goodBelow -BadAbove $badAbove # Phase 15 — read weight (default 3 if not stored) $weight = if ($null -ne $config.Weight -and $config.Weight -gt 0) { [int] $config.Weight } else { 3 } $result = [PSCustomObject] @{ CustomerID = $CustomerID SignalIndex = $SignalIndex SignalName = $config.SignalName RawValue = [Math]::Round($rawValue, 2) Normalised = [Math]::Round($normalised, 4) SignalColour = $colour Weight = $weight ThresholdActive = ($goodBelow -ge 0 -or $badAbove -ge 0) GoodBelow = $goodBelow BadAbove = $badAbove SourceType = $config.SourceType Timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") } return $result } # ============================================================ # GET-VBAFCENTERALLSIGNALS # ============================================================ function Get-VBAFCenterAllSignals { param( [Parameter(Mandatory)] [string] $CustomerID, [switch] $Simulate ) Initialize-VBAFCenterSignalStore $indices = @("Signal1","Signal2","Signal3","Signal4","Signal5","Signal6", "Signal7","Signal8","Signal9","Signal10") $results = @() Write-Host "" Write-Host "Live Signal Acquisition: $CustomerID" -ForegroundColor Cyan Write-Host (" {0,-10} {1,-25} {2,10} {3,10} {4,6} {5,8} {6}" -f ` "Index","Signal","Raw","Norm","Weight","Colour","Source") -ForegroundColor Yellow Write-Host (" {0}" -f ("-" * 80)) -ForegroundColor DarkGray foreach ($idx in $indices) { $path = Join-Path $script:SignalStorePath "$CustomerID-$idx.json" if (-not (Test-Path $path)) { continue } if ($Simulate) { $config = Get-Content $path -Raw | ConvertFrom-Json $config.SourceType = "Simulated" $config | ConvertTo-Json -Depth 5 | Set-Content $path -Encoding UTF8 } $signal = Get-VBAFCenterSignal -CustomerID $CustomerID -SignalIndex $idx if ($null -eq $signal) { continue } $consoleColour = switch ($signal.SignalColour) { "Red" { "Red" } "Yellow" { "Yellow" } default { "Green" } } $thresholdMark = if ($signal.ThresholdActive) { "*" } else { "" } Write-Host (" {0,-10} {1,-25} {2,10:F2} {3,10:F4} {4,6} {5,8} {6}" -f ` $signal.SignalIndex, $signal.SignalName, $signal.RawValue, $signal.Normalised, ("W{0}" -f $signal.Weight), ($signal.SignalColour + $thresholdMark), $signal.SourceType) -ForegroundColor $consoleColour $results += $signal } Write-Host "" Write-Host " * = threshold-based colour active" -ForegroundColor DarkGray # Phase 14 — flag any RED signals $redSignals = $results | Where-Object { $_.SignalColour -eq "Red" } if ($redSignals) { Write-Host "" Write-Host " ALERT — RED signals detected:" -ForegroundColor Red foreach ($r in $redSignals) { Write-Host (" {0} ({1}) — raw value {2}" -f ` $r.SignalName, $r.SignalIndex, $r.RawValue) -ForegroundColor Red } } Write-Host "" # Phase 15 — weighted average $weightedSum = 0.0 $weightTotal = 0 $simpleSum = 0.0 foreach ($r in $results) { $weightedSum += $r.Normalised * $r.Weight $weightTotal += $r.Weight $simpleSum += $r.Normalised } $simpleAvg = if ($results.Count -gt 0) { [Math]::Round($simpleSum / $results.Count, 4) } else { 0.0 } $weightedAvg = if ($weightTotal -gt 0) { [Math]::Round($weightedSum / $weightTotal, 4) } else { 0.0 } Write-Host (" Simple average (Phase 3 legacy) : {0:F4}" -f $simpleAvg) -ForegroundColor DarkGray Write-Host (" Weighted average (Phase 15 active): {0:F4}" -f $weightedAvg) -ForegroundColor Cyan Write-Host "" # Build VBAF input using weighted average logic $vbafInput = @() foreach ($r in $results) { $vbafInput += $r.Normalised } Write-Host (" VBAF input ready: [{0}]" -f ($vbafInput -join ", ")) -ForegroundColor Green Write-Host "" return @{ Signals = $results VBAFInput = [double[]] $vbafInput SimpleAvg = $simpleAvg WeightedAvg = $weightedAvg RedSignals = @($redSignals) YellowSignals = @($results | Where-Object { $_.SignalColour -eq "Yellow" }) } } # ============================================================ # GET-VBAFCENTERSIGNALSTATUS (Phase 14 — new) # ============================================================ function Get-VBAFCenterSignalStatus { <# .SYNOPSIS Shows full threshold status for all signals — Green / Yellow / Red. .EXAMPLE Get-VBAFCenterSignalStatus -CustomerID "TruckCompanyDK" #> param( [Parameter(Mandatory)] [string] $CustomerID ) Write-Host "" Write-Host "Signal Status: $CustomerID" -ForegroundColor Cyan Write-Host (" {0,-10} {1,-25} {2,10} {3,8} {4,10} {5,10} {6,8}" -f ` "Index","Signal","Raw","Colour","GoodBelow","BadAbove","Weight") -ForegroundColor Yellow Write-Host (" {0}" -f ("-" * 85)) -ForegroundColor DarkGray $result = Get-VBAFCenterAllSignals -CustomerID $CustomerID $greenCount = 0 $yellowCount = 0 $redCount = 0 foreach ($s in $result.Signals) { $consoleColour = switch ($s.SignalColour) { "Red" { "Red" } "Yellow" { "Yellow" } default { "Green" } } $goodStr = if ($s.GoodBelow -ge 0) { $s.GoodBelow.ToString() } else { "---" } $badStr = if ($s.BadAbove -ge 0) { $s.BadAbove.ToString() } else { "---" } Write-Host (" {0,-10} {1,-25} {2,10:F2} {3,8} {4,10} {5,10} {6,8}" -f ` $s.SignalIndex, $s.SignalName, $s.RawValue, $s.SignalColour, $goodStr, $badStr, ("W{0}" -f $s.Weight)) -ForegroundColor $consoleColour switch ($s.SignalColour) { "Green" { $greenCount++ } "Yellow" { $yellowCount++ } "Red" { $redCount++ } } } Write-Host "" Write-Host (" Summary: {0} Green {1} Yellow {2} Red" -f ` $greenCount, $yellowCount, $redCount) -ForegroundColor White if ($redCount -gt 0) { Write-Host " One or more signals are RED — consider raising action level." -ForegroundColor Red } elseif ($yellowCount -gt 0) { Write-Host " One or more signals are YELLOW — monitor closely." -ForegroundColor Yellow } else { Write-Host " All signals GREEN — situation normal." -ForegroundColor Green } Write-Host "" return $result } # ============================================================ # INVOKE-VBAFCENTERTHRESHOLDCHECK (Phase 14 — new) # ============================================================ function Invoke-VBAFCenterThresholdCheck { <# .SYNOPSIS Shows only signals currently in Red or Yellow state. Returns nothing if all signals are Green. .EXAMPLE Invoke-VBAFCenterThresholdCheck -CustomerID "TruckCompanyDK" #> param( [Parameter(Mandatory)] [string] $CustomerID ) $result = Get-VBAFCenterAllSignals -CustomerID $CustomerID $alerts = $result.Signals | Where-Object { $_.SignalColour -ne "Green" } Write-Host "" Write-Host "Threshold Check: $CustomerID" -ForegroundColor Cyan if (-not $alerts -or @($alerts).Count -eq 0) { Write-Host " All signals GREEN — no threshold alerts." -ForegroundColor Green Write-Host "" return } Write-Host (" {0} signal(s) require attention:" -f @($alerts).Count) -ForegroundColor Yellow Write-Host "" foreach ($s in $alerts) { $consoleColour = if ($s.SignalColour -eq "Red") { "Red" } else { "Yellow" } $badStr = if ($s.BadAbove -ge 0) { "BadAbove $($s.BadAbove)" } else { "" } $goodStr = if ($s.GoodBelow -ge 0) { "GoodBelow $($s.GoodBelow)" } else { "" } Write-Host (" [{0}] {1} ({2})" -f $s.SignalColour, $s.SignalName, $s.SignalIndex) -ForegroundColor $consoleColour Write-Host (" Raw value : {0} Threshold : {1} {2}" -f ` $s.RawValue, $goodStr, $badStr) -ForegroundColor $consoleColour Write-Host (" Weight : {0}/5 Normalised : {1:F4}" -f ` $s.Weight, $s.Normalised) -ForegroundColor DarkGray Write-Host "" } return $alerts } # ============================================================ # SET-VBAFCENTERSIGNALTHRESHOLD (Phase 14 — new) # ============================================================ function Set-VBAFCenterSignalThreshold { <# .SYNOPSIS Update GoodBelow, BadAbove and/or Weight for an existing signal without reconfiguring the full signal from scratch. .EXAMPLE Set-VBAFCenterSignalThreshold -CustomerID "TruckCompanyDK" -SignalIndex "Signal1" -GoodBelow 25 -BadAbove 40 Set-VBAFCenterSignalThreshold -CustomerID "TruckCompanyDK" -SignalIndex "Signal2" -Weight 5 #> param( [Parameter(Mandatory)] [string] $CustomerID, [Parameter(Mandatory)] [string] $SignalIndex, [double] $GoodBelow = -999, # -999 = not changing [double] $BadAbove = -999, # -999 = not changing [ValidateRange(1,5)] [int] $Weight = 0 # 0 = not changing ) Initialize-VBAFCenterSignalStore $path = Join-Path $script:SignalStorePath "$CustomerID-$SignalIndex.json" if (-not (Test-Path $path)) { Write-Host "Signal config not found: $CustomerID $SignalIndex" -ForegroundColor Red return } $config = Get-Content $path -Raw | ConvertFrom-Json if ($GoodBelow -ne -999) { $config.GoodBelow = $GoodBelow } if ($BadAbove -ne -999) { $config.BadAbove = $BadAbove } if ($Weight -gt 0) { $config.Weight = $Weight } $config | ConvertTo-Json -Depth 5 | Set-Content $path -Encoding UTF8 Write-Host "" Write-Host "Signal threshold updated!" -ForegroundColor Green Write-Host (" Customer : {0}" -f $CustomerID) -ForegroundColor White Write-Host (" Signal : {0} ({1})" -f $config.SignalName, $SignalIndex) -ForegroundColor White Write-Host (" GoodBelow : {0}" -f $config.GoodBelow) -ForegroundColor White Write-Host (" BadAbove : {0}" -f $config.BadAbove) -ForegroundColor White Write-Host (" Weight : {0}/5" -f $config.Weight) -ForegroundColor White Write-Host "" } # ============================================================ # TEST-VBAFCENTERSIGNALCONFIG # ============================================================ function Test-VBAFCenterSignalConfig { param( [Parameter(Mandatory)] [string] $CustomerID ) Write-Host "" Write-Host "Testing signal configuration for: $CustomerID" -ForegroundColor Yellow $result = Get-VBAFCenterAllSignals -CustomerID $CustomerID -Simulate if ($result.VBAFInput.Length -eq 0) { Write-Host "No signals configured yet!" -ForegroundColor Red Write-Host "Use New-VBAFCenterSignalConfig to add signals." -ForegroundColor Yellow } else { Write-Host "Signal test passed!" -ForegroundColor Green Write-Host (" Signals configured : {0}" -f $result.VBAFInput.Length) -ForegroundColor White Write-Host (" Simple average : {0:F4}" -f $result.SimpleAvg) -ForegroundColor White Write-Host (" Weighted average : {0:F4}" -f $result.WeightedAvg) -ForegroundColor White if ($result.RedSignals -and @($result.RedSignals).Count -gt 0) { Write-Host (" RED signals : {0}" -f @($result.RedSignals).Count) -ForegroundColor Red } } return $result } # ============================================================ # LOAD MESSAGE # ============================================================ Write-Host "VBAF-Center Phase 3 loaded [Signal Acquisition + Phase 14 Thresholds + Phase 15 Weights]" -ForegroundColor Cyan Write-Host " New-VBAFCenterSignalConfig — define signal source" -ForegroundColor White Write-Host " Get-VBAFCenterSignal — get one live signal" -ForegroundColor White Write-Host " Get-VBAFCenterAllSignals — get all signals" -ForegroundColor White Write-Host " Test-VBAFCenterSignalConfig — test configuration" -ForegroundColor White Write-Host " Get-VBAFCenterSignalStatus — show Green/Yellow/Red status" -ForegroundColor Cyan Write-Host " Invoke-VBAFCenterThresholdCheck — show only Red and Yellow alerts" -ForegroundColor Cyan Write-Host " Set-VBAFCenterSignalThreshold — tune thresholds and weights" -ForegroundColor Cyan Write-Host "" |