Beispiel #1
0
        /// <summary>
        /// Send the PDF document referenced by "dt" to the PACS system defined in the database lkpInterfaceDefinitions for this document
        /// effective 1/22/16 the values required to be present in lkpInterfaceDefinitions are as follows:
        /// interfaceType = "PACS"
        /// ipAddress, port self explanatory
        /// stringParam1: "their" AET
        /// stringParam2: "our" AET
        /// stringParam3: qualified path to DCMTK binaries, e.g., C:\HUGHES_DCMTK_DICOM\bin
        /// intParam1: send file type, see enum.  1=pdf, 2=jpg, 3=bmp
        /// stringParam4: Modality to send to PACS.  Defaults to "OT"
        /// stringParam5: Additional parameters.  see documentation for DCMTK "storescu".  e.g., when sending a jpg, you might have to specify jpg type, e.g., "-xy"
        /// </summary>
        /// <param name="dt">Document Template, as per the HTML document generation</param>
        /// <param name="patient">V3 Patient object</param>
        /// <param name="filename">Name of the PDF file to be converted to a .DCM (DICOM) file and transmitted to PACS</param>
        /// <param name="apptId">Appointment ID</param>
        /// <returns></returns>
        public static bool send2PACS(DocumentTemplate dt, RiskApps3.Model.PatientRecord.Patient patient, string filenameIn, int apptId)
        {
            List<TripleArg> args = new List<TripleArg>();   // for want of a tuple which doesn't exist till .NET 4.0
            List<KeyValuePair<string, string>> dicomOutputArgs = new List<KeyValuePair<string, string>>();
            List<InterfaceInstance> interfaces = new List<InterfaceInstance>();

            string accessionNumber = "";
            string patientname = "";
            string dob = "";
            string gender = "";
            string apptDate = "";
            string apptTime = "";

            ParameterCollection pacsArgs = new ParameterCollection();
            pacsArgs.Add("documentTemplateID", dt.documentTemplateID);
            string filename;
            Image img = null;

            SqlDataReader reader = BCDB2.Instance.ExecuteReaderSPWithParams("sp_getInterfaceDefinitionFromTemplateID", pacsArgs);
            while (reader.Read())
            {
                InterfaceInstance instance = new InterfaceInstance();

                if (reader.IsDBNull(0) == false)
                {
                    instance.InterfaceId = reader.GetInt32(0);
                }
                if (reader.IsDBNull(1) == false)
                {
                    instance.InterfaceType = reader.GetString(1);
                }
                if (reader.IsDBNull(2) == false)
                {
                    instance.IpAddress = reader.GetString(2);
                }
                if (reader.IsDBNull(3) == false)
                {
                    instance.Port = reader.GetString(3);
                }
                if (reader.IsDBNull(4) == false)
                {
                    instance.AeTitleRemote = reader.GetString(4);
                }
                if (reader.IsDBNull(5) == false)
                {
                    instance.AeTitleLocal = reader.GetString(5);
                }
                if (reader.IsDBNull(6) == false)
                {
                    instance.DCMTKpath = reader.GetString(6);
                }
                if (reader.IsDBNull(7) == false)
                {
                    instance.PacsSubType = reader.GetInt32(7);   // for now, 1 == PDF, 2 == JPG, 3 == BMP (see enum pacsSubTypes, above)
                }
                // there is no intParam2 for PACS as yet...skipping ahead to the new-as-of-jan-2016 string param 4
                if (reader.IsDBNull(9) == false)
                {
                    instance.Modality = reader.GetString(9);
                }
                if (reader.IsDBNull(10) == false)
                {
                    instance.AdditionalParams = reader.GetString(10);
                }

                if (instance.InterfaceId > 0)
                {
                    interfaces.Add(instance);   // multiple PACS interfaces per document are now supported.
                }

            }

            // just do this once...
            if (apptId > 0)
            {
                // This is an FMH specific hack, they are sending the Accession Number in the HL7 ADT_A08 message in PID|26...
                // and it is being stored in the appointment table in the unused field "referral".
                // No more obvious way of accomplishing this has jumped out at me yet, but this will have to be made more generic as new
                // customers are brought on-line.
                string sqlStr = "SELECT referral,patientname,dob,gender,apptDate,apptTime FROM tblAppointments WHERE apptID=" + apptId.ToString() + ";";
                reader = BCDB2.Instance.ExecuteReader(sqlStr);
                while (reader.Read())
                {
                    if (reader.IsDBNull(0) == false)
                    {
                        accessionNumber = reader.GetString(0);
                    }
                    if (reader.IsDBNull(1) == false)   // taking this from appointment table, alternatively, could use Patient object
                    {
                        patientname = reader.GetString(1);
                    }
                    if (reader.IsDBNull(2) == false)
                    {
                        dob = reader.GetString(2);
                    }
                    if (reader.IsDBNull(3) == false)
                    {
                        gender = reader.GetString(3);
                    }
                    if (reader.IsDBNull(4) == false)
                    {
                        apptDate = reader.GetString(4);
                    }
                    if (reader.IsDBNull(5) == false)
                    {
                        apptTime = reader.GetString(5);
                    }
                }
                reader.Close();
            }

            foreach (InterfaceInstance iface in interfaces)     // send document to each eligible interface, converting it as need be
            {
                if ((iface.InterfaceId > 0) && (iface.InterfaceType == "PACS"))     // no interface?  not a PACS interface?  bye bye!
                {
                    // Ok!  We're sending this thing to PACS... go ahead and convert the PDF file to a DCM file...
                    // This will call into the DCMTK library to do their pdf2dcm, dcmodify (to set metadata), and storescu.  Add appropriate parameters to the DCM.

                    filename = filenameIn;      // reinit

                    // filter out oddball cases of calling this method with conflicting interface parameters versus the type of file passed in.
                    // however, we WILL reconvert PDFs to an image upon demand, see below.
                    if (((filename.ToUpper().Contains(".JPG")) || (filename.ToUpper().Contains(".BMP"))) && (iface.PacsSubType == (int)pacsSubTypes.PDF))
                    {
                        Logger.Instance.WriteToLog("Send2PACS:  Attempted to send an image file to a PDF interface, interfaceID #" + iface.InterfaceId + ".  Request Aborted" + (apptId > 0 ? " for appt id " + apptId.ToString() + "." : "."));
                        continue;
                    }
                    if (iface.PacsSubType == (int)pacsSubTypes.JPG)    // jpg
                    {
                        // If we were sent a PDF file, yet it's pacsSubType is different, convert the document to an image
                        // this could happen depending on what type of file is generated from the survey, and the settings made in lkpInterfaceDefinitions
                        // as of now, we're not converting between jpgs and bmps... if you send a bmp and interface type is jpg, bail... and vice versa
                        if (filename.ToUpper().Contains(".BMP"))
                        {
                            Logger.Instance.WriteToLog("Send2PACS:  Attempted to send a BMP file to a JPG interface.  Request Aborted" + (apptId > 0 ? " for appt id " + apptId.ToString() + "." : "."));
                            continue;
                        }
                        if (filename.ToUpper().Contains(".PDF"))    // re-convert this html to an image instead
                        {
                            HtmlToImageConverter htmlToJpgConverter = new HtmlToImageConverter();
                            htmlToJpgConverter.LicenseKey = "sjwvPS4uPSskPSgzLT0uLDMsLzMkJCQk";
                            filename = filename.ToUpper().Replace(".PDF", "") + ".jpg";
                            htmlToJpgConverter.ConvertHtmlToFile(dt.htmlText, "", System.Drawing.Imaging.ImageFormat.Jpeg, filename);
                        }
                    }

                    if ((iface.PacsSubType == (int)pacsSubTypes.BMP) ||  (iface.PacsSubType == (int)pacsSubTypes.inverseBMP)) // bitmap
                    {
                        // If we were sent a PDF file, yet it's pacsSubType is different, convert the document to an image
                        // this could happen depending on what type of file is generated from the survey, and the settings made in lkpInterfaceDefinitions
                        // as of now, we're not converting between jpgs and bmps... if you send a bmp and interface type is jpg, bail... and vice versa
                        if (filename.ToUpper().Contains(".JPG"))
                        {
                            Logger.Instance.WriteToLog("Send2PACS:  Attempted to send a JPG file to a BMP interface.  Request Aborted" + (apptId > 0 ? " for appt id " + apptId.ToString() + "." : "."));
                            continue;
                        }
                        if (filename.ToUpper().Contains(".PDF"))     // re-convert this html to an image instead
                        {
                            HtmlToImageConverter htmlToJpgConverter = new HtmlToImageConverter();
                            htmlToJpgConverter.LicenseKey = "sjwvPS4uPSskPSgzLT0uLDMsLzMkJCQk";
                            filename = filename.ToUpper().Replace(".PDF", "") + ".bmp";
                            if (iface.PacsSubType == (int)pacsSubTypes.inverseBMP)
                            {
                                img = htmlToJpgConverter.ConvertHtmlToImageObject(dt.htmlText, "");
                            }
                            else
                            {
                                htmlToJpgConverter.ConvertHtmlToFile(dt.htmlText, "", System.Drawing.Imaging.ImageFormat.Bmp, filename);
                            }
                        }

                        // jdg 1/26/2016 invert bmp
                        if (iface.PacsSubType == (int)pacsSubTypes.inverseBMP)
                        {
                            InvertBitmap(filename, img);
                        }
                    }

                    args.Clear();

                    string dcmFilename = filename + ".dcm";

                    TripleArg arg1 = new TripleArg();
                    //arg1.ArgSwitch = filename;
                    arg1.ArgSwitch = "\"" + filename + "\"";
                    args.Add(arg1);

                    TripleArg arg2 = new TripleArg();
                    //arg2.ArgSwitch = dcmFilename;
                    arg2.ArgSwitch = "\"" + dcmFilename + "\"";
                    args.Add(arg2);

                    if ((patient != null) && (iface.PacsSubType == (int)pacsSubTypes.PDF)) // for some reason, img2dcm (jpg) doesn't have a patient identifier arg.  will have to insert this into the metadata later.
                    {
                        TripleArg arg3 = new TripleArg();
                        arg3.ArgSwitch = "+pi";
                        arg3.Name = patient.unitnum;
                        args.Add(arg3);
                    }

                    if ((iface.PacsSubType == (int)pacsSubTypes.BMP) || (iface.PacsSubType == (int)pacsSubTypes.inverseBMP))       // defaults to jpg
                    {
                        TripleArg arg4 = new TripleArg();
                        arg4.ArgSwitch = "-i";
                        arg4.Name = "BMP";
                        args.Add(arg4);
                    }

                    if ((iface.PacsSubType == (int)pacsSubTypes.JPG) || (iface.PacsSubType == (int)pacsSubTypes.BMP) || (iface.PacsSubType == (int)pacsSubTypes.inverseBMP))    // jpg, bmp
                    {
                        // do the conversion jpg --> dcm;  this is a local command, no ip/port needed for this one.
                        wrapDCMTKmethodAsExternalCommand(null, null, iface.DCMTKpath + "\\img2dcm.exe", args);
                    }
                    else
                    {
                        // do the conversion pdf --> dcm;  this is a local command, no ip/port needed for this one.
                        wrapDCMTKmethodAsExternalCommand(null, null, iface.DCMTKpath + "\\pdf2dcm.exe", args);
                    }
                    if (!File.Exists(dcmFilename))
                    {
                        Logger.Instance.WriteToLog("Send2PACS: Unable to convert JPG or PDF to DCM OR perhaps just unable to write it to disk.  Check lkp_AutomationDocuments, lkpInterfaceDefinitions, document storage location and DICOM binary library (" + iface.DCMTKpath + ") for existence and/or permissions.  Filename: " + dcmFilename + ".");
                        continue;        // something went wrong, but didn't throw an error...
                    }

                    // I'm guessing that we'll be adding a study/series ID later, just add another TripleArg here, if you didn't build the DCM with it in the metadata initially
                    // If you need to fetch the UID, here is a sample of what a findscu would look like... don't forget the -aec and -aet...
                    // findscu localhost 5678 -S -k StudyInstanceUID -k QueryRetrieveLevel=STUDY -k AccessionNumber=22222893 -aet Hughes -aec Hughes2
                    // and of course, you have to read the resulting array list.

                    args.Clear();

                    TripleArg modArg0 = new TripleArg();   // dcm file name
                    modArg0.ArgSwitch = "\"" + dcmFilename + "\"";
                    args.Add(modArg0);

                    if (!String.IsNullOrEmpty(accessionNumber))
                    {
                        TripleArg modArg1 = new TripleArg();   // accession number
                        modArg1.ArgSwitch = "-m";
                        modArg1.Name = "AccessionNumber";    // DICOM literal string, do not modify
                        modArg1.Value = accessionNumber;
                        args.Add(modArg1);
                    }
                    else
                    {
                        // make a note of this circumstance, it's especially critical for FMH
                        Logger.Instance.WriteToLog("Send2PACS:  Accession number is not set for Interface #" + iface.InterfaceId + " for appt # : " + apptId.ToString());
                    }

                    TripleArg modArg2 = new TripleArg();   // Modality hard-coded to "OT".  May need to be parameterized later.
                    modArg2.ArgSwitch = "-i";
                    modArg2.Name = "Modality";  // DICOM literal string, do not modify
                    modArg2.Value = iface.Modality;     //  "OT";   no longer hard-coded jdg 1/21/16

                    args.Add(modArg2);

                    TripleArg modArg3 = new TripleArg();   // Patient Name
                    modArg3.ArgSwitch = "-m";
                    modArg3.Name = "PatientName";   // DICOM literal string, do not modify
                    patientname = patientname.Replace(",  ", ",");   // hack for extra space in name...
                    patientname = patientname.Replace(", ", ",");   // hack for extra space in name...
                    modArg3.Value = "\"" + patientname.Replace(",", "^") + "\"";
                    args.Add(modArg3);

                    try
                    {
                        if (!String.IsNullOrEmpty(dob))
                        {
                            dob = DateTime.ParseExact(dob, "MM/dd/yyyy", CultureInfo.InvariantCulture).ToString("yyyyMMdd");    // FMH preferred format... DICOM Standard??
                        }
                    }
                    catch (Exception) { }   // else use dob as found in database

                    TripleArg modArg4 = new TripleArg();   // Date of Birth
                    modArg4.ArgSwitch = "-m";
                    modArg4.Name = "PatientBirthDate";  // DICOM literal string, do not modify
                    modArg4.Value = dob;
                    args.Add(modArg4);

                    TripleArg modArg5 = new TripleArg();   // Gender
                    modArg5.ArgSwitch = "-m";
                    modArg5.Name = "PatientSex";    // DICOM literal string, do not modify
                    modArg5.Value = gender;
                    args.Add(modArg5);

                    try
                    {
                        if (!String.IsNullOrEmpty(apptDate))
                        {
                            apptDate = DateTime.ParseExact(apptDate, "MM/dd/yyyy", CultureInfo.InvariantCulture).ToString("yyyyMMdd");
                        }
                    }
                    catch (Exception) { }   // else use apptDate as found in database

                    TripleArg modArg6 = new TripleArg();   // Study Date
                    modArg6.ArgSwitch = "-m";
                    modArg6.Name = "StudyDate"; // DICOM literal string, do not modify
                    modArg6.Value = apptDate;
                    args.Add(modArg6);

                    if ((iface.PacsSubType == (int)pacsSubTypes.JPG) || (iface.PacsSubType == (int)pacsSubTypes.BMP))  // pdf already has this in the metadata; img2dcm won't take patient id argument like pdf2dcm, for reasons that escape me
                    {
                        TripleArg modArg7 = new TripleArg();   // MRN
                        modArg7.ArgSwitch = "-m";
                        modArg7.Name = "PatientID"; // DICOM literal string, do not modify
                        modArg7.Value = patient.unitnum;
                        args.Add(modArg7);
                    }

                    try
                    {
                        if (!String.IsNullOrEmpty(apptTime))
                        {
                            apptTime = DateTime.ParseExact(apptTime, "hh:mm tt", CultureInfo.InvariantCulture).ToString("hhmm") + "00";
                            TripleArg modArg8 = new TripleArg();   // Date of Birth
                            modArg8.ArgSwitch = "-m";
                            modArg8.Name = "StudyTime"; // DICOM literal string, do not modify
                            modArg8.Value = apptTime;
                            args.Add(modArg8);
                        }
                    }
                    catch (Exception) { }   // no time?  no big deal.

                    TripleArg modArg9 = new TripleArg();   // Study ID
                    modArg9.ArgSwitch = "-m";
                    modArg9.Name = "StudyID"; // DICOM literal string, do not modify
                    //modArg9.Value = "1";
                    modArg9.Value = iface.InterfaceId.ToString();
                    args.Add(modArg9);

                    TripleArg modArg10 = new TripleArg();   // Series ID
                    modArg10.ArgSwitch = "-m";
                    modArg10.Name = "SeriesNumber"; // DICOM literal string, do not modify
                    //modArg10.Value = "1";
                    modArg10.Value = iface.InterfaceId.ToString();
                    args.Add(modArg10);

                    TripleArg modArg11 = new TripleArg();   // Instance
                    modArg11.ArgSwitch = "-m";
                    modArg11.Name = "InstanceNumber"; // DICOM literal string, do not modify
                    //modArg11.Value = "1";
                    modArg11.Value = iface.InterfaceId.ToString();
                    args.Add(modArg11);

                    wrapDCMTKmethodAsExternalCommand(null, null, iface.DCMTKpath + "\\dcmodify.exe", args);

                    args.Clear();

                    TripleArg sendArg1 = new TripleArg();   // file to xmit
                    sendArg1.ArgSwitch = "\"" + dcmFilename + "\"";
                    args.Add(sendArg1);

                    TripleArg sendArg2 = new TripleArg();   // ae title HRA
                    sendArg2.ArgSwitch = "-aet";
                    sendArg2.Name = iface.AeTitleLocal;
                    args.Add(sendArg2);

                    TripleArg sendArg3 = new TripleArg();   // ae title remote
                    sendArg3.ArgSwitch = "-aec";
                    sendArg3.Name = iface.AeTitleRemote;
                    args.Add(sendArg3);

                    if (!String.IsNullOrEmpty(iface.AdditionalParams))
                    {
                        TripleArg sendArg4 = new TripleArg();   // additional params
                        sendArg4.ArgSwitch = iface.AdditionalParams;
                        args.Add(sendArg4);
                    }

                    // Send it out... finally!
                    dicomOutputArgs = wrapDCMTKmethodAsExternalCommand(iface.IpAddress, iface.Port, iface.DCMTKpath + "\\storescu.exe", args);
                    // todo: parse out any useful returned values... in the meantime, unless an error is thrown we'll return success
                    Logger.Instance.WriteToLog("Send2PACS: Successful send to " + iface.AeTitleRemote + " for Interface #" + iface.InterfaceId + " " + (apptId > 0 ? " for appt id " + apptId.ToString() : ""));

                    try
                    {
                        File.Delete(dcmFilename);   // no point in keeping these around.
                        File.Delete(dcmFilename + ".bak");
                        File.Delete(filename);
                    }
                    catch (Exception e)
                    {
                        Logger.Instance.WriteToLog("Send2PACS: Unable to delete " + dcmFilename + ", underlying error was " + e.Message);
                    }
                }
            }

            // if some interfaces were processed, return true.
            if (interfaces.Count > 0) return true;
            else return false;
        }