1) { $command = _JAVA_HOME . ' LANG=' . $pacs->getLocale() . ' ' . PATH_BASE_DCM4CHE2 . 'dcmqr -device ' . AETITLE_GATEWAY . ' ' . $pacs->getDicomServer() . $qFilter . $extraFields; //echo $command; //exit; _debuglog($command); $outputRes = array(); exec($command, $outputRes); $dicom = processResponse($outputRes, $pacs->encoding, TRUE, FALSE, TRUE); $n = 0; foreach ($dicom as $k => $v) { $accession_no = $v["0008,0050"]["value"]; $study_iuid = $v["0020,000D"]["value"]; $study_id = $v["0020,0010"]["value"]; $study_description = strtoupper(addslashes(trim($v["0008,1030"]["value"]))); $study_date = $v["0008,0020"]["value"]; $study_time = $v["0008,0030"]["value"]; $study_datetime = $study_date . $study_time; $study_datetime_str = date("Y-m-d H:i", strtotime(substr($study_datetime, 0, 14))); $number_of_series = $v["0020,1206"]["value"]; $number_of_instances = $v["0020,1208"]["value"]; $modality = $v["0008,0061"]["value"]; $patient_name = $v["0010,0010"]["value"]; $patient_date_of_birth = $v["0010,0030"]["value"]; $patient_sex = $v["0010,0040"]["value"]; $patient_mrn = $v["0010,0020"]["value"]; $n = $study_datetime . "." . $accession_no; $ret["studies"][$n]["accession_no"] = $accession_no; $ret["studies"][$n]["study_iuid"] = $study_iuid; $ret["studies"][$n]["study_description"] = $study_description; $ret["studies"][$n]["study_datetime"] = $study_datetime_str; $ret["studies"][$n]["number_of_series"] = $number_of_series; $ret["studies"][$n]["number_of_instances"] = $number_of_instances; $ret["studies"][$n]["modality"] = $modality; $ret["studies"][$n]["patient_mrn"] = $patient_mrn; $ret["studies"][$n]["patient_name"] = $patient_name; $ret["studies"][$n]["patient_sex"] = $patient_sex; $n++; } krsort($ret["studies"]); $json = json_encode($ret, JSON_PRETTY_PRINT); echo $json; } else { $ret["studies"] = null; $json = json_encode($ret, JSON_PRETTY_PRINT); echo $json; } } // 20120112: Parameters are obtained directly from $_GET // ToDo: Dealing with errors (returning error information) in the case of incorrect parameters function dicomQRAccessionNumber($pacs) { header('Content-type: text/json'); // Nivel Q/R | | -P | -S | -I $extraFields = '-r 00080061 -r 00081030 -r 00100010 -r 00100021 -r 00100030 -r 00100040 -r 00201206 -r 00201208 -r 00100020'; // (0008,0061) Modalities in Study // (0008,1030) Study Description // (0010,0010) Patient's Name // (0010,0021) Issuer of Patient ID // (0010,0030) Patient's Birth Date // (0010,0040) Patient's Sex // (0010,0020) LO #6 [72471] Patient ID // (0020,1206) Number of Study Related Series // (0020,1208) Number of Study Related Instances // (0020,000D) Study IUID $dbhis = new Database(_HIS_DB_HOST, _HIS_DB_USER, _HIS_DB_PASS, _HIS_DB_NAME, _HIS_DB_PORT); $AccessionNumber = $_GET["AccessionNumber"]; $sql = "SELECT RegID,TrxLayananID FROM pacs_result_series WHERE AccessionNumber = '$AccessionNumber' LIMIT 1"; $result = $dbhis->query($sql); $exp = NULL; if ($dbhis->getRowsNum($result) > 0) { $exp = array(); list($RegID, $TrxLayananID) = $dbhis->fetchRow($result); $sql = "SELECT a.Ekspertise,a.EkspertiseBuatOleh,b.Nama,a.EkspertiseBuatTanggal," . "a.EkspertiseEditOleh,c.Nama,a.EkspertiseEditTanggal" . " FROM trxlayananradiologi a" . " LEFT JOIN user b ON b.UserID = a.EkspertiseBuatOleh" . " LEFT JOIN user c ON c.UserID = a.EkspertiseEditOleh" . " WHERE a.TrxLayananID = '$TrxLayananID'" . " AND a.NA = 'N'"; $result = $dbhis->query($sql); _debuglog($sql); if ($dbhis->getRowsNum($result) > 0) { while (list( $expertise, $expertise_user_id, $expertise_user_nm, $expertise_dttm, $expertise_edit_user_id, $expertise_edit_user_nm, $expertise_edit_dttm ) = $dbhis->fetchRow($result)) { if (trim($expertise) == "") continue; if (trim($expertise_user_id) == "") continue; $sql = "SELECT TanggalBuat,TrxDepartemenID FROM trxlayanan WHERE TrxLayananID = '$TrxLayananID'"; $rtr = $dbhis->query($sql); _debuglog($sql); $ordering_physician = ""; if ($dbhis->getRowsNum($rtr) > 0) { list($order_dttm, $TrxDepartemenID) = $dbhis->fetchRow($rtr); $sql = "SELECT b.Nama FROM trxdepartemen a" . " LEFT JOIN dokter b ON b.DokterID = a.Dokter2" . " WHERE a.TrxDepartemenID = '$TrxDepartemenID'"; $rdr = $dbhis->query($sql); _debuglog($sql); if ($dbhis->getRowsNum($rdr) > 0) { list($ordering_physician) = $dbhis->fetchRow($rdr); } } $exp[] = array( "expertise" => $expertise, "radiologist" => $expertise_user_nm, "expertise_dttm" => substr($expertise_dttm, 0, -3), "radiologist_edit" => $expertise_edit_user_nm, "expertise_edit_dttm" => substr($expertise_edit_dttm, 0, -3), "ordering_physician" => $ordering_physician ); } } } // ToDo: Take also into account PatientIdIssuer $qPatId = isset($_GET['PatientID']) ? " -q 00100020='" . $_GET['PatientID'] . "'" : ""; $qAccessionNumber = isset($_GET['AccessionNumber']) ? " -q 00080050='" . $_GET['AccessionNumber'] . "'" : ""; $qStudyDate = isset($_GET['studyDate']) ? " -q StudyDate={$_GET['studyDate']}" : ""; // AAAAMMDD ??? $qFilter = $qPatId . $qAccessionNumber . $qStudyDate . " "; // $command = 'LANG='.$pacs->getLocale().' '.PATH_BASE_DCM4CHE2.'dcmqr -device '.AETITLE_GATEWAY.' '.$pacs->getDicomServer()." -q 00100020='$patientId' $extraFields"; if (strlen($qFilter) > 1) { $command = _JAVA_HOME . ' LANG=' . $pacs->getLocale() . ' ' . PATH_BASE_DCM4CHE2 . 'dcmqr -device ' . AETITLE_GATEWAY . ' ' . $pacs->getDicomServer() . $qFilter . $extraFields; _debuglog($command); $outputRes = array(); exec($command, $outputRes); $dicom = processResponse($outputRes, $pacs->encoding, TRUE); $accession_no = $dicom["0008,0050"]["value"]; $study_iuid = $dicom["0020,000D"]["value"]; $study_id = $dicom["0020,0010"]["value"]; $study_description = addslashes(trim($dicom["0008,1030"]["value"])); $study_date = $dicom["0008,0020"]["value"]; $study_time = $dicom["0008,0030"]["value"]; $study_datetime = $study_date . $study_time; $number_of_series = $dicom["0020,1206"]["value"]; $number_of_instances = $dicom["0020,1208"]["value"]; $modality = $dicom["0008,0061"]["value"]; $patient_name = $dicom["0010,0010"]["value"]; $patient_date_of_birth = $dicom["0010,0030"]["value"]; $patient_sex = $dicom["0010,0040"]["value"]; $patient_mrn = $dicom["0010,0020"]["value"]; $patient_age = dateDifference($study_date, $patient_date_of_birth); if (trim($study_iuid) == "") { ////////// empty study iuid, image not pushed yet to the pacs $ret["study"] = NULL; $json = json_encode($ret); echo $json; exit(0); } $dicomseries = dicomQRSeries($pacs, $study_iuid); $ret = array(); $ret["study"]["accession_no"] = $accession_no; $ret["study"]["study_iuid"] = $study_iuid; $ret["study"]["study_description"] = $study_description; $ret["study"]["study_datetime"] = $study_datetime; $ret["study"]["number_of_series"] = $number_of_series; $ret["study"]["number_of_instances"] = $number_of_instances; $ret["study"]["modality"] = $modality; $ret["study"]["patient_mrn"] = $patient_mrn; $ret["study"]["patient_name"] = $patient_name; $ret["study"]["patient_sex"] = $patient_sex; $ret["study"]["patient_date_of_birth"] = $patient_date_of_birth; $ret["study"]["patient_age"] = $patient_age; $ret["study"]["expertise"] = $exp; $series_number_json = 0; foreach ($dicomseries as $k => $v) { $cnt = count($v); if ($cnt > 10) { $idx = floor($cnt * 31 / 51); } else { $idx = 0; } $n = 0; $imgs = array(); // $idx = 1; foreach ($v as $kk => $vv) { $sop_iuid = $vv["0008,0018"]["value"]; $series_iuid = $vv["0020,000E"]["value"]; $series_number = $vv["0020,0011"]["value"]; $series_description = addslashes(trim($vv["0008,103E"]["value"])); $number_of_instances = $cnt; $ret["study"]["series"][$series_number_json]["series_number"] = $series_number; $ret["study"]["series"][$series_number_json]["series_iuid"] = $series_iuid; $ret["study"]["series"][$series_number_json]["series_description"] = $series_description; $ret["study"]["series"][$series_number_json]["number_of_instances"] = $number_of_instances; if ($n == $idx) { $wado_thumb = XOCP_SERVER . XOCP_SERVER_SUBDIR . "/wado_proxy_thumb.php?requestType=WADO&studyUID=${study_iuid}&seriesUID=${series_iuid}&objectUID=${sop_iuid}&rows=123"; // $thumbnail = resize_center_image(193,123,file_get_contents($wado_thumb),90); // $img_base64 = base64_encode($thumbnail); $ret["study"]["series"][$series_number_json]["thumbnail"] = $wado_thumb; // $ret["study"]["series"][$series_number_json]["thumbnail_base64"] = $img_base64; } $imgs[$n] = $sop_iuid; $n++; } $ret["study"]["series"][$series_number_json]["sop_iuids"] = $imgs; $series_number_json++; } $json = json_encode($ret, JSON_PRETTY_PRINT); echo $json; } /* Example of a Q/R answer (at study level) 14:28:31,609 INFO - Query Response #4: (0008,0005) CS #10 [ISO_IR 100] Specific Character Set (0008,0020) DA #8 [20080410] Study Date (0008,0030) TM #14 [103025.000000] Study Time (0008,0050) SH #0 [] Accession Number (0008,0052) CS #6 [STUDY] Query/Retrieve Level (0008,0054) AE #8 [PACSECO] Retrieve AE Title (0008,0056) CS #6 [ONLINE] Instance Availability (0010,0020) LO #6 [72471] Patient ID (0020,000D) UI #62 [1.2.840.113543.6.6.3.4.617968937028517893191307041671843345256] Study Instance U (0020,0010) SH #4 [3379] Study ID (0020,1206) IS #2 [1] Number of Study Related Series (0020,1208) IS #2 [2] Number of Study Related Instances (0088,0130) SH #0 [] Storage Media File-set ID (0088,0140) UI #0 [] Storage Media File-set UID */ } // ***************************************************************************** /** * Performa a QR operation to get series and instances from a given study */ function dicomQRSeries($pacs, $study_IUID) { // Nivel Q/R | | -P | -S | -I // Problema: Algun campo extra lo pide en la subquery de instancias, pero no en la query principal de series $extraFields = '-r 00080060 -r 0008103E -r 00200011 -r 00201209'; // (0008,0060) Modality // (0008,103E) Series Description // Solo se envia en las subquery // (0020,0011) Series Number // (0020,1209) Number of Series Related Instances // Solo se envia en las subquery $command = _JAVA_HOME . ' LANG=' . $pacs->getLocale() . ' ' . PATH_BASE_DCM4CHE2 . 'dcmqr -device ' . AETITLE_GATEWAY . ' -I ' . $pacs->getDicomServer() . " -q 0020000D=$study_IUID $extraFields"; _debuglog($command); $outputRes = array(); exec($command, $outputRes); $dicom = processResponse($outputRes, $pacs->encoding, TRUE, TRUE); return $dicom; } /** * The Query/Retrieve answer is processed * outputRes: Q/R answer string */ function processResponse($outputRes, $encoding, $return_array = FALSE, $series = FALSE, $patient = FALSE) { $pattern = "/^((?:[0|1][0-9]|2[0-3])(?::[0-5][0-9]){2},[0-9]{3})\s([A-Z]+)\s+-\s(.+)$/"; $dicom = array(); $ret = array(); foreach ($outputRes as $numLine => $strLine) { $matches = array(); $numMatches = preg_match($pattern, $strLine, $matches); if ($numMatches == 1) { // $matches[1]: time (hh:mm:ss,mil) // $matches[2]: INFO|ERROR|??? $outputType = $matches[2]; // $matches[3]: info string $outputStr = $matches[3]; if ($outputType == 'ERROR') { return false; } if ($element = identifyPattern($outputStr)) { $lineNum = $numLine + 1; $xmlString = ''; if (isset($element["qrequest"]) && isset($element["number"])) { $request_key = $element["qrequest"] . "/" . $element["number"]; } else if ($patient === TRUE && $element["tag"] == "response") { $request_key = $element["number"]; } else { $request_key = NULL; } while (strlen($outputRes[$lineNum]) > 0) { if ($df = processDicomField($outputRes[$lineNum], $encoding)) { $key = $df["tagGroup"] . "," . $df["tagElement"]; if (isset($request_key)) { if ($patient === TRUE) { $k0 = $element["number"]; $ret[$k0][$key] = $df; } else { $k0 = $element["qrequest"]; $k1 = $element["number"]; $ret[$k0][$k1][$key] = $df; } } else { if ($series === FALSE) { $ret[$key] = $df; } } $xmlString .= $df['xmlString'] . "\n"; } $lineNum++; } $element['xmlString'] = $element['xmlPre'] . $xmlString . $element['xmlPost']; array_push($dicom, $element); } } } if ($return_array) { return $ret; } return $dicom; } // function processResponse(...) function processResponseOriginal($outputRes, $encoding, $return_array = FALSE, $series = FALSE) { $pattern = "/^((?:[0|1][0-9]|2[0-3])(?::[0-5][0-9]){2},[0-9]{3})\s([A-Z]+)\s+-\s(.+)$/"; $dicom = array(); $ret = array(); foreach ($outputRes as $numLine => $strLine) { $matches = array(); $numMatches = preg_match($pattern, $strLine, $matches); if ($numMatches == 1) { // $matches[1]: time (hh:mm:ss,mil) // $matches[2]: INFO|ERROR|??? $outputType = $matches[2]; // $matches[3]: info string $outputStr = $matches[3]; if ($outputType == 'ERROR') { return false; } if ($element = identifyPattern($outputStr)) { $lineNum = $numLine + 1; $xmlString = ''; if (isset($element["qrequest"]) && isset($element["number"])) { $request_key = $element["qrequest"] . "/" . $element["number"]; } else { $request_key = NULL; } while (strlen($outputRes[$lineNum]) > 0) { if ($df = processDicomField($outputRes[$lineNum], $encoding)) { $key = $df["tagGroup"] . "," . $df["tagElement"]; if (isset($request_key)) { $k0 = $element["qrequest"]; $k1 = $element["number"]; $ret[$k0][$k1][$key] = $df; } else { if ($series === FALSE) { $ret[$key] = $df; } } $xmlString .= $df['xmlString'] . "\n"; } $lineNum++; } $element['xmlString'] = $element['xmlPre'] . $xmlString . $element['xmlPost']; array_push($dicom, $element); } } } if ($return_array) { return $ret; } return $dicom; } // function processResponse(...) function processDicomField($dcmString, $encoding) { /* // (0008,0020) DA #8 [20080410] Study Date // Returned values (string): tagGroup = 0008 tagElement = 0020 value = 20080410 tagName = Study Date valueRepr = DA valueLength = 8 (int) */ if (DEBUG_LEVEL >= DEBUG_INFO) { echo "processDicomField($dcmString)
"; } $matches = array(); $pattern = "/^\(([0-9A-F]{4}),([0-9A-F]{4})\)\s([A-Z]{2})\s#([0-9]+)\s\[([^\]]*)\]\s(.*)$/"; $numMatches = preg_match($pattern, $dcmString, $matches); if ($numMatches == 1) { $d = array(); if (DEBUG_LEVEL >= DEBUG_DUMP) { echo "
";
         print_r($matches);
         echo "
"; } $d['tagGroup'] = $matches[1]; $d['tagElement'] = $matches[2]; $d['valueRepr'] = $matches[3]; $d['valueLength'] = $matches[4]; $d['value'] = $matches[5]; $d['tagName'] = $matches[6]; // Characters to convert to UTF-8: Elements with VR=PN, SH, LO, ST, LT, UT in the Data Set. $vr = $d['valueRepr']; if ($vr == 'PN' || $vr == 'SH' || $vr == 'LO' || $vr == 'ST' || $vr == 'LT' || $vr == 'UT') { $d['value'] = iconv($encoding, XML_ENCODING, $d['value']); $d['value'] = htmlspecialchars($d['value'], ENT_QUOTES); // Convert non valid XML characters } $xmlString = "\n"; $xmlString = ""; $xmlString .= ""; $xmlString .= "{$d['value']}"; $d['xmlString'] = $xmlString; } else { if (DEBUG_LEVEL >= DEBUG_DUMP) { echo "NO match!!!
"; } $d = false; } return $d; } function identifyPattern($testStr) { $patterns = array(); $element = false; if (SHOW_REQUEST) { // Send Query Request using 1.2.840.10008.5.1.4.1.2.2.1/Study Root Query/Retrieve Information Model - FIND: $patterns[QUERY_REQUEST_ROOT] = "/^Send Query Request using ([0-9]+(?:\.[0-9]+)+)\/([[:alpha:][:space:]\/\-]+):$/"; // Send Query Request #1/3 using 1.2.840.10008.5.1.4.1.2.2.1/Study Root Query/Retrieve Information Model - FIND: $patterns[QUERY_REQUEST] = "/^Send Query Request #([1-9][0-9]*)\/([1-9][0-9]*) using ([0-9]+(?:\.[0-9]+)+)\/([[:alpha:][:space:]\/\-]+):$/"; } // Query Response #1: $patterns[QUERY_RESPONSE_ROOT] = "/^Query Response #([1-9][0-9]*):$/"; // Query Response #1 for Query Request #1/3: $patterns[QUERY_RESPONSE] = "/^Query Response #([1-9][0-9]*) for Query Request #([1-9][0-9]*)\/([1-9][0-9]*):$/"; $matches = array(); $numMatches = 0; foreach ($patterns as $type => $pattern) { if (DEBUG_LEVEL >= DEBUG_INFO) { echo "Testing $testStr
against pattern: $pattern
"; } $numMatches = preg_match($pattern, $testStr, $matches); if ($numMatches == 1) { $element = array(); $element['xmlPre'] = "\n"; $element['type'] = $type; switch ($type) { case QUERY_REQUEST_ROOT: $element['tag'] = 'request'; $element['xmlPre'] .= "<{$element['tag']} qrim='{$matches[1]}'>\n"; break; case QUERY_RESPONSE_ROOT: $element['tag'] = 'response'; $element['xmlPre'] .= "<{$element['tag']} number='{$matches[1]}'>\n"; $element['number'] = $matches[1]; break; case QUERY_REQUEST: $element['tag'] = 'qrequest'; $element['xmlPre'] .= "<{$element['tag']} number='{$matches[1]}' qrim='{$matches[3]}'>\n"; $element['number'] = $matches[1]; $element['qrim'] = $matches[2]; break; case QUERY_RESPONSE: $element['tag'] = 'qresponse'; $element['xmlPre'] .= "<{$element['tag']} number='{$matches[1]}' qrequest='{$matches[2]}'>\n"; $element['number'] = $matches[1]; $element['qrequest'] = $matches[2]; break; default: $element['tag'] = 'dummy'; $element['xmlPre'] .= "<{$element['tag']}>\n"; break; } $element['xmlPost'] = "\n"; break; // break 2 ??? } } return $element; } /** * Recovers a Dicom object via WADO */ function getWado($pacs, $studyUID, $seriesUID, $objectUID) { // $tStart = microtime(true); $uriWado = $pacs->getUriWado($studyUID, $seriesUID, $objectUID); if (isset($_GET['contentType']) && $_GET['contentType'] == 'application/dicom') { $header = "Content-Type: application/dicom"; $uriWado .= "&contentType=application%2Fdicom"; // 20130502: Force transfer syntax to Explicit VR little endian $uriWado .= "&transferSyntax=1.2.840.10008.1.2.1"; } else { error_log("ERROR jpeg/WADO files should not be used anymore"); $header = "Content-Type: image/jpeg"; } // To get thumbnails: if (isset($_GET['rows']) && isset($_GET['cols'])) { // $uriWado .= "&rows={$_GET['rows']}&cols={$_GET['cols']}"; $uriWado .= "&rows=" . THUMBNAIL_SIZE . "&cols=" . THUMBNAIL_SIZE; } if (RETRIEVE_LOCAL) { // Store a local copy of the Dicom file to obtain its size. $tmpFWado = tempnam('/tmp', 'dicom_'); // $tmpFWado = tempnam('/dev/shm/dicom', 'dicom_'); if ($getOk = getLocalWado($uriWado, $tmpFWado)) { $fp = fopen($tmpFWado, 'rb'); header("Content-Type: application/dicom"); header("Content-Length: " . filesize($tmpFWado)); header("Content-Disposition: inline"); fpassthru($fp); fclose($fp); } else { error_log("Error retrieving WADO/Dicom object"); } unlink($tmpFWado); } else { // header("Cache-Control: public"); // header('Expires: '.gmdate('D, d M Y H:i:s', strtotime('+1 day')).' GMT'); header($header); readfile($uriWado); } /* $tEnd = microtime(true); $tDelta = round(1000 * ($tEnd - $tStart)); $logMsg = "getWado: $tDelta ms"; error_log($logMsg); */ } // ******* ********* ********* ********* ********* ********* ********* ********* function getLocalWado($uriWado, $tmpFWado) { $getOk = false; $numTry = 0; $maxTry = 3; $delayTry = 1; while (!$getOk && $numTry < $maxTry) { if ($numTry > 0) { sleep($delayTry); } $retrieveCommand = PATH_WGET . " '$uriWado' -O $tmpFWado --server-response 2> /dev/stdout | grep Content-Type | awk -F\"Content-Type: \" '{print $2}'"; // error_log($retrieveCommand); $outputCommand = array(); exec($retrieveCommand, $outputCommand); // Verification: The returned contents is a dicom object $getOk = $outputCommand[0] == 'application/dicom'; $numTry++; } return $getOk; } // ******* ********* ********* ********* ********* ********* ********* *********