Пример #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;
        }
Пример #2
0
        /// <summary>
        /// Find an interface specific to fetching appointments from a DICOM Modality Worklist (MWL) and get the appointments into the database.
        /// effective 1/22/16 the values required to be present in lkpInterfaceDefinitions are as follows:
        /// interfaceType = "ApptMWL"
        /// ipAddress, port self explanatory
        /// stringParam1: "their" AET
        /// stringParam2: "our" AET
        /// stringParam3: qualified path to DCMTK binaries, e.g., C:\HUGHES_DCMTK_DICOM\bin
        /// intParam1: (optional) number of minutes BEFORE current time to bound the MWL query
        /// intParam2: (optional) number of minutes AFTER current time to bound the MWL query.  
        ///             Summary: (CurrentTime - intParam1) - Current Time - (Current Time + intParam2).  DEFAULTS to whole day.
        /// stringParam4: Modality to send to MWL.  Defaults to "MG".
        /// stringParam5: Name of DICOM field that contains the ClinicCode (mapping to lkpClinics.ClinicCode).  E.g., ScheduledStationName
        /// </summary>
        /// <param name="clinicID">string representation of clinicID, key to lkpClinics</param>
        /// <returns>true if succeeds, false otherwise</returns>
        public static bool updateApptListFromMWL(string clinicID)
        {
            System.Threading.Mutex appMutex = new System.Threading.Mutex(false, "MWLApptFeed");
            // prevent concurrent MWL fetches.  Just exit if we don't get the mutex, as someone else is updating the same list of appointments.  jdg 1/15/16
            if (appMutex.WaitOne(100, false))
            {
                try
                {
                    bool bSuccessInsertAppt = false;
                    // Probably will just be one appt interface, but why assume?  Let's support multiple ApptMWL's.
                    // I can imagine updating for selected clinic(s) or all clinics.
                    List<InterfaceInstance> interfaces = new List<InterfaceInstance>();
                    SqlDataReader reader = BCDB2.Instance.ExecuteReader("EXEC sp_getInterfaceDefinitionForApptMWL");
                    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.TimeSpanFrom = reader.GetInt32(7);
                        }

                        if (reader.IsDBNull(8) == false)
                        {
                            instance.TimeSpanTo = reader.GetInt32(8);
                        }

                        if (reader.IsDBNull(9) == false)
                        {
                            instance.Modality = reader.GetString(9);
                        }

                        if (reader.IsDBNull(10) == false)
                        {
                            instance.ClinicFieldInMWLQuery = reader.GetString(10);
                        }

                        if (instance.InterfaceId > 0)
                        {
                            interfaces.Add(instance);   // multiple appointment sources per query are supported.  Who knows?  Maybe this will be useful someday.
                        }

                    }

                    foreach (InterfaceInstance iface in interfaces)     // send request to each eligible interface
                    {
                        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>>();

                        TripleArg sendArg1 = new TripleArg();   // file to xmit
                        sendArg1.ArgSwitch = "-W";
                        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);

                        TripleArg sendArg5 = new TripleArg();
                        sendArg5.ArgSwitch = "-k";
                        sendArg5.Name = "(0010,0010)";      // "PatientName";
                        args.Add(sendArg5);

                        TripleArg sendArg6 = new TripleArg();
                        sendArg6.ArgSwitch = "-k";
                        sendArg6.Name = "(0010,0020)";      // "PatientID";
                        args.Add(sendArg6);

                        TripleArg sendArg7 = new TripleArg();
                        sendArg7.ArgSwitch = "-k";
                        sendArg7.Name = "(0008,0092)";      // "ReferringPhysicianAddress", hijacked by NSMC for referring provider id
                        args.Add(sendArg7);

                        TripleArg sendArg8 = new TripleArg();
                        sendArg8.ArgSwitch = "-k";
                        sendArg8.Name = "(0008,0094)";  // "ReferringPhysicianTelephoneNumber", hijacked by NSMC for pcp id
                        args.Add(sendArg8);

                        TripleArg sendArg12 = new TripleArg();
                        sendArg12.ArgSwitch = "-k";
                        sendArg12.Name = "(0040,0100)[0].Modality";
                        sendArg12.Value = iface.Modality;           // no long hardcoded "MG";
                        args.Add(sendArg12);

                        TripleArg sendArg4 = new TripleArg();
                        sendArg4.ArgSwitch = "-k";
                        sendArg4.Name = "(0008,0050)";      // "AccessionNumber"
                        args.Add(sendArg4);

                        TripleArg sendArg9 = new TripleArg();
                        sendArg9.ArgSwitch = "-k";
                        sendArg9.Name = "(0040,0100)[0].ScheduledProcedureStepStartDate";        //"StudyDate";
                        sendArg9.Value = DateTime.Now.ToString("yyyyMMdd");
                        args.Add(sendArg9);

                        TripleArg sendArg10 = new TripleArg();
                        sendArg10.ArgSwitch = "-k";
                        sendArg10.Name = "(0040,0100)[0].ScheduledProcedureStepStartTime";      // "StudyTime";
                        // NOTE NOTE NOTE: jdg 1/14/16 you can't do a date range on a -W query only a -S... according to McKesson.  Maybe other systems will be more DICOM compliant,
                        // so I'll leave this code here.  In any case, if you don't specify the two intParams it's moot anyway.

                        // STUDY TIME RANGE jdg 1/6/15 Use lkpInterfaceDefinitions entry to supply in intParam1 and 2 the number of minutes BEFORE the current time to query
                        // AND the number of minutes AFTER the current time to query.
                        if ((iface.TimeSpanFrom >= 0) && (iface.TimeSpanTo > 0))    // no negative values, suckers.  and no null ranges.
                        {
                            TimeSpan fromSpan = new TimeSpan(0, iface.TimeSpanFrom, 0);
                            TimeSpan toSpan = new TimeSpan(0, iface.TimeSpanTo, 0);
                            DateTime timeFrom = DateTime.Now - fromSpan;
                            DateTime timeTo = DateTime.Now + toSpan;
                            string strTimeFrom = timeFrom.ToString("HHmmss");
                            string strTimeTo = timeTo.ToString("HHmmss");
                            sendArg10.Value = strTimeFrom + "-" + strTimeTo;
                            //Logger.Instance.WriteToLog("MWL: time span from = " + iface.TimeSpanFrom + ", to = " + iface.TimeSpanTo + ", date range = " + strTimeFrom + "-" + strTimeTo);
                        }
                        // end jdg 1/6/15
                        args.Add(sendArg10);

                        TripleArg sendArg11 = new TripleArg();
                        sendArg11.ArgSwitch = "-k";
                        sendArg11.Name = "(0010,0030)";     // "PatientBirthDate"
                        args.Add(sendArg11);

                        TripleArg sendArg13 = new TripleArg();
                        sendArg13.ArgSwitch = "-k";
                        sendArg13.Name = "(0010,0040)";     // "PatientSex"
                        args.Add(sendArg13);

                        try
                        {
                            dicomOutputArgs = wrapDCMTKmethodAsExternalCommand(iface.IpAddress, iface.Port, iface.DCMTKpath + "\\findscu.exe", args);

                            bSuccessInsertAppt = parseAndInsertAppointments(dicomOutputArgs, iface);
                        }
                        catch (Exception e3)
                        {
                            RiskApps3.Utilities.Logger.Instance.WriteToLog("MWL Appt Feed FAILED for interface # " + iface.InterfaceId.ToString() + "; underlying error was: " + e3.ToString());
                        }

                    }
                }
                finally
                {
                    appMutex.ReleaseMutex();
                }

            }   // end mutex

            return true;
        }
Пример #3
0
        /// <summary>
        /// Send the PDF document referenced by "dt" to the PACS system defined in the database lkpInterfaceDefinitions for this document
        /// </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>();
            //int interfaceId = -1;
            //string interfaceType = "";
            //string ipAddress = "";
            //string port = "";
            //string aeTitleRemote = "";
            //string aeTitleLocal = "";
            //string DCMTKpath = "";
            string accessionNumber = "";
            string patientname     = "";
            string dob             = "";
            string gender          = "";
            string apptDate        = "";
            string apptTime        = "";
            //int pacsSubType = (int)pacsSubTypes.PDF;    // NEW jdg 8/7/15... which type of encapsulated DCM to generate?  Default to PDF... NOW 2 ==> JPG
            ParameterCollection pacsArgs = new ParameterCollection();

            pacsArgs.Add("documentTemplateID", dt.documentTemplateID);
            string filename;

            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)
                }

                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)    // 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";
                            htmlToJpgConverter.ConvertHtmlToFile(dt.htmlText, "", System.Drawing.Imaging.ImageFormat.Bmp, filename);
                        }
                    }

                    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) // 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))    // 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     = "OT";
                    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);

                    // 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);
            }
        }