LUA calling external application, processing result

Hi All,

Has anyone had any experience in getting Orthanc to call an external application and process it’s response?

I’m using Orthanc to route images to a PACS, but would like Orthanc to check the PACS to see if an order exists for the images first.

I’m thinking of the following:

  • Radiographers “should” be entering an order into a RIS, which sends a HL7 message to PACS (creating a placeholder for images).
  • Orthanc receives an image from a modality
  • LUA script calls something like DCM4CHE and queries the PACS based on accessionnumber.
  • LUA processes the response to see if there is the placeholder in PACS already
  • If the response is “yes”, let the images through. If it’s a “No” reject.

Anyone?

Hi,

I’ve used a LUA to call a PHP script everytime Orthanc stores an instance, but haven’t used it as part of a IF in LUA yet.

I think you can call a PHP script to get Orthanc to query the PACS to check whether the accessionnumber exists and return the result, but I’ve never tried something like that (I know that LUA can read the PHP response, but not sure whether it will wait the time for PHP to perform the query).

  • no experience in RIS & HL7 yet… :stuck_out_tongue: *

Em

I think i’ve answered my own question here…

Here is an LUA script that queries a RIS to see if an accessionnumber exsits before passing the images on.

  • Obviously, the query function and query itself would need to be adjusted for each RIS.

-It creates a temporary cache (with timeout) of prior queries to prevent multiple RIS queries in the event of CT’s etc

-Any images NOT passed on are held and can be viewed/resent in Orthanc, but are deleted if RIS is sorted out and images re-sent.

  • i’ve left a lot of the logging in for now so that i can work out what functions are being called etc etc.

No guarantee on the quality of this code, but it works for me. Hope this helps someone.

