PowerShell Toggle Button for Background Job Report Generation

28 views Asked by At

i would like create a Generate-SummaryReports-CalculateAtIntervals toggle button which it is a background job that can be like a switch that can be turn on and off which allow the Generate-SummaryReports-CalculateAtIntervals be run like an background app but it still can allow user to do choose function .But currently the issue im facing is no matter how i trigger the Function Toggle-ReportGeneration it just wont trigger Generate-SummaryReports-CalculateAtIntervals to run , but it will show "Report generation is now turned ON.1" and "Report generation is now turned ON.3". Is there any way i can fix it or solve the issue

Function Toggle-ReportGeneration

$global:reportGenerationEnabled = $false
$global:reportJob = $null

# Function to toggle report generation on or off
Function Toggle-ReportGeneration {

    # Invert the current state of reportGenerationEnabled
    $global:reportGenerationEnabled = -not $global:reportGenerationEnabled

    if ($global:reportGenerationEnabled) {
        if ($global:reportJob -eq $null -or $global:reportJob.State -eq 'Completed') {
            Write-Host "Report generation is now turned ON.1"
            # If report generation is turned on, start the background job
            $global:reportJob = Start-Job -ScriptBlock {
                # Call the function directly within the background job
                param($baselineFilePath)
                while ($global:reportGenerationEnabled) {
                    Generate-SummaryReports-CalculateAtIntervals -baselineFilePath $baselineFilePath
                    Write-Host "Report generation is now turned ON.2"
                    Start-Sleep -Seconds 1  # Add a delay to prevent the loop from running too quickly
                }
            } -ArgumentList $global:baselineFilePath

            # Display the message outside the background job's script block
            Write-Host "Report generation is now turned ON.3"
        } else {
            Write-Host "Report generation is already running."
        }
    } else {
        Write-Host "Report generation is now turned OFF."
        # If report generation is turned off, stop the background job
        if ($global:reportJob -ne $null) {
            $global:reportJob | Stop-Job -PassThru | Remove-Job
            $global:reportJob = $null
        }
    }
}
Generate-SummaryReports-CalculateAtIntervals
Function Generate-SummaryReports-CalculateAtIntervals {
    param (
        [string]$baselineFilePath,
        [int]$lowSensitivityInterval = 15,     # Time interval for low sensitivity in seconds
        [int]$mediumSensitivityInterval = 10,  # Time interval for medium sensitivity in seconds
        [int]$highSensitivityInterval = 5      # Time interval for high sensitivity in seconds
    )
    Write-Host "Inside Generate-SummaryReports-CalculateAtIntervals function"

    # Define start time
    $startTime = Get-Date
    Write-host "1"
    # Infinite loop for continuous background job
    while ($true) {
        $currentTime = Get-Date

        # Calculate elapsed time since start
        $elapsedTime = $currentTime - $startTime

        # Calculate the number of intervals completed for each sensitivity level
        $lowIntervalsCompleted = [math]::Floor($elapsedTime.TotalSeconds / $lowSensitivityInterval)
        $mediumIntervalsCompleted = [math]::Floor($elapsedTime.TotalSeconds / $mediumSensitivityInterval)
        $highIntervalsCompleted = [math]::Floor($elapsedTime.TotalSeconds / $highSensitivityInterval)

        # Calculate the next scheduled time for each sensitivity level
        $nextLowReportTime = $startTime.AddSeconds(($lowIntervalsCompleted + 1) * $lowSensitivityInterval)
        $nextMediumReportTime = $startTime.AddSeconds(($mediumIntervalsCompleted + 1) * $mediumSensitivityInterval)
        $nextHighReportTime = $startTime.AddSeconds(($highIntervalsCompleted + 1) * $highSensitivityInterval)

        # Find the next earliest report time
        $nextReportTime = $nextLowReportTime
        if ($nextMediumReportTime -lt $nextReportTime) {
            $nextReportTime = $nextMediumReportTime
        }
        if ($nextHighReportTime -lt $nextReportTime) {
            $nextReportTime = $nextHighReportTime
        }

        # Wait until the next report time for the earliest sensitivity level
        $waitTime = $nextReportTime - $currentTime
        if ($waitTime.TotalMilliseconds -gt 0) {
            Start-Sleep -Milliseconds $waitTime.TotalMilliseconds
        }

        # Generate summary report for the sensitivity level that has reached its interval
        if ($nextReportTime -eq $nextLowReportTime) {
            # Log the start of the report generation
            Write-Output "Generating Low sensitivity summary report at $(Get-Date)" | Out-File -Append -FilePath "summary_reports.log"
            Generate-SummaryReport-AtIntervals -baselineFilePath $baselineFilePath -sensitivity "Low"
            # Log the completion of the report generation
            Write-Output "Low sensitivity summary report generation complete at $(Get-Date)" | Out-File -Append -FilePath "summary_reports.log"
        }
        if ($nextReportTime -eq $nextMediumReportTime) {
            # Log the start of the report generation
            Write-Output "Generating Medium sensitivity summary report at $(Get-Date)" | Out-File -Append -FilePath "summary_reports.log"
            Generate-SummaryReport-AtIntervals -baselineFilePath $baselineFilePath -sensitivity "Medium"
            # Log the completion of the report generation
            Write-Output "Medium sensitivity summary report generation complete at $(Get-Date)" | Out-File -Append -FilePath "summary_reports.log"
        }
        if ($nextReportTime -eq $nextHighReportTime) {
            # Log the start of the report generation
            Write-Output "Generating High sensitivity summary report at $(Get-Date)" | Out-File -Append -FilePath "summary_reports.log"
            Generate-SummaryReport-AtIntervals -baselineFilePath $baselineFilePath -sensitivity "High"
            # Log the completion of the report generation
            Write-Output "High sensitivity summary report generation complete at $(Get-Date)" | Out-File -Append -FilePath "summary_reports.log"
        }
    }
}
1

