Hi again, stopped working on that since the MR scanner install got really delayed because of COVID. I did revisit and started experimenting around again a bit using sequences in the MWL. An example of the dcmdump of the text file created is here: The sequences that I added are:
(0008,0096) SQ (Sequence with explicit length #=1) # 252, 1 ReferringPhysicianIdentificationSequence
&
(0040,0100) SQ (Sequence with explicit length #=1) # 110, 1 ScheduledProcedureStepSequence
It seems to convert the text file with dump2dcm and back with dcmdump, so at least it isn't complaining about the formt. I remove the Header for now.
Just wondering if that seems to basically conform to the format.
I'll have to consult the Conformance Statement and hopefully the Esaote technicians once they get rolling:
Here is their Conformance:
https://www.esaote.com/fileadmin/user_u ... 3-1_3_.pdf
Code: Select all
# Dicom-File-Format
# Dicom-Meta-Information-Header
# Used TransferSyntax: Unknown Transfer Syntax
# Dicom-Data-Set
# Used TransferSyntax: Little Endian Explicit
(0008,0005) CS [ISO_IR 100] # 10, 1 SpecificCharacterSet
(0008,0050) SH [DEVACC00000002] # 14, 1 AccessionNumber
(0008,0060) CS [MR] # 2, 1 Modality
(0008,0080) LO [Cayman Medical Ltd.] # 20, 1 InstitutionName
(0008,0090) PN [0001:Scotti^Stephen^^Dr.^,M.D.] # 30, 1 ReferringPhysicianName
(0008,0096) SQ (Sequence with explicit length #=1) # 252, 1 ReferringPhysicianIdentificationSequence
(fffe,e000) na (Item with explicit length #=4) # 244, 1 Item
(0008,0080) LO [Cayman Medical Ltd.] # 20, 1 InstitutionName
(0040,1101) SQ (Sequence with explicit length #=1) # 48, 1 PersonIdentificationCodeSequence
(fffe,e000) na (Item with explicit length #=3) # 40, 1 Item
(0008,0100) SH [0001] # 4, 1 CodeValue
(0008,0102) SH [L] # 2, 1 CodingSchemeDesignator
(0008,0104) LO [Local Code] # 10, 1 CodeMeaning
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0040,1103) LO [US-Phone^WPN^PH^email] # 40, 1 PersonTelephoneNumbers
(0040,1104) LT [US-Phone^WPN^PH^email] # PersonTeleco... # 100, 1 PersonTelecomInformation
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0008,1030) LO [MRI BRAIN / BRAIN STEM] # 22, 1 StudyDescription
(0010,0010) PN [Scotti^Stephen^Douglas] # 22, 1 PatientName
(0010,0020) LO [D] # 2, 1 PatientID
(0010,0030) DA [YYYYMMDD] # 8, 1 PatientBirthDate
(0010,0040) CS [M] # 2, 1 PatientSex
(0010,1040) LO [Address] # 52, 1 PatientAddress
(0010,2000) LO (no value available) # 0, 0 MedicalAlerts
(0010,2110) LO (no value available) # 0, 0 Allergies
(0010,2154) SH [US-Phone^PRN^PH^email] # 36, 1 PatientTelephoneNumbers
(0010,2155) LT [US-Phone^WPN^PH^email] # 36, 1 PatientTelecomInformation
(0010,21b0) LT [test] # 4, 1 AdditionalPatientHistory
(0020,000d) UI [1.3.6.1.4.1.56016.1.20210127000000] # 34, 1 StudyInstanceUID
(0020,0010) SH [0001] # 4, 1 StudyID
(0032,1060) LO [MRI BRAIN / BRAIN STEM] # 22, 1 RequestedProcedureDescription
(0040,0100) SQ (Sequence with explicit length #=1) # 110, 1 ScheduledProcedureStepSequence
(fffe,e000) na (Item with explicit length #=6) # 102, 1 Item
(0008,0060) CS [MR] # 2, 1 Modality
(0040,0001) AE [AETITLE_MRI] # 12, 1 ScheduledStationAETitle
(0040,0002) DA [20210127] # 8, 1 ScheduledProcedureStepStartDate
(0040,0003) TM [000000] # 6, 1 ScheduledProcedureStepStartTime
(0040,0007) LO [MRI BRAIN / BRAIN STEM] # 22, 1 ScheduledProcedureStepDescription
(0040,0009) SH [0001] # 4, 1 ScheduledProcedureStepID
(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem
(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem
(0040,1001) SH [0001] # 4, 1 RequestedProcedureID
(0040,1003) SH [R]
As a side note, I've been working on a python script to integrate with Orthanc that creates the MWL file from an HL7 messages sent to Orthanc. That uses the PIP module:
import hl7 #
https://python-hl7.readthedocs.io/en/latest/, and the MWL plug-in from Orthanc. Still a work in progress, but hoping to develop it further to process the HL7 message to create the MWL file for use with Orthanc.
Code: Select all
def CreateAndSave(output, uri, **request):
if request['method'] != 'POST':
output.SendMethodNotAllowed('POST')
else:
query = json.loads(request['body'])
pathtodump2dcm = shutil.which("dump2dcm")
pathtoworklist = json.loads(orthanc.GetConfiguration())['Worklists']['Database']
if not os.path.exists(pathtoworklist):
os.makedirs(pathtoworklist)
charset = "ISO_IR 100"
if "HL7" in query:
message = hl7.parse(query['HL7'])
# family name complex, given name complex, middle name, name prefix, name suffix. in dicom
query = dict()
print(message['PID1.F2.R1.C1'])
query['AccessionNumber'] = str(message.segments('OBR')[0][3]); #field 3 of first obr
query['Modality'] = str(message.segments('IPC')[0][5]); #field 4 of first IPC
query['InstitutionName'] = str(message.segment('PV1')[39]); #field 39 of first PV!
query['ReferringPhysiciansName'] = str(message.segment('PV1')[8][0][0]) + ':' + str(message.segment('PV1')[8][0][1]) + '^' + str(message.segment('PV1')[8][0][2]) + '^' + str(message.segment('PV1')[8][0][3]) + '^' + str(message.segment('PV1')[8][0][5]) + '^' + str(message.segment('PV1')[8][0][4]) + ',' + str(message.segment('PV1')[8][0][6]) # ReferringPhysiciansName
query['PatientName'] = str(message.segment('PID')[5]); #field 5
query['PatientID'] = str(message.segment('PID')[2][0][0]); #field 2, first R, component 0
query['PatientBirthDate'] = str(message.segment('PID')[7]);
query['PatientSex'] = str(message.segment('PID')[8]);
query['PatientAddress'] = str(message.segment('PID')[11]);
query['PatientTelephoneNumbers'] = str(message.segment('PID')[13]);
query['PatientTelecomInformation'] = str(message.segment('PID')[14]);
query['MedicalAlerts'] = "" # message.segments('OBR')[0][8];
query['Allergies'] = ""# message.segments('OBR')[0][8];
query['AdditionalPatientHistory'] = str(message.segments('OBR')[0][13]) # message.segments('OBR')[0][8];
query['StudyInstanceUID'] = str(message.segments('IPC')[0][3]) #
query['RequestingPhysician'] = str(message.segment('PV1')[8]) # RequestingPhysician
query['RequestedProcedureDescription'] = str(message.segments('OBR')[0][4][0][1])
query['ScheduleStationAETitle'] = str(message.segments('IPC')[0][9])
query['ScheduledProcedureStepStartDate'] = str(message.segments('OBR')[0][36])[:8]
query['ScheduledProcedureStepStartTime'] = str(message.segments('OBR')[0][36])[-6:]
query['RequestedProcedureID'] = str(message.segments('OBR')[0][4][0][0])
query['RequestedProcedurePriority'] =str(message.segments('OBR')[0][5])
query['MediaStorageSOPClassUID'] = "NONE" # might need this
query['PhysicianIDforSequence'] =str(message.segment('PV1')[8][0][0])
query['PersonTelephoneNumbers'] =str(message.segments('OBR')[0][17])
query['PersonTelecomInformation'] = str(message.segments('OBR')[0][17])
mwl = [];
mwl.append("# Dicom-File-Format")
mwl.append("")
mwl.append("# Dicom-Meta-Information-Header")
#mwl.append("(0002,0000) UL 202 # FileMetaInformationGroupLength")
#mwl.append("(0002,0001) OB 00\\01 # FileMetaInformationVersion")
#mwl.append("(0002,0002) UI [" + query['MediaStorageSOPClassUID'] + "] # MediaStorageSOPClassUID")
#mwl.append("(0002,0003) UI [1.2.276.0.7230010.3.1.4.2831176407.11154.1448031138.805061] # MediaStorageSOPInstanceUID")
#mwl.append("(0002,0010) UI =LittleEndianExplicit # TransferSyntaxUID")
#mwl.append("(0002,0012) UI [1.2.276.0.7230010.3.0.3.6.0] # ImplementationClassUID")
#mwl.append("(0002,0013) SH [OFFIS_DCMTK_360] # ImplementationVersionName")
#mwl.append("")
mwl.append("# Dicom-Data-Set")
mwl.append("(0008,0005) CS [" +charset + "] # SpecificCharacterSet")
mwl.append("(0008,0050) SH [" + query['AccessionNumber'] + "] # AccessionNumber")
mwl.append("(0008,0060) CS [" + query['Modality'] + "] # Modality")
mwl.append("(0008,0080) LO [" + query['InstitutionName'] + "] # InstitutionName")
# PN format is last,first,middle,prefix,suffix, HL7 format is "ID^LAST^FIRST^MIDDLE^SUFFIX^PREFIX^DEGREE"
# Want this to be ID:Last^First^Middle^Prefix^Suffix for DICOM
mwl.append("(0008,0090) PN [" + query['ReferringPhysiciansName'] + "] # ReferringPhysiciansName")
mwl.append("(0008,1030) LO [" + query['RequestedProcedureDescription'] + "] # RequestedProcedureDescription")
mwl.append("(0010,0010) PN [" + query['PatientName'] + "] # PatientName")
mwl.append("(0010,0020) LO [" + query['PatientID'] + "] # PatientID")
mwl.append("(0010,0030) DA [" + query['PatientBirthDate'] + "] # PatientBirthDate")
mwl.append("(0010,0040) CS [" + query['PatientSex'] + "] # PatientSex")
mwl.append("(0010,1040) LO [" + query['PatientAddress'] + "] # PatientAddress")
mwl.append("(0010,2154) SH [" + query['PatientTelephoneNumbers'] + "] # PatientTelephoneNumbers")
mwl.append("(0010,2155) LT [" + query['PatientTelecomInformation'] + "] # PatientTelecomInformation")
mwl.append("(0010,2000) LO [" + query['MedicalAlerts'] + "] # MedicalAlerts")
mwl.append("(0010,2110) LO [" + query['Allergies'] + "] # Allergies")
mwl.append("(0010,21B0) LT [" + query['AdditionalPatientHistory'] + "] # AdditionalPatientHistory")
mwl.append("(0020,000d) UI [" + query['StudyInstanceUID'] + "] # StudyInstanceUID")
mwl.append("(0020,0010) SH [" + query['RequestedProcedureID'] + "] # StudyID")
mwl.append("(0032,1060) LO [" + query['RequestedProcedureDescription'] + "] # RequestedProcedureDescription")
#mwl.append("(0040,0001) AE [" + query['ScheduleStationAETitle'] + "] # ScheduleStationAETitle")
#mwl.append("(0040,0002) DA [" + query['ScheduledProcedureStepStartDate'] + "] # ScheduledProcedureStepStartDate")
#mwl.append("(0040,0003) TM [" + query['ScheduledProcedureStepStartTime'] + "] # ScheduledProcedureStepStartTime")
mwl.append("(0040,1001) SH [" + query['RequestedProcedureID'] + "] # RequestedProcedureID")
mwl.append("(0040,1003) SH [" + query['RequestedProcedurePriority'] + "] # RequestedProcedurePriority")
mwl.append("(0040,0100) SQ (Sequence with explicit length #=1) # ScheduledProcedureStepSequence")
mwl.append("(fffe,e000) na (Item with explicit length #=6) # Item")
mwl.append("(0040,0001) AE [" + query['ScheduleStationAETitle'] + "] # ScheduledStationAETitle")
mwl.append("(0040,0002) DA [" + query['ScheduledProcedureStepStartDate'] + "] # ScheduledProcedureStepStartDate")
mwl.append("(0040,0003) TM [" + query['ScheduledProcedureStepStartTime'] + "] # ScheduledProcedureStepStartTime")
mwl.append("(0040,0007) LO [" + query['RequestedProcedureDescription'] + "] # ScheduledProcedureStepDescription")
mwl.append("(0040,0009) SH [" + query['RequestedProcedureID'] + "] # ScheduledProcedureStepID")
mwl.append("(0008,0060) CS [" + query['Modality'] + "] # Modality")
mwl.append("(fffe,e00d) na (ItemDelimitationItem for re-encoding) # 0, 0 ItemDelimitationItem")
mwl.append("(fffe,e0dd) na (SequenceDelimitationItem for re-encod.) # 0, 0 SequenceDelimitationItem")
mwl.append("(0008,0096) SQ (Sequence with explicit length #=1) # ReferringPhysicianIdentificationSequence")
mwl.append("(fffe,e000) na (Item with explicit length #=4) # Item")
mwl.append("(0008,0080) LO [" + query['InstitutionName'] + "] # InstitutionName")
mwl.append("(0040,1101) SQ (Sequence with explicit length #=1) # PersonIdentificationCodeSequence")
mwl.append("(fffe,e000) na (Item with explicit length #=3) # Item")
mwl.append("(0008,0100) SH [" + query['PhysicianIDforSequence'] + "] # CodeValue")
mwl.append("(0008,0102) SH [L] # CodingSchemeDesignator")
mwl.append("(0008,0104) LO [Local Code] # CodeMeaning")
mwl.append("(fffe,e00d) na (ItemDelimitationItem for re-encoding) # ItemDelimitationItem")
mwl.append("(fffe,e0dd) na (SequenceDelimitationItem for re-encoding) # SequenceDelimitationItem")
mwl.append("(0040,1103) LO [" + query['PersonTelephoneNumbers'] + "] # PersonTelephoneNumbers")
mwl.append("(0040,1104) LT [" + query['PersonTelecomInformation'] + "] # PersonTelecomInformation, [Phone^WPN^CP^email]")
mwl.append("(fffe,e00d) na (ItemDelimitationItem for re-encoding) # ItemDelimitationItem")
mwl.append("(fffe,e0dd) na (SequenceDelimitationItem for re-encoding.) # SequenceDelimitationItem")
try:
errorstatus = False
response = dict()
filename = pathtoworklist + '/' + query['AccessionNumber']
returnedtext = ""
original = sys.stdout
for line in mwl:
returnedtext = returnedtext + line + "\n"
with open(filename + ".txt", 'w+') as filehandle:
# set the new output channel
sys.stdout = filehandle
for line in mwl:
print(line)
# restore the old output channel
sys.stdout = original
filehandle.close()
subprocess.Popen(pathtodump2dcm +' -F +te ' + filename + ".txt " + filename + ".wl", shell = True)
# raise Exception("Testing Error.")
except Exception as e:
errorstatus = True
response['error'] = str(e)
response['status'] = 'Problem with MWL: ' + query['AccessionNumber']
if errorstatus == False:
response['status'] = 'MWL File Written ' + query['AccessionNumber']
response['error'] = "OK"
output.AnswerBuffer(json.dumps(response, indent = 3), 'application/json')
orthanc.RegisterRestCallback('/mwl/file/make', CreateAndSave)