WttStudioUtils.psm1

Import-Module $PSScriptRoot\Logger.psm1
Import-Module $PSScriptRoot\PsHelper.psm1
Import-Module $PSScriptRoot\Utils.psm1

$script:WorkFlowCommandLine = Join-Path $env:WTTSTDIOATLAS "WorkFlowCommandLine.exe"
$script:WTTCL = Join-Path $env:WTTSTDIOATLAS "WTTCL.exe"
$script:WTTDimUpdate = Join-Path $env:WTTSTDIOATLAS  "WTTDimUpdate.exe"

function Test-WttInstalled() {
    if (-not (Test-Path $script:WorkFlowCommandLine)) {
        throw "Wtt studio install not found, cannot continue"
    }
}

$Global:DataStoreConnections = @{}
$Global:IdentityServer = "ATLASIdentity.redmond.corp.microsoft.com"
$Global:IdentityDb = "WTTIdentity"
$Script:WttInitialized = $false

function Initialize-Wtt {
    Test-WttInstalled

    if ($Script:WttInitialized) {
        return
    }

    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMAsset.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMBase.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMAssetConfig.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMIdentity.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMJobs.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMParameter.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMResource.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMSQLProvider.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMDimension.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS WTTOMStage.dll) -Scope Global
    Import-Module (Join-Path $env:WTTSTDIOATLAS ConditionalWorkflowParameterStageTemplate.dll) -Scope Global

    $Script:WttInitialized = $true
    
}