There are 1 answers

0
Santiago Squarzon On

The main issue with your code is the use of Start-Job, jobs are out of process so, it wouldn't be possible to see the $global:reportGenerationEnabled being updated to stop that while loop. The next issue is that the job would never know what Generate-SummaryReports-CalculateAtIntervals or Generate-SummaryReport-AtIntervals are because of what is mentioned before. You can use a runspace to accomplish this task as these run in the same process in a different thread, however your code will require many modifications. I'll share a minimal example of how your code can work excluding the functionally of the functions mentioned before.

$iss = [initialsessionstate]::CreateDefault2()
$iss.Commands.Add(
    [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new(
        'Generate-SummaryReports-CalculateAtIntervals',
        'definition for the function here'))
$iss.Commands.Add(
    [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new(
        'Generate-SummaryReport-AtIntervals',
        'definition for the function here'))

Both functions must be passed to the runspace scope one way or the other, there are many different ways to do this, the above adds the functions to the runspace via its initial session state, you could however for simplicity, define them inside the script itself (inside .AddScript).

$reportGenerationEnabled = [System.Threading.ManualResetEventSlim]::new()
$reportJob = $null

# Function to toggle report generation on or off
function Toggle-ReportGeneration {
    # Invert the current state of reportGenerationEnabled
    if (-not $reportGenerationEnabled.IsSet) {
        $reportGenerationEnabled.Set()
    }
    else {
        $reportGenerationEnabled.Reset()
    }

    if ($reportGenerationEnabled.IsSet) {
        if (-not $reportJob -or $reportJob.iasyncresult.IsCompleted) {
            Write-Host 'Report generation is now turned ON.1'
            $iss = [initialsessionstate]::CreateDefault2()
            $iss.Commands.Add(
                [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new(
                    'Generate-SummaryReports-CalculateAtIntervals',
                    'definition for the function here'))
            $iss.Commands.Add(
                [System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new(
                    'Generate-SummaryReport-AtIntervals',
                    'definition for the function here'))

            $rs = [runspacefactory]::CreateRunspace($Host, $iss)
            $rs.Open()
            $rs.SessionStateProxy.SetVariable(
                'reportGenerationEnabled',
                $reportGenerationEnabled)

            $ps = [powershell]::Create().AddScript{
                # Call the function directly within the background job
                param($baselineFilePath)

                while ($reportGenerationEnabled.IsSet) {
                    Get-Random              # some output data for testing
                    Write-Host 'Report generation is now turned ON.2'
                    Start-Sleep -Seconds 1  # Add a delay to prevent the loop from running too quickly
                }
            }.AddParameter('baselineFilePath', $baselineFilePath)

            $ps.Runspace = $rs

            $script:reportJob = @{
                instance     = $ps
                iasyncresult = $ps.BeginInvoke()
            }

            # Display the message outside the background job's script block
            Write-Host 'Report generation is now turned ON.3'
        }
        # this condition will never be met with the current `while ($true)` implementaton
        else {
            Write-Host 'Report generation is already running.'
        }
    }
    else {
        Write-Host 'Report generation is now turned OFF.'
        # If report generation is turned off, stop the background job
        if ($reportJob) {
            try {
                $reportJob.instance.EndInvoke($reportJob.iasyncresult)
                if ($reportJob.instance.HadErrors) {
                    foreach ($e in $reportJob.instance.Streams.Error) {
                        Write-Error $e
                    }
                }
                $script:reportJob = $null
            }
            finally {
                if ($rs) {
                    $rs.Dispose()
                }
                if ($ps) {
                    $ps.Dispose()
                }
            }
        }
    }
}

Toggle-ReportGeneration