# encoding: ascii
# api: powershell
# title: Send-HL7Message
# description: Send HL7 message to a remote host via TCP (MLLP framing)
# version: 1.0
# type: script
# author: Rob Holme
# license: CC0
# x-poshcode-id: 6446
# x-archived: 2016-08-19T05:35:20
# x-published: 2016-07-13T07:02:00
#
#
<# 
.NOTES  
    File Name  : send-HL7Message.ps1  
    Author     : Rob Holme (rob@holme.com.au) 
    Version    : 1.0 (12/07/2016)
    Requires   : PowerShell V2

.SYNOPSIS  
    Send a HL7 message via TCP to a remote host (MLLP framing)
	
.DESCRIPTION  
    Send a HL7 message via TCP to a remote host (MLLP framing)
	
.PARAMETER HostName
    The remote hostname or IP address receiving the HL7 message

.PARAMETER Port
    The remote port of the listener receiving the message

.PARAMETER FileName
    The path and name of the file to send

.PARAMETER NoACK
    If the -NoAck switch is set the script will not wait to receive an ACK message from the remote host.

.EXAMPLE
    .\send-HL7Message -HostName 127.0.0.1 -Port 1234 -FileName c:\test\message.hl7
    # send the file c:\test\message.hl7 to 127.0.0.1:1234, wait for ACK

.EXAMPLE
    .\send-HL7Message 127.0.0.1 1234 c:\test\message.hl7 -NoACK 
    # send the file c:\test\message.hl7 to 127.0.0.1:1234, ignore ACK
#>

[CmdletBinding()]
Param(
        [parameter(Mandatory=$true, Position=0)]
        [string] $HostName,
        [parameter(Mandatory=$true, Position=1)]
        [string] $Port,
        [parameter(Mandatory=$true, Position=3)]
        [string] $FileName,
        [parameter(Mandatory=$false, Position=4)]
        [switch] $NoACK
)

# Warn if the file does not exist.
If (!(Test-Path $FileName))
{
    write-Output "`nError: The file $FileName does not exist."
    return
}

# frame the message using MLLP control characters
$message = [char]0x0B
foreach ($line in get-content $FileName)
{ 
    $message += $line
    $message += [char]0x0D
}
$message += [char]0x1C
$message += [char]0x0D

# create a TCP connection to the remote host, send the message
$tcpConnection = New-Object System.Net.Sockets.TcpClient
Try 
{
	Write-Output "`nConnecting to $($HostName):$($Port) (TCP) ..."
    $tcpConnection.Connect($HostName, $Port)
    $tcpStream = $tcpConnection.GetStream()
    $encoder = New-Object System.Text.UTF8Encoding
    $writeBuffer = New-Object Byte[] 4096
    $writeBuffer = $encoder.GetBytes($message)
    $tcpStream.Write($writeBuffer, 0, $writeBuffer.Length)
    $tcpStream.Flush()
    Write-Output "Message sent"
    # wait for the ACK message returned from the remote host unless -NoACK switch set
    if (!$NoACK)
    {
        write-Output "Waiting for ACK ..."
        $readBuffer = New-Object Byte[] 4096
        $bytesRead = $tcpStream.Read($readBuffer, 0, 4096)
        $ackMessage = $encoder.GetString($readBuffer, 0, $bytesRead)
        $start = $ackMessage.IndexOf([char]0x0B)
        if ($start -ge 0)
        {
            # Search for the end of the MLLP frame (FS control character)
            $end = $ackMessage.IndexOf([char]0x1C)
            if ($end -gt $start)
            {
                # split the ACK message on <CR> character (segment delineter), output each segment of the ACK on a new line
                $ackLines = $($ackMessage.SubString($start+1, $end-1)).Split([char]0x0D)
                foreach ($line in $ackLines)
                {
                    Write-Output $line
                }    
            }
        }
    }
}
# the connection failed
Catch 
{
	Write-Output "Connection to $($HostName):$($Port) failed"
}
Finally 
{
	# for Powershell v3+, call Dispose(), for earlier versions call Close()
    if ($PSVersionTable.PSVersion.Major -lt 3) {
		$tcpConnection.Close()
	}
	else {
		$tcpConnection.Dispose()	
	}
}
