Private/Assert-BMSMessage.ps1
Function Assert-BMSMessage { [CmdletBinding(DefaultParameterSetName='Command')] param ( [Parameter(ValueFromPipeline)] [Parameter(Mandatory = $true, ParameterSetName = 'Command')]$Command ) begin { $Command = Invoke-CmdPreprocessor $Command #instance an ordered array to contain packaged instruction(s) $InstructionStack = New-Object System.Collections.Generic.List[System.Object] #instance the library $Library = $BMSInstructionSet.Command.PSObject.Copy() #define a private validation function Function MinMaxValidate{ param($instructionValue, $Book) if ([double]$Command.$Key -ge [double]$Book.Range.Min) { if ([double]$Command.$Key -le [double]$Book.Range.Max) { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + " <= " + $Book.Range.Max + "]: RangeMax Accepted") $thisMinMax.Max = $true } else { $thisMinMax.Max = $false Write-Verbose ("[MsgAssert]: [" + $Command.$Key + " !<= " + $Book.Range.Max + "]: RangeMax NOT Accepted") } Write-Verbose ("[MsgAssert]: [" + $Command.$Key + " >= " + $Book.Range.Min + "]: RangeMin Accepted") $thisMinMax.Min = $true } else { $thisMinMax.Min = $false Write-Verbose ("[MsgAssert]: [" + $Command.$Key + " !>= " + $Book.Range.Min + "]: RangeMin NOT Accepted") } Write-Verbose ("[MsgAssert]: [" + $Book.Return.Value + "]: Instruction type validated") return $thisMinMax } #validates instructions and formats the values (if any) to the correct format defined in the library, then returns an array of pscustomobjects Function ValidateInstructionStack { param($Command) $CommandCopy = @() foreach ($Key in $Command.Keys) { #get instruction book from library $Book = $null #clear the previous hex encoding $HexEncoded = $null #Clear previous handler count $HandlerCount = 0 $Book = ($Library | ?{$_.Instruction -eq $Key.ToUpper()}).PSObject.Copy() if (!$Book) { Write-Verbose ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Instruction is Unknown") #throw the book? Write-Warning ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Instruction is Unknown") return } else { Write-Verbose ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Instruction Validation Success") } Write-Verbose ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Instruction is Known") Write-Verbose ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: " + $Book.Name) switch ($Book.Return.Value) { Array { $HandlerCount = $HandlerCount + ($Book.Return.Unit.Array.Part | Sort-Object -Unique).Count } Default { $HandlerCount = $HandlerCount + $Book.Return.Unit.Count } } #region valdating command/query if (($Command.$Key.Length -eq "0") -or ($Command.$Key -eq "?")) { #if data is empty or query, turn it into a query or assert as a query Write-Verbose ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Null Instruction Data: Asserting to Query") $HexEncodedInstruction = New-Object System.Collections.Generic.List[System.Object] $Key.ToUpper().ToCharArray() | %{'{0:x2}' -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} [char]"?" | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} $CommandCopy += ,[pscustomobject]@{ "Hex"=$HexEncodedInstruction; "Plain"=$Command.$Key; "Command"=$Key.ToUpper(); "HandlerCount"=$HandlerCount; "Instruction"=$Book } } else { Write-Verbose ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Validating Instruction DataType") #validation code using the a page from the book aka return object parameters #don't want to turn up the BMS to 11 accidently. is there a knob that goes that high? if (($Book.ReadOnly -eq $true) -and ($Command.$Key -ne "?")) { #respect readonly instruction flag definition. Discard any instruction data that is included. #in a more strict implementation, this instruction should probably just be discarded. Write-Warning ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Expected Query with ReadOnly Instruction") Write-Warning ("[MsgAssert]: [" + $Key + ":" + $Command.$Key + "]: Disallowed Instruction Data: Setting to Query") $HexEncodedInstruction = New-Object System.Collections.Generic.List[System.Object] $CommandCopy += ,[pscustomobject]@{"Hex"=$HexEncodedInstruction;"Plain"=$Command.$Key;"Command"=$Key.ToUpper()} $Key.ToUpper().ToCharArray() | %{'{0:x2}' -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} [char]"?" | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} } else { #region verify instruction is of the correct value type (float, int, char etc) #verify instruction is within the range of correct value for type. #initialize flag object to tag verification $thisMinMax = @{"Min"=$false;"Max"=$false} $thisTypeValid = $false switch ($Book.Return.Value) { "float" { if ($Command.$Key -is [double]) { Write-Verbose ("[MsgAssert]: [" + ("{0:N}" -f $Command.$Key) + "]: Value Is float") $thisTypeValid = $true #create new array holding hex values for instruction $HexEncodedInstruction = New-Object System.Collections.Generic.List[System.Object] #validate min and max of this value $thisMinMax = MinMaxValidate -instructionValue $Command.$Key -Book $Book if ($thisMinMax -eq $false) { Write-Warning "[MsgAssert]: Instruction value is out of bounds" break } else { #add instruction as hex values to array $Key.ToUpper().ToCharArray() | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} #since this is an insruction not a query, add a space (thanks Tine!) [Convert]::ToInt16($BMSInstructionSet.Config.Message.Components.CMD,16) | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} #add the value as chars (regardless of it's original type) to the array ([string]$Command.$Key).ToCharArray() | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} } } else { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + "]: Value Is NOT float") } } "int" { if (($Command.$Key -is [int]) -or ($Command.$Key -eq 0)) { Write-Verbose ("[MsgAssert]: [" + ("{0:N}" -f $Command.$Key) + "]: Value Is int") $thisTypeValid = $true $HexEncodedInstruction = New-Object System.Collections.Generic.List[System.Object] $thisMinMax = MinMaxValidate -instructionValue $Command.$Key -Book $Book if ($thisMinMax -eq $false) { Write-Warning "[MsgAssert]: Instruction value is out of bounds" break } else { $Key.ToUpper().ToCharArray() | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} $BMSInstructionSet.Config.Message.Components.CMD | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} ([string]$Command.$Key).ToCharArray() | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} } } else { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + "]: Value Is NOT int") } } "array" { if ($Command.$Key) { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + "]: Value Is an array") $thisTypeValid = $true $HexEncodedInstruction = New-Object System.Collections.Generic.List[System.Object] #array types are always read only and have a return handler elsewhere # min and max don't actually do anything. set to true. #set encoded instruction to query always $thisMinMax.Max = $true $thisMinMax.Min = $true $Key.ToUpper().ToCharArray() | %{'{0:x2}' -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} [char]"?" | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} } else { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + "]: Value Is NOT an array") } } "char" { if ($Command.$Key -is [char]) { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + "]: Value Is char") $thisTypeValid = $true $HexEncodedInstruction = New-Object System.Collections.Generic.List[System.Object] $thisMinMax = MinMaxValidate -instructionValue $Command.$Key -Book $Book $Key.ToUpper().ToCharArray() | %{'{0:x2}' -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} $BMSInstructionSet.Config.Message.Components.CMD | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} [char]$Command.$Key | %{"{0:x2}" -f [int][char]$_} | %{$HexEncodedInstruction.Add($_)} } else { Write-Verbose ("[MsgAssert]: [" + $Command.$Key + "]: Value Is NOT char") } } Default { Write-Verbose ("[MsgAssert]: [" + $Book.Return.Value + "]: No handler for this value type") Write-Error ("[MsgAssert]: [" + $Book.Return.Value + "]: No handler for this value type. Verify Dictionary data.") $thisTypeValid = $false $thisMinMax.Max = $false $thisMinMax.Min = $false } } if (($thisMinMax.Max -eq $true) -and ($thisMinMax.Min -eq $true) -and ($thisTypeValid -eq $true)) { Write-Verbose ("[MsgAssert]: [" + $Key + "]: Added to validated instruction stack") $CommandCopy += ,[pscustomobject]@{ "Hex"=$HexEncodedInstruction; "Plain"=$Command.$Key; "Command"=$Key.ToUpper(); "HandlerCount"=$HandlerCount; "Instruction"=$Book } } else { Write-Verbose ("[MsgAssert]: [" + $Key + "]: NOT Added to validated instruction stack") } } } } $CommandCopy } } #end of begin region #process region process { $InstructionStack = ValidateInstructionStack $Command } #end of process region #begin of end region end { return $InstructionStack } #end of end region. The End. } |