`

—Set location of Cache text file to hold RIS query results
cachefile=“c:/Orthanc/Plug/CheckRIS/CheckRIS_CacheFile.txt”
–Set a timeout for subsequent image send events (prevents multiple RIS queries in the event of things like CT slices etc)
querytimeout=60

function OnStoredInstance(instanceId, tags, metadata, remoteAet, calledAet)

function file_check(file_name)
local file_found=io.open(file_name, “r”)

if file_found==nil then
io.open(cachefile,“w”)
io.close()
else
end
end

function sleep(sec)
local ntime = os.time() + sec
repeat until os.time() > ntime
end

function writetocache(accn,status)
ts = os.date(‘%Y%m%d %H%M%S’, os.time())
fh,err = io.open(cachefile,“a”)
fh:write(“\n”)
fh:write(accn …" " … status … " " … ts)
end

function updatecache(accn,status)
s=‘’
newfile=‘’
newline=‘’
tm = os.date(‘%Y%m%d %H%M%S’, os.time())
f = io.open (cachefile,“r”)
repeat
s = f:read (“*l”) – read one line
if s then – if not end of file (EOF)
if string.find(s,accn…" “) then
newline = string.gsub(s, s, accn…” “…status…” “…tm)
else
newline = s
end
newfile = newfile…newline…”\n"
end
until not s
f:close ()
f = io.open (cachefile,“w+”)
f:write(newfile:sub(1,#newfile-1))
sleep(2)
io.close(f)
end

function queryRIS(accn)
–Create and execute an SQL query on RIS to check if placeholder exists down stream. SQL query should return Passthrough1 if yes, Passthrough0 if no.
qry,err = io.open(“c:/orthanc/plug/CheckRIS/CheckRIS_Query.sql”,“w “)
qry:write(“SET HEADING OFF \nSET TRIMOUT ON \nSET HEADING OFF \n SET PAGESIZE 0 \nSET TRIMSPOOL ON \nSET NEWPAGE NONE \nSELECT ‘Passthrough’ || TRIM(COUNT(ACCESSIONNUMBER)) AS T FROM USER.EXAMRECORD WHERE ACCESSIONNUMBER LIKE '”…accn…”';”)
qry:close()
n = os.tmpname ()
os.execute (“echo EXIT | C:\oracleinstantclient\sqlplus -s USERNAME/PASSWORD@DATASOURCE @c:/orthanc/plug/CheckRIS/CheckRIS_Query.sql >” … n)
status=‘’
–Check SQL query result
for sqlline in io.lines(n) do
if string.find(sqlline,‘Passthrough1’) then
status=‘allowed’
print(“queryRIS says SEND SOMEIMAGES”)
return(status)
elseif string.find(sqlline,‘Passthrough0’) then
status=‘deny’
print(“queryRIS says DONT SEND SOMEIMAGES”)
return(status)
else
status=‘deny’
print(“SQLPLUS Query is not happening”)
return(status)
end
end
os.remove (n)
end

function sendimages(accn)
print("sendimages says SEND THIS IMAGE - "…instanceId)
Delete(SendToModality(instanceId, ‘TestDestination’, remoteAet))
end

function deleteimages(accn)
print(“Holding Images - goto WebViewer to view - http://orthancserver:8042”)
end

function checkRIS(accn)
if queryRIS(accn) == ‘allowed’ then
print(‘checkRIS found something - SEND SOME IMAGES’)
writetocache(accn,‘allowed’)
sendimages(accn)
else
print(‘checkRIS didnt find anything - DONT SEND SOME IMAGES’)
writetocache(accn,‘deny’)
sleep(3)
deleteimages(accn)
end
end

function recheckRIS(accn)
if queryRIS(accn) ==‘allowed’ then
print(‘reckeckRIS found something - SEND SOME IMAGES’)
updatecache(accn,‘allowed’)
sendimages(accn)
else
print(‘recheckRIS didnt find anything - DONT SEND SOME IMAGES’)
updatecache(accn,‘deny’)
deleteimages(accn)
end
end

function foundallowed(accn)
print(“found in cache and allowed - send some images (foundandallowed)”)
sendimages(accn)
end

function foundnotallowed(accn)
print(“found and denied - dont send images (foundandnotallowed)”)
deleteimages(accn)
end

function secdiff(line)
local lineyr=string.sub(line,-15,-12)
local linemt=string.sub(line,-11,-10)
local linedy=string.sub(line,-9,-7)
local linehh=string.sub(line,-6,-5)
local linemm=string.sub(line,-4,-3)
local liness=string.sub(line,-2)
local convertedTimestamp = os.time({year = lineyr, month = linemt, day = linedy, hour = linehh, min = linemm, sec = liness})
now = os.time()
timeDifference = now - convertedTimestamp
return (timeDifference)
end

function search(accn)

file_check(cachefile)
status = ‘’
fh,err = io.open(cachefile)
if err then print(“Cant open cache file”); return; end
while true do
line = fh:read()
if line == nil then
print(“Not found in cached log - goto checkRIS”)
checkRIS(accn)
break end
if string.find(line,accn…" “) and string.find(line,“allowed”) then
print(“Found in cached log and allowed - goto foundallowed”)
foundallowed(accn)
break
elseif string.find(line,accn…” “) and string.find(line,“deny”) and secdiff(line) > querytimeout then
print(“Denied in cache log, but old, re-check - goto recheckRIS”)
recheckRIS(accn)
break
elseif string.find(line,accn…” “) and string.find(line,“deny”) and secdiff(line) < querytimeout then
print(“Denied in cache log not long ago - goto foundnotallowed”)
foundnotallowed(accn)
break
elseif string.find(line,accn…” ") and not (string.find(line,“deny”) or string.find(line,“allowed”)) then
print(“Partially found in cache log, re-check - goto recheckRIS”)
recheckRIS(accn)
break
end
end
fh:close()
end

if tags[‘AccessionNumber’] == ‘’ or tags[‘AccessionNumber’] == nil then
accn = ‘empty’ else
accn = tags[‘AccessionNumber’]
end
print(“________________________________________START INSTANCE “…accn…”___________________________________”)
search(accn)
end

`

Ultimately i’d like to do something similar to prevent Orthanc accepting the images in the first place (so the user knows they’ve been rejected), but haven’t worked that bit out yet.