llp.lua
Verified Featured
Added by iNTERFACEWARE
Allows you to use LLP connections from a Translator script
Source Code
-- llp module - allows Lua to act as a LLP client
-- See http://help.interfaceware.com/code/details/llp-lua
-- Here we expose a single function, connect(), that opens
-- an LLP connection to a remote host, and returns a table
-- to interact with. When debugging, it actually returns
-- a fake connection, which can be sent HL7 messages, and
-- replies with ACKs (one ACK per message sent).
--
-- If you want to test a real LLP connection in the editor,
-- pass live=true along with your connection settings.
--
-- local llp = require 'llp'
--
-- local s = llp.connect{host='frink',port=8086}
-- s:send(Data)
-- local Ack = s:recv()
-- s:close()
__llp = {
send_llp = function(s, msg)
local sent, text = 0, '\v'..msg..'\28\r'
repeat
sent = sent + s:send(text, sent+1)
until sent >= text:len()
return sent
end;
recv_llp = function(s, buf)
local head = buf:find('\v', 1, true)
local tail = head and buf:find('\28\r', head, true)
while not tail do
local part = s:recv()
if not part then
return nil, buf, ''
end
local old_end = buf:len()
buf = buf..part
head = head or buf:find('\v', old_end, true)
if head then
local i = math.max(old_end, head)
tail = buf:find('\28\r', i, true)
end
end
local msg = buf:sub(head + 1, tail - 1)
local remainder = buf:sub(tail + 2)
local skipped = buf:sub(1, head - 1)
return msg, remainder, skipped
end;
real_meta = {
__index = {
send = function(self, msg)
return __llp.send_llp(self.s, msg)
end;
recv = function(self)
local msg, skipped
msg, self.buf, skipped = __llp.recv_llp(self.s, self.buf)
return msg, skipped
end;
close = function(self)
self.s:close()
end
}
};
--
-- Metatable for Simulation
--
simulation_meta = {
__index = {
send = function(self, msg)
if not self.connected then
error('not connected', 2)
end
self.sent = msg
return msg:len()
end;
recv = function(self)
if not self.connected then
error('not connected', 2)
elseif not self.sent then
error('timeout', 2)
else
--local got = ack.generate(self.sent) -- OLD VERSION
--ack.generate() only works in a From LLP script
--replaced it with inline function "ackGenerate"
--so module works in any component
local ackGenerate = function(self)
local Msg = hl7.parse{vmd='ack.vmd', data=self.sent}
local Ack = hl7.message{vmd='ack.vmd', name='Ack'}
Ack.MSH[3][1] = Msg.MSH[5][1]
Ack.MSH[4][1] = Msg.MSH[6][1]
Ack.MSH[5][1] = Msg.MSH[3][1]
Ack.MSH[6][1] = Msg.MSH[4][1]
Ack.MSH[10] = Msg.MSH[10]
Ack.MSH[9][1] = 'ACK'
Ack.MSH[11][1] = 'P'
Ack.MSH[12][1] = Msg.MSH[12][1]
Ack.MSA[1] = 'AA'
Ack.MSA[2] = Msg.MSH[10]
return Ack:S()
end;
local got = ackGenerate(self)
self.sent = nil
return got
end
end;
close = function(self)
self.connected = false
end
}
};
--
-- Error Checking
--
check_arg = function(args, k, t, optional)
local help = [[Connect to a remote LLP host.
Takes a table with the following required entries:
'host' - the hostname of the remote site
'port' - the port on the remote site
and optionally these entries:
'timeout' - maximum wait time, in seconds (default 5s)
'live' - create live LLP connections in the editor
e.g. local s = llp.connect{host='hostname',port=8086}
s:send(Data)
local Ack = s:recv()
s:close()
]]
if not args then
error(help, 3)
elseif type(args) ~= 'table' then
error('Parameter 1 is not a table.\n'..help, 3)
elseif not optional and not args[k] then
error("Parameter '"..k.."' is required.\n"..help, 3)
elseif args[k] and type(args[k]) ~= t then
error("Parameter '"..k.."' should be a "..t..'.\n'..help, 3)
end
end;
}
-- generate a
function ackGenerate(Data)
local Msg = hl7.parse{vmd='ack.vmd', data=Data}
local Ack = hl7.message{vmd='ack.vmd', name='Ack'}
Ack.MSH[3][1] = Msg.MSH[5][1]
Ack.MSH[4][1] = Msg.MSH[6][1]
Ack.MSH[5][1] = Msg.MSH[3][1]
Ack.MSH[6][1] = Msg.MSH[4][1]
Ack.MSH[10] = Msg.MSH[10]
Ack.MSH[9][1] = 'ACK'
Ack.MSH[11][1] = 'P'
Ack.MSH[12][1] = Msg.MSH[12][1]
Ack.MSA[1] = 'AA'
Ack.MSA[2] = Msg.MSH[10]
return Ack:S()
end
--
-- Public Interface
--
local llp = {}
function llp.connect(args)
local required, optional = false, true
__llp.check_arg(args, 'host', 'string', required)
__llp.check_arg(args, 'port', 'number', required)
__llp.check_arg(args, 'timeout', 'number', optional)
__llp.check_arg(args, 'live', 'boolean', optional)
if args.live or not iguana.isTest() then
-- Normal behaviour (in running channel).
args.live = nil
local Success, Socket = pcall(net.tcp.connect, args)
if not Success then
-- raise error to caller level
error(Socket, 2)
end
return setmetatable({
s = Socket,
buf = '', -- input buffer.
}, __llp.real_meta)
else
-- Simulate behaviour while editing.
return setmetatable({
connected = true,
}, __llp.simulation_meta)
end
end
local llp_connect = {
Title="llp.connect";
Usage="llp.connect{host, port}",
SummaryLine="Opens a new LLP connection",
Desc=[[Opens an LLP connection (socket) using the specified host name and port
number.
<p>An LLP socket connection is returned as a table. This table contains four fields:
A boolean "connected" flag, and three functions send(), recv(), and close(). Use
send() to send messages, recv() to receive returned ACK messages, and close() to
close the connection.
<p><b>Note</b>: It is best practice to close the connection everytime so you don't
overload the remote LLP host (many hosts will refuse multiple connections anyway).
<p>For more information about send(), recv(), and close(), see the
<a href="http://help.interfaceware.com/api/#net_tcp">net.tcp functions</a>
of the same name in the API Reference (LLP connections are actually just sockets
that use those net.tcp functions).
]];
["Returns"] = {
{Desc="LLP connection (socket) <u>table</u>."},
};
ParameterTable= true,
Parameters= {
{host= {Desc='LLP host name <u>string</u>.'}},
{port= {Desc='Port number <u>integer</u>.'}},
};
Examples={
[[ -- connect to LLP host
local s = llp.connect{host='localhost',port=7013}
-- send HL7 message
s:send(Data)
-- receive ACK return
local Ack = s:recv()
trace(Ack)
-- best practise to close the connection
-- many hosts will reject multiple connections
s:close()
]],
};
SeeAlso={
{
Title="stringutil.lua - in our code repository.",
Link="http://help.interfaceware.com/code/details/stringutil-lua"
},
{
Title=" LLP Client – custom .",
Link="http://help.interfaceware.com/v6/llp-client-custom"
},
{
Title="Stringutil – string functions",
Link="http://help.interfaceware.com/v6/stringutil-string-functions"
}
}
}
help.set{input_function=llp.connect, help_data=llp_connect}
return llp
Description
Allows you to use LLP connections from a Translator script
Usage Details
The LLP module enables the use of LLP connections (over TCP/IP) from any module (as opposed to using a From LLP component). The module has just one function, llp.connect{}, to open new LLP connections.
How to use llp.lua:
- Add the module to a script
- Add
local llp = require 'llp'at the top of your script - Use
local s = llp.connect{}to return a connection handle
Note: The handle has three functions send(), recv() and close() - Use
s:send()to send data - Use
s:recv()to receive an ACK - Use
s:close()to close the connection
Here is some sample code:
local llp = require 'llp'
-- The LLP client allows one to make LLP connections from the translator in Lua
-- https://help.interfaceware.com/code/details/llp-lua
function main(Data)
-- connect to LLP host
local s = llp.connect{host='localhost',port=7013}
-- send HL7 message
s:send(Data)
-- receive ACK return
local Ack = s:recv()
trace(Ack)
-- best practise to close the connection
-- Note: many hosts will reject multiple connections
s:close()
util.sleep(50)
end
More Information