function Get-WttConnection {
    param(
        [Parameter(Mandatory = $true)][string] $DataStore
    )
    
    Initialize-Wtt

    $connection = $Global:DataStoreConnections[$DataStore]

    if ($connection -ne $null) {
        return $connection
    }

    $connInfo = New-Object -TypeName "Microsoft.DistributedAutomation.SqlIdentityConnectInfo" `
        -ArgumentList @($Global:IdentityServer , $Global:IdentityDb)
    $serviceName = [Microsoft.DistributedAutomation.Jobs.JobsRuntimeDataStore]::ServiceName
    $dsConnection = [Microsoft.DistributedAutomation.Enterprise]::Connect($DataStore, $serviceName, $connInfo)
    $Global:DataStoreConnections[$DataStore] = $dsConnection
    
    return $dsConnection
}

function Get-ActiveWttWorkFlowInstance {
    param(
        [Parameter(Mandatory = $true)][string]$DataStore,
        [Parameter(Mandatory = $false)][string]$InstanceId,
        [Parameter(Mandatory = $false)][string]$Name  
    )

    if ([String]::IsNullOrEmpty($InstanceId) -and [String]::IsNullOrEmpty($Name)) {
        throw "Get-ActiveWttWorkFlowInstance: Either InstanceId or Name must be specified"
    }

    Write-TraceLog "Get-ActiveWttWorkFlowInstance: DataStore: $DataStore, Name: $Name, InstanceId: $InstanceId"

    $query = New-Object Microsoft.DistributedAutomation.Query ([Microsoft.DistributedAutomation.Workflow.WorkflowInstance])

    if (-not [String]::IsNullOrEmpty($InstanceId)) {
        $query.AddExpression('Id', 'Equals', $InstanceId)
    }

    if (-not [String]::IsNullOrEmpty($Name)) {
        $query.AddExpression('Name', 'Equals', $Name)
    }

    $query.AddConjunction('And')
    $query.AddExpression('Status', 'Equals', 'Active')
    $dsConnection = Get-WttConnection -DataStore $DataStore
    $instance = $dsConnection.Query($query) | select -First 1

    if ($instance -eq $null) {
        Write-TraceLog "Get-ActiveWttWorkFlowInstance: No active workflow instance found"
        return $null
    }

    Write-TraceLog "Get-ActiveWttWorkFlowInstance: Active workflow instance found"
    return $instance
}

function Get-WttResultCollectionsByName {
    param(
        [Parameter(Mandatory = $true)][string]$DataStore,
        [Parameter(Mandatory = $true)][string]$Name    
    )

    $dsConnection = Get-WttConnection -DataStore $DataStore

    $query = New-Object -TypeName Microsoft.DistributedAutomation.Query -ArgumentList @([Microsoft.DistributedAutomation.Jobs.ResultSummary])
    $query.AddExpression("Name", [Microsoft.DistributedAutomation.QueryOperator]::Equals, $Name)

    try {
        $resColl = $dsConnection.Query($query)
    } catch {
        Write-TraceLog "Get-WttResultCollectionsByName: Failed to query result collections, error $_"
        return $null
    }

    if ($resColl.Count -eq 0) {
        return $null
    }
    $retval = @()
    foreach ($r in $resColl) {
        $retval += $r
    }
    return $retval
}

function Get-WttMachine() {

    param(
        [Parameter(Mandatory = $true)][string]$DataStore,
        [Parameter(Mandatory = $true)][string]$Name
    )

    $connection = Get-WttConnection -DataStore $DataStore
    $query = New-Object -TypeName Microsoft.DistributedAutomation.Query -ArgumentList @([Microsoft.DistributedAutomation.Asset.Resource])
    $e = [Microsoft.DistributedAutomation.QueryOperator]::Equals
    $query.AddExpression("Name", $e, $Name)

    $resColl = $connection.Query($query)

    return $resColl[0]

}

function Get-WttPoolForMachine() {
    param(
        [string]$DataStore,
        [string]$Name
    )

    $machine = Get-WttMachine -DataStore $DataStore -Name $Name
    if ($machine -eq $null) {
        Write-Error "No machine '$machineName' found"
    }

    $query = New-Object -TypeName Microsoft.DistributedAutomation.Query -ArgumentList @([Microsoft.DistributedAutomation.Asset.ResourcePool])
    $e = [Enum]::Parse([Microsoft.DistributedAutomation.QueryOperator], "Equals")
    $query.AddExpression("Id", $e, $machine.ResourcePoolId)
    
    $connection = Get-WttConnection -DataStore $DataStore
    $pools = $connection.Query($query);

    if ($pools -eq $null -or $pools.Count -eq 0) {
        return $null
    }

    return $pools[0].FullPath
}

function Test-IsRetriableError {
    param(
        [string]$Result,
        [switch]$RetryOnNull
    )

    if ($Result -eq $null -or [String]::IsNullOrEmpty($Result)) {
        return $RetryOnNull.IsPresent
    }

    if ($Result.Contains("A connection to the data store")) {
        # ERROR Message : A connection to the data store 'WTTIDENTITY' could not be established.
        return $true
    }

    return $false
}
function Invoke-WttWorkflow() {
    param(
        [int][parameter(Mandatory = $true)]$ID,
        [string][parameter(Mandatory = $true)]$MachinePool,
        [string[]][parameter(Mandatory = $true)]$Machines,
        [HashTable][parameter(Mandatory = $false)] $Params, # @ {"Param1" = "Value1"; "Param2" = "Value2"}
        [string][parameter(Mandatory = $true)]$MachineDataStore,
        [string][parameter(Mandatory = $true)]$JobDataStore,
        [bool][parameter(Mandatory = $false)]$WaitForExit,
        [string][parameter(Mandatory = $true)]$ResultCollection,
        [HashTable][parameter(Mandatory = $false)]$MachineRole = $null
    )


    if ($machines -eq $null -or $machines.Count -eq 0) {
        throw "Invoke-WttWorkflow: No machines specified"
    }
    
    $CommandArgs = " /IdentityServer:$Global:IdentityServer /IdentityDatabase:WTTIDENTITY /ID:$ID /DataStore:$JobDataStore /ResourceDataStore:$MachineDataStore /MachinePool:'$MachinePool' "

    
    if ($Params -ne $nul) {
        foreach ($param in $Params.Keys) {
            $paramVal = $Params[$param];
            $CommandArgs += " /CommonParam:$param='$paramVal'"        
        }
    }

    if ($WaitForExit) {
        $CommandArgs += " /runandwait"
    }
    else {
        $CommandArgs += " /run"

    }

    $CommandArgs += " /MailTo:" + (whoami).Split("\")[1] + "@microsoft.com"
    
    if ($Machines.Count -gt 1) {
        $MachineList = $Machines -join ","
    }
    else {
        $MachineList = $Machines[0]
    }

    $workFlowInstanceIds = @()

    foreach ($node in $Machines) {
        $rCol = $ResultCollection + "-" + $node
        $machineCmd = $CommandArgs + " /ResultCollection:$rCol"

        $roleSet = $false
        
        if ($MachineRole -ne $null) {
            $role = $MachineRole[$node]
            if ($role -ne $null) {
                $machineCmd = $machineCmd + " /MachineRole:$role=$node"
                $roleSet = $true
            }
        }

        if (-not $roleSet) {
            $machineCmd = $machineCmd + " /Machine:$node"
        }
        
        $cmd = '&' + "'" + $WorkFlowCommandLine + "'" + $machineCmd

        Write-TraceLog "Invoke-WttWorkflow: Command: $cmd"

        $bytes = [Text.Encoding]::Unicode.GetBytes($cmd)
        $encodedCommand = [Convert]::ToBase64String($bytes)
        $retryCount = 3
        $retriableError = $false
        while ($retryCount -gt 0) {
            
            if ($retriableError) {
                Start-Sleep 60
            } 

            $result = powershell.exe -noprofile -encodedCommand $encodedCommand

            Write-TraceLog "Invoke-WttWorkflow: Result: $result"
            $retriableError = Test-IsRetriableError -Result $result -RetryOnNull
            if ($retriableError) {
                Write-TraceLog "Invoke-WttWorkflow: Retriable error detected, will retry"
            }
            else {
                break
            }
    
            $retryCount--

        }
        
        $line = $result | Select-String "Created Workflow Instance ID:"
        if ($line -eq $null) {
            throw "Invoke-WttWorkflow: Failed to create workflow instance. Result: $result"
        }
        $id = $line.ToString().Split(":")[1].Trim()
        $workFlowInstanceIds += $id
    }

    return $workFlowInstanceIds
}


function Invoke-WttJob() {
    param(
        [string][parameter(Mandatory = $true)]$MachinePool,
        [string[]][parameter(Mandatory = $true)]$Machines,
        [string][parameter(Mandatory = $true)]$ID,
        [string][parameter(Mandatory = $true)]$MachineDataStore ,
        [string][parameter(Mandatory = $false)]$JobDataStore = $MachineDataStore,
        [HashTable][parameter(Mandatory = $false)] $Params, # @ {"Param1" = "Value1"; "Param2" = "Value2"}
        [string][parameter(Mandatory = $true)]$ResultCollection,
        [switch][parameter(Mandatory = $false)]$Wait,
        [switch][parameter(Mandatory = $false)]$ThrowOnFailure
    )

    $CommandArgs = " schedulejob /IdentityServer:$Global:IdentityServer /IdentityDatabase:WTTIDENTITY /SourceDataStore:$JobDataStore /DestinationDataStore:$MachineDataStore /MachinePool:'$MachinePool' "
    $CommandArgs += " /ResultCollection:$ResultCollection"
    $CommandArgs += " /RunAll:True"
    

    if ($Wait.IsPresent -or $ThrowOnFailure.IsPresent) {
        $CommandArgs += " /Wait"
    }

    if ($Params -ne $nul) {
        $tmpParamsAll = "";
        foreach ($param in $Params.Keys) {
            $tmpParamsAll += " /parameter " + '`{' + " /name:$param /value:$($Params[$param]) " + '`} '        
        }
        $CommandArgs += " /Job " + '`{' + " /Id:$ID $tmpParamsAll " + '`} '
    }
    else {
        $CommandArgs += " /JobId:$ID"
    }  
    
    if ($Machines.Count -gt 1) {
        $MachineList = $Machines -join ","
    }
    else {
        $MachineList = $Machines[0]
    }

    $CommandArgs += " /MachineList:$MachineList"
    $cmd = '&' + "'" + $WTTCL + "'" + $CommandArgs

    Write-TraceLog "Invoke-WttJob: Command: $cmd"

    $bytes = [Text.Encoding]::Unicode.GetBytes($cmd)
    $encodedCommand = [Convert]::ToBase64String($bytes)

    $retryCount = 3
    $retriableError = $false
    while ($retryCount -gt 0) {
        
        if ($retriableError) {
            Start-Sleep 60
        } 

        $result = powershell.exe -noprofile -encodedCommand $encodedCommand
        Write-TraceLog "Invoke-WttJob: Result: $result"
        $retriableError = Test-IsRetriableError -Result $result
        if ($retriableError) {
            Write-TraceLog "Invoke-WttJob: Retriable error detected, will retry"
        }
        else {
            break
        }

        $retryCount--
    }


    [array]$jobIdResults = $result | Select-String "Schedule Created with Id"    

    if ($jobIdResults -eq $null -or $jobIdResults.Count -eq 0) {
        throw "Invoke-WttJob: No results found"
    }

    [array]$scheduleIds = $jobIdResults | ForEach-Object { $_.ToString().Split(" ") | Select -Last 1 }
    
    if ($scheduleIds -eq $null -or $scheduleIds.Count -eq 0) {
        throw "Invoke-WttJob: No scheduleIds found"
    }

    if ($ThrowOnFailure) {
        $scheduleIds | ForEach-Object { 
            $success = Test-WttJobSuccess -ScheduleID $_ -MachineDataStore $MachineDataStore
            if (-not $success) {
                throw "Invoke-WttJob: Job $ID failed"
            }
        }
    }
}

function Test-WttJobSuccess {
    param(
        [string][parameter(Mandatory = $true)]$ScheduleID,
        [string][parameter(Mandatory = $true)]$MachineDataStore
    )

    $params = @(
        "schedulestatus",
        "/IdentityServer:ATLASIDENTITY",
        "/IdentityDatabase:WTTIDENTITY",
        "/ScheduleID:$ScheduleID",
        "/DataStore:$MachineDataStore"
    )

    $params = " " + ($params -Join " ")
    $cmd = '&' + "'" + $WTTCL + "'" + $params
    Write-TraceLog "Test-WttJobSuccess: Command: $cmd"


    $retryCount = 3
    $resultIdFound = $false
    while ($retryCount -gt 0) {
        
        if ($retriableError) {
            Start-Sleep 60
        } 

        $result = Invoke-PowershellCommand -cmd $cmd
        Write-TraceLog "Test-WttJobSuccess: Schedule $ScheduleID Results: $result"

        $line = 0;         
        foreach ($r in $result) { 
            if ($r.StartsWith("ResultID")) {
                $resultIdFound = $true
                break
            }
            elseif ((Test-IsRetriableError -Result $r)) {
                Write-TraceLog "Test-WttJobSuccess: Retrying $ScheduleID"
                $retriableError = $true
                break
            }
            
            $line++ 
        }

        if (-not $retriableError) {
            break
        }

        $retryCount--
    }
    
    if ($resultIdFound -eq $false) {
        throw "Test-WttJobSuccess: Failed to find ResultID in the output. Result: $result"
    }

    $resultLineNumber = $line + 2
    if ($result.Count -lt $resultLineNumber) {
        throw "Test-WttJobSuccess: Failed to find result in the output. Result: $result"
    }

    $resultLine = $result[$line + 2]

    $resultLineEntires = $resultLine.split((@(" ")), [StringSplitOptions]::RemoveEmptyEntries)

    if ($resultLineEntires.Count -lt 3) {
        throw "Test-WttJobSuccess: Failed to find result status in the output. ResultLine: $resultLine"
    }

    return ($resultLineEntires[2] -eq "Completed")

}

function Get-WttJobStatus {
    param(
        [string]$DataStore,
        [string]$ScheduleID
    )

    $CommandArgs = " schedulestatus /DataStore:$DataStore /ScheduleID:$ScheduleID"
    $cmd = '&' + "'" + $WTTCL + "'" + $CommandArgs
    Write-TraceLog "Get-WttJobStatus: Command: $cmd"

    $bytes = [Text.Encoding]::Unicode.GetBytes($cmd)
    $encodedCommand = [Convert]::ToBase64String($bytes)
    $result = powershell.exe -noprofile -encodedCommand $encodedCommand
    $status = $result | Select-String "Schedule status :" 
    $status.ToString().Split(":")[1].Trim()
}

function Wait-ForWttJob {

    param(
        [string]$DataStore,
        [string]$ScheduleID
    )

    $status = Get-WttJobStatus -DataStore $DataStore -ScheduleID $ScheduleID
    while ($true) {
        if ($status -eq "InProgress") {
            Write-TraceLog "Wait-ForWttJob: ScheduleID $ScheduleID"
            Start-Sleep -Seconds 30
            $status = Get-WttJobStatus -DataStore $DataStore -ScheduleID $ScheduleID
        }
        else {
            Write-TraceLog "Wait-ForWttJob: ScheduleID $ScheduleID completed with $status"
            break
        }
    }
}

function Wait-ForWttWorkflow {

    param(
        [string]$DataStore,
        [string]$ResultCollectionName,
        [int] $TotalExpectedJobs
    )

    Write-TraceLog "Wait-ForWttWorkflow: ResultCollectionName $ResultCollectionName"

    while ($true) {
        $resultCol = Get-WttResultCollectionsByName -DataStore $DataStore -Name $ResultCollectionName
            
        if ($resultCol -eq $null) {
            Write-TraceLog "Wait-ForWttWorkflow: ResultCollection $ResultCollectionName not found, will wait" 
            Start-Sleep -Seconds 60
            continue
        }

        $notComplete = $false
        foreach ($r in $resultCol) {
            Write-TraceLog "Wait-ForWttWorkflow: ResultCollection $($r.Name) Status $($r.ResultSummaryStatusId) Completed Jobs $($r.CompletedResults)"
            $o = ($r | Select-Object -Property InProgressResults, InvestigateResults, CancelledResults, CompletedResults, TotalResults, RuntimeLeft)
            Write-TraceLog "Wait-ForWttWorkflow: $o"

            if ($r.InvestigateResults -ne 0) {
                throw "Wait-ForWttWorkflow: $ResultCollectionName failed"
            }
                
            if ($TotalExpectedJobs -ne 0) {
                $percent = [Math]::Round(($r.CompletedResults / $TotalExpectedJobs) * 100, 0)
                Write-TraceLog "Wait-ForWttWorkflow: $percent% completed"
            }

            if ($r.ResultSummaryStatusId -ne "Completed") {
                $notComplete = $true
            }
            else {
                $instance = Get-ActiveWttWorkFlowInstance -DataStore $DataStore -Name $ResultCollectionName
                if ($instance -ne $null) {
                    Write-TraceLog "Wait-ForWttWorkflow: Active instance found, will wait for $ResultCollectionName"
                    $notComplete = $true
                }
                else {
                    Write-TraceLog "Wait-ForWttWorkflow: $ResultCollectionName completed"
                }
            }
        }

        if ($notComplete) {
            Write-TraceLog "Wait-ForWttWorkflow: Sleeping for 2 minutes"
            Start-Sleep -Seconds 120
        }
        else {
            break
        }        
    }
}
# SIG # Begin signature block
# MIIoDAYJKoZIhvcNAQcCoIIn/TCCJ/kCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBtcyJVqzZMZ24G
# 8PS2U95PCUw6vRR9iq6UIONmLfCRj6CCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGewwghnoAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCggZAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJKoZIhvcNAQkEMSIE
# IIHH1H986hnBUUIMojlELJfbblMe3In41mY3jyDZkOgMMEIGCisGAQQBgjcCAQwx
# NDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20wDQYJKoZIhvcNAQEBBQAEggEAT96G+JCTrC3Wquqsjg21RheXkXJcUHpj
# xmSBdRBvcHj04aOHE6VKnQIV9bQWJL/Tz2Ngl8JtATxTz0eJPZrZYaQY1gHgnjSz
# X+v5m+MfOpz0oXKfD52PV13/6HEdluPBlsNL7W2AYDCjqZuOgBAyGa0YTnDfvzUH
# +PPQ4RCyIs5ztGHAq96rkb+NZI1YdyAqhE00ikA0NOidzg40CFxOxQW1G7d/NTwd
# vyubtHy62qT+9aSsRd34/aXFZpLWikdFKnGQkryPylmuZhreEMbYlF/FrSN1ECVn
# y3jIEd6jfCEtmcVG9lSeAU9Te7wb8XCuBucSerycAUYXrrjnalxtVaGCF5QwgheQ
# BgorBgEEAYI3AwMBMYIXgDCCF3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJ
# YIZIAWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYB
# BAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCBHvJwHm4wnZojhiKaUKqMqOp1Sw8Ik
# FDtSU4G3wpvemgIGZ/W8zuEVGBMyMDI1MDQxMDAzMjI1Mi4zODhaMASAAgH0oIHR
# pIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYD
# VQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hp
# ZWxkIFRTUyBFU046RjAwMi0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBU
# aW1lLVN0YW1wIFNlcnZpY2WgghHqMIIHIDCCBQigAwIBAgITMwAAAgU8dWyCRIfN
# /gABAAACBTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg
# MjAxMDAeFw0yNTAxMzAxOTQyNDlaFw0yNjA0MjIxOTQyNDlaMIHLMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQg
# QW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046RjAw
# Mi0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZp
# Y2UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCSkvLfd7gF1r2wGdy8
# 5CFYXHUC8ywEyD4LRLv0WYEXeeZ0u5YuK7p2cXVzQmZPOHTN8TWqG2SPlUb+7Pld
# zFDDAlR3vU8piOjmhu9rHW43M2dbor9jl9gluhzwUd2SciVGa7f9t67tM3KFKRSM
# XFtHKF3KwBB7aVo+b1qy5p9DWlo2N5FGrBqHMEVlNyzreHYoDLL+m8fSsqMu/iYU
# qxzK5F4S7IY5NemAB8B+A3QgwVIi64KJIfeKZUeiWKCTf4odUgP3AQilxh48P6z7
# AT4IA0dMEtKhYLFs4W/KNDMsYr7KpQPKVCcC5E8uDHdKewubyzenkTxy4ff1N3g8
# yho5Pi9BfjR0VytrkmpDfep8JPwcb4BNOIXOo1pfdHZ8EvnR7JFZFQiqpMZFlO5C
# AuTYH8ujc5PUHlaMAJ8NEa9TFJTOSBrB7PRgeh/6NJ2xu9yxPh/kVN9BGss93MC6
# UjpoxeM4x70bwbwiK8SNHIO8D8cql7VSevUYbjN4NogFFwhBClhodE/zeGPq6y6i
# xD4z65IHY3zwFQbBVX/w+L/VHNn/BMGs2PGHnlRjO/Kk8NIpN4shkFQqA1fM08fr
# rDSNEY9VKDtpsUpAF51Y1oQ6tJhWM1d3neCXh6b/6N+XeHORCwnY83K+pFMMhg8i
# sXQb6KRl65kg8XYBd4JwkbKoVQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFHR6Wrs2
# 7b6+yJ3bEZ9o5NdL1bLwMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1Gely
# MF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lv
# cHMvY3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNy
# bDBsBggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9z
# b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBD
# QSUyMDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYB
# BQUHAwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQAOuxk47b1i
# 75V81Tx6xo10xNIr4zZxYVfkF5TFq2kndPHgzVyLnssw/HKkEZRCgZVpkKEJ6Y4j
# vG5tugMi+Wjt7hUMSipk+RpB5gFQvh1xmAEL2flegzTWEsnj0wrESplI5Z3vgf2e
# GXAr/RcqGjSpouHbD2HY9Y3F0Ol6FRDCV/HEGKRHzn2M5rQpFGSjacT4DkqVYmem
# /ArOfSvVojnKEIW914UxGtuhJSr9jOo5RqTX7GIqbtvN7zhWld+i3XxdhdNcflQz
# 9YhoFqQexBenoIRgAPAtwH68xczr9LMC3l9ALEpnsvO0RiKPXF4l22/OfcFffaph
# nl/TDwkiJfxOyAMfUF3xI9+3izT1WX2CFs2RaOAq3dcohyJw+xRG0E8wkCHqkV57
# BbUBEzLX8L9lGJ1DoxYNpoDX7iQzJ9Qdkypi5fv773E3Ch8A+toxeFp6FifQZyCc
# 8IcIBlHyak6MbT6YTVQNgQ/h8FF+S5OqP7CECFvIH2Kt2P0GlOu9C0BfashnTjod
# mtZFZsptUvirk/2HOLLjBiMjDwJsQAFAzJuz4ZtTyorrvER10Gl/mbmViHqhvNAC
# fTzPiLfjDgyvp9s7/bHu/CalKmeiJULGjh/lwAj5319pggsGJqbhJ4FbFc+oU5zf
# fbm/rKjVZ8kxND3im10Qp41n2t/qpyP6ETCCB3EwggVZoAMCAQICEzMAAAAVxedr
# ngKbSZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRp
# ZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4
# MzIyNVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qls
# TnXIyjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLA
# EBjoYH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrE
# qv1yaa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyF
# Vk3v3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1o
# O5pGve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg
# 3viSkR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2
# TPYrbqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07B
# MzlMjgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJ
# NmSLW6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6
# r1AFemzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+
# auIurQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3
# FQIEFgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl
# 0mWnG1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUH
# AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0
# b3J5Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMA
# dQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAW
# gBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8v
# Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRf
# MjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL
# /Klv6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu
# 6WZnOlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5t
# ggz1bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfg
# QJY4rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8s
# CXgU6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCr
# dTDFNLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZ
# c9d/HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2
# tVdUCbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8C
# wYKiexcdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9
# JZTmdHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDB
# cQZqELQdVTNYs6FwZvKhggNNMIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFt
# ZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkYwMDIt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# oiMKAQEwBwYFKw4DAhoDFQDVsH9p1tJn+krwCMvqOhVvXrbetKCBgzCBgKR+MHwx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p
# Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA66GM
# oTAiGA8yMDI1MDQxMDAwMTczN1oYDzIwMjUwNDExMDAxNzM3WjB0MDoGCisGAQQB
# hFkKBAExLDAqMAoCBQDroYyhAgEAMAcCAQACAjbeMAcCAQACAhJeMAoCBQDrot4h
# AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSCh
# CjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBAFxvNFtilxXyzR36Jp+HkHWX
# GumYux3ptpYp329283nVIwuzJhr8IvassJT8dZ3vWWye1hS16XlFFEdCDoeYwuVO
# VM2cnuDoUOjL+oyKRcVr5vKQ7laNnpIa72ksmNy1qux2l6mx/1sSHBQ6GfRUWIDa
# l8G5yR+xc4rzhyXRzHumFLrYtR5emkfQTcjQPQkTEgPmqss7lwJ7YZs+6NugqXst
# IPUnrGlqhnN5wLhUIda5kwLUg+CmrW6xYhKr9dvLuxwvEE5AMDTEmy82kvC6GaVr
# nNvGUHFWgIT6O2Tx8CylW7UK8SsRI/URKnI++ebfw6nmHF3Mz8o/ws5fQjgWa0Ux
# ggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA
# AgU8dWyCRIfN/gABAAACBTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDU95e60ggBEQSljnwd1Y2k
# fKxmH+99z0dedgxFQBb7UzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIIAN
# Az3ceY0umhdWLR2sJpq0OPqtJDTAYRmjHVkwEW9IMIGYMIGApH4wfDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIFPHVsgkSHzf4AAQAAAgUwIgQge1bP
# +vGEmNt9ZC0RK3VeWV5HvZJfXx4OWfpqh1o1D14wDQYJKoZIhvcNAQELBQAEggIA
# YGi1DAqd+3PK44ZAMn0ivCPslWvbiAZPkmCkyr1te/yqtlsrYdVdeNgCLeSZ2/F0
# di6UUmiWoJd0biWGLI4X72FxiaYoh1nuE1edBe/tN8CDSUupF6C/mHw6q5ITA15X
# kQsCDkmBMSnhtDHtU+SZRVtCZdNNgDGhkcN4CMIYrUHQBHbqmpzDKbGZ9E90wcwN
# cMiMrjo/atJVx2dsDlCTR5u4gdecLjUqWnc7bOcKKMPMaroctv1T9eCPpVSnpzmg
# MyxD3ddFX0ZKzgI/apRrzmVGG/mXboOdlVa/AR/0WTu9Gw1sPIm2ENGbG21EWAlM
# trAuumC0O1dEFJe0PXR6h/Xe/2uLBCuluAOH7cj41cKQjTIzTT2sU0PEHvEtlpiH
# a27Lw1tAroivSL1AeOGon7et1/NC2GBoHv+h0dAl0rqjJPjlkMX8drsfdQG43t2/
# 2FozwM9Sw0/lpPVRBhKCgFohpx0arUepY1SyChWIwzPT8t7RdmKrdz0LynIDpAEe
# hYXaFku4un+iFxEHFgTleA1EWoisucaxMw5whJpPRuNn2mwa14QLcxkhUQcsTQ/3
# v2DNGqSVxeU6D94528FwwqYcBX9Og182yQmRwZEHnPoeYmo9VLy4HNqjdRHuQye4
# EKd7mdzLxf+A8Yp20Vqg5oLLkAaRm4gdJcgv7HuqwBA=
# SIG # End signature block