Skip to content

Instantly share code, notes, and snippets.

@adbertram
Created October 15, 2014 16:31
Show Gist options
  • Save adbertram/3d820b6062c2b30cdaca to your computer and use it in GitHub Desktop.
Save adbertram/3d820b6062c2b30cdaca to your computer and use it in GitHub Desktop.
#Requires -Version 4
<#
.NOTES
Created on: 10/8/2014
Created by: Adam Bertram
Filename: Start-ModPollMonitor.ps1
Requirements: Share permissions for Everyone on destination share
The source computer account Modify NTFS rights on destination folder
.DESCRIPTION
.EXAMPLE
PS> .\Start-ModPollMonitor.ps1
.PARAMETER SourceFolderPath
This is the folder path on the local computer that will be monitored for
new files.
.PARAMETER DestinationHost
The destination host that the file will be copied to
.PARAMETER DestinationFileShare
The share name on the destination host where the file will be copied to
.PARAMETER FileExtension
This is the file extension that, when placed into SourceFolderPath path
will kick off the Modbus process. This defaults to PDF.
.PARAMETER ModPollFilePath
The file path where the modpoll.exe file is located
.PARAMETER FailureSourceFolderPath
The path to the local folder that all failed modpoll files will be placed.
This is only used if the file copy is unsuccessful.
.PARAMETER MonitorIntervalSecs
The number of seconds between script runs.
.PARAMETER LogFilePath
The file path to the log file that's generated with the progress of the
script. This defaults to C:\Windows\Temp\Start-ModPollMonitor.log
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter()]
[ValidateScript({
if (!(Test-Path -Path $_ -PathType Container)) {
throw "The source folder path '$($_)' cannot be found"
} else {
$true
}
})]
[string]$SourceFolderPath = 'C:\TestSourceFolder\folder',
[Parameter()]
[ValidateScript({
if (!(Test-Connection -ComputerName $_ -Quiet -Count 1)) {
throw "The destination host '$($_)' does not appear to be online"
} else {
$true
}
})]
[string]$DestinationHost = 'labdc.lab.local',
[Parameter()]
[string]$DestinationFileShare = 'plcscript',
[Parameter()]
[ValidatePattern('^\w{3}$')]
[string]$FileExtension = 'pdf',
[ValidateScript({
if (!(Test-Path -Path $_ -PathType Leaf)) {
throw "Modpoll.exe cannot be found at '$($_)'"
} else {
$true
}
})]
[string]$ModPollFilePath = 'C:\Users\administrator.LAB\Dropbox\Side Business\GitHubRepos\MiscScripts\PLCScript\Source\modpoll.exe',
[ValidateScript({
if (!(Test-Path -Path $_ -PathType Container)) {
throw "The failure source folder path '$($_)' cannot be found"
} else {
$true
}
})]
[string]$FailureSourceFolderPath = 'C:\FailureSourceFolder',
[Parameter()]
[int]$MonitorIntervalSecs = 10,
[Parameter()]
[string]$LogFilePath = "$([environment]::GetEnvironmentVariable('TEMP','Machine'))\Start-ModPollMonitor.log"
)
begin {
function Write-Log {
<#
.SYNOPSIS
This function creates or appends a line to a log file
.DESCRIPTION
This function writes a log line to a log file
.PARAMETER Message
The message parameter is the log message you'd like to record to the log file
.PARAMETER LogLevel
The logging level is the severity rating for the message you're recording.
You have 3 severity levels available; 1, 2 and 3 from informational messages
for FYI to critical messages. This defaults to 1.
.EXAMPLE
PS C:\> Write-Log -Message 'Value1' -LogLevel 'Value2'
This example shows how to call the Write-Log function with named parameters.
#>
[CmdletBinding()]
param (
[Parameter(
Mandatory = $true)]
[string]$Message,
[Parameter()]
[ValidateSet(1, 2, 3)]
[int]$LogLevel = 1
)
try {
$TimeGenerated = "$(Get-Date -Format HH:mm:ss).$((Get-Date).Millisecond)+000"
## Build the line which will be recorded to the log file
$Line = '{2} {1}: {0}'
$LineFormat = $Message, $TimeGenerated, (Get-Date -Format MM-dd-yyyy)
$Line = $Line -f $LineFormat
Add-Content -Value $Line -Path $LogFilePath
} catch {
Write-Error $_.Exception.Message
}
}
function Convert-ToUncPath($LocalFolderPath, $Computername) {
$RemoteFolderPathDrive = ($LocalFolderPath | Split-Path -Qualifier).TrimEnd(':')
"\\$Computername\$RemoteFolderPathDrive`$$($LocalFolderPath | Split-Path -NoQualifier)"
}
function Send-PollToModbusSlave ($PollNumber,$RegisterNumber) {
Write-Log -Message "Polling the Modbus slave with poll number $PollNumber"
$Result = Start-Process -FilePath $ModPollFilePath -ArgumentList "-r$RegisterNumber localhost $PollNumber" -Wait -NoNewWindow -PassThru
Start-Sleep -Seconds 2
if ($Result.ExitCode -ne 0) {
Write-Log -Message 'Poll to modbus slave failed' -LogLevel '3'
$false
} else {
Write-Log -Message 'Poll to modbus slave succeeded'
$true
}
}
function Validate-ModbusSlaveRunning {
Write-Log -Message 'Validating the Modbus slave is running'
$Result = Start-Process -FilePath $ModPollFilePath -ArgumentList "-r1 localhost $((get-date).Second)" -Wait -NoNewWindow -PassThru
Start-Sleep -Seconds 2
if ($Result.ExitCode -ne 0) {
Write-Log -Message 'Modbus slave is not running' -LogLevel '2'
$false
} else {
Write-Log -Message 'Modbus slave is running'
$true
}
}
function Start-ModbusSlave {
if (Test-Path 'C:\Program Files (x86)\Everest\Tools\PeakHmiMBTCPSlave.exe') {
Start-Process -FilePath 'C:\Program Files (x86)\Everest\Tools\PeakHmiMBTCPSlave.exe' -NoNewWindow
} elseif (Test-Path 'C:\Program Files\Everest\Tools\PeakHmiMBTCPSlave.exe') {
Start-Process -FilePath 'C:\Program Files\Everest\Tools\PeakHmiMBTCPSlave.exe' -NoNewWindow
}
Write-Log -Message 'Modbus slave started'
if (!(Validate-ModbusSlaveRunning)) {
throw 'Attempted to start Modbus slave but failed.'
} else {
Write-Log -Message 'Successfully started modbus slave'
$true
}
}
function Start-FileCopyProcess ($FilePath, $DestUncFolderPath) {
Write-Log -Message "Starting file copy of '$FilePath' to '$DestUncFolderPath'"
$SourceHash = (Get-FileHash $FilePath).Hash
Write-Log -Message "Source hash for '$FilePath' is '$SourceHash'"
Copy-Item -Path $FilePath -Destination $DestUncFolderPath
$DestUncFilePath = "$DestUncFolderPath\$($FilePath | Split-Path -Leaf)"
$DestHash = (Get-FileHash $DestUncFilePath).Hash
Write-Log -Message "Dest hash for '$DestUncFilePath' is '$DestHash'"
if ($SourceHash -ne $DestHash) {
Write-Log -Message "Source and destination file hash differ post file-copy. Copy failed." -LogLevel '2'
Write-Log -Message "Moving $FilePath to $FailureSourceFolderPath" -LogLevel '2'
Move-Item -Path $FilePath -Destination $FailureSourceFolderPath -Force
} else {
Write-Log -Message "Successfully copied file '$FilePath' to '$DestUncFolderPath' after 1 try"
Send-PollToModbusSlave -PollNumber 2 -RegisterNumber 1
Write-Log -Message "Removing the file '$FilePath'"
Remove-Item $FilePath -Force
}
}
function New-FileMonitor {
$WmiEventFilterQuery = "
SELECT * FROM __InstanceCreationEvent WITHIN $MonitorIntervalSecs
WHERE targetInstance ISA 'CIM_DataFile'
AND targetInstance.Drive = `"$WmiEventSourceDrive`"
AND targetInstance.Path = `"$($WmiEventSourceFolderPath.Replace('\','\\'))`"
AND targetInstance.Extension = `"$FileExtension`""
$WmiFilterParams = @{
'Class' = '__EventFilter'
'Namespace' = 'root\subscription'
'Arguments' = @{ Name = 'NewFile'; EventNameSpace = 'root\cimv2'; QueryLanguage = 'WQL'; Query = $WmiEventFilterQuery }
}
$WmiEventFilterPath = Set-WmiInstance @WmiFilterParams
$WmiConsumerParams = @{
'Class' = 'ActiveScriptEventConsumer'
'Namespace' = 'root\subscription'
'Arguments' = @{ Name = 'CopyFile'; ScriptFileName = $WmiEventLaunchScriptFilePath; ScriptingEngine = 'VBscript' }
}
$WmiConsumer = Set-WmiInstance @WmiConsumerParams
$WmiFilterConsumerParams = @{
'Class' = '__FilterToConsumerBinding'
'Namespace' = 'root\subscription'
'Arguments' = @{ Filter = $WmiEventFilterPath; Consumer = $WmiConsumer }
}
Set-WmiInstance @WmiFilterConsumerParams | Out-Null
}
function Validate-IsFileMonitorCreated {
Get-WmiObject -Namespace 'root\subscription' -Class __FilterToConsumerBinding -Filter { Consumer = 'ActiveScriptEventConsumer.Name="CopyFile"' }
}
try {
$WorkingDir = $MyInvocation.MyCommand.Path | Split-Path -Parent
$WmiEventLaunchScriptFilePath = Get-ChildItem $WorkingDir -Filter *.vbs | select -ExpandProperty fullname
$WmiEventSourceFolderPath = "$($SourceFolderPath | Split-Path -NoQualifier)\"
$WmiEventSourceDrive = $SourceFolderPath | Split-Path -Qualifier
if ($WmiEventLaunchScriptFilePath -is [array]) {
throw "Multiple VBS files located in '$WorkingDir'. Only a single launch VBS should exist"
}
} catch {
Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3'
exit
}
}
process {
try {
if (!(Validate-ModbusSlaveRunning)) {
Write-Log -Message 'Modbus slave not running. Attempting to start'
Start-ModbusSlave
}
if (!(Validate-IsFileMonitorCreated)) {
Write-Log -Message 'The file copy monitor has not been created yet. Creating now'
New-FileMonitor
#} elseif () {
# Write-Warning 'The file copy process is still running. Skipping this time'
# exit
}
Send-PollToModbusSlave -PollNumber (Get-Date).Seconds -RegisterNumber 2
$SourceFiles = Get-ChildItem $SourceFolderPath -Filter "*.$FileExtension" -File
if ($SourceFiles) {
Write-Log -Message "Found $($SourceFiles.Count) files to attempt to copy."
$DestUncFolderPath = "\\$DestinationHost\$DestinationFileShare"
Write-Log -Message "Destination folder path is '$DestUncFolderPath'"
if (!(Test-Path $DestUncFolderPath)) {
throw "Destination folder path '$DestUncFolderPath' does not exist on remote computer"
}
if (!(Send-PollToModbusSlave -PollNumber 1 -RegisterNumber 1)) {
throw "Failed to poll modbus slave with poll number 1"
}
$SourceFiles | foreach {
Start-FileCopyProcess -DestUncFolderPath $DestUncFolderPath -FilePath $_.FullName
}
} else {
Write-Log -Message "No $FileExtension files to process yet"
}
$FailedFiles = Get-ChildItem $FailureSourceFolderPath -File
if ($FailedFiles) {
Write-Log -Message "Found $($FailedFiles.Count) failed file to attempt to copy."
foreach ($File in $FailedFiles) {
Start-FileCopyProcess -DestUncFolderPath $DestUncFolderPath -FilePath $File.FullName
}
$FailedFiles = Get-ChildItem $FailureSourceFolderPath -File
if (!$FailedFiles) {
Write-Log -Message "Successfully copied all failed files from '$FailureSourceFolderPath'"
if (!(Send-PollToModbusSlave -PollNumber 2 -RegisterNumber 1)) {
throw "Failed to poll modbus slave with poll number 2"
}
} else {
throw "Failed to copy all failed files from '$FailureSourceFolderPath'"
}
} else {
Write-Log -Message 'No previously failed file copies to process'
}
} catch {
Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3'
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment