396 lines
17 KiB
PHP
396 lines
17 KiB
PHP
<?php
|
|
/*
|
|
* Use Case: Untuk API antara BISONE dengan DCM4CHE PACS Server
|
|
* Location: PACS Server /data/docker/nv/html/ApiPacs.php
|
|
* Input: JSON
|
|
* Proses: HL7 ke DCM4CHE
|
|
* Return: JSON
|
|
* Version: 1.0
|
|
* Date: 2024-12-13
|
|
*/
|
|
header('Content-Type: application/json');
|
|
|
|
// Entry point
|
|
require_once 'ApiPacs.php';
|
|
|
|
// Create an instance and route the request
|
|
$apiPacs = new ApiPacs();
|
|
$apiPacs->route();
|
|
|
|
|
|
class ApiPacs
|
|
{
|
|
private $dcmHost = '128.199.154.150';
|
|
private $dcmPort = 2575;
|
|
|
|
public function route()
|
|
{
|
|
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
|
$action = basename($path);
|
|
|
|
if (method_exists($this, $action)) {
|
|
$this->$action();
|
|
} else {
|
|
http_response_code(404);
|
|
$this->sys_error("Endpoint not found");
|
|
}
|
|
}
|
|
|
|
public function sendOrder()
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
try {
|
|
$json = file_get_contents('php://input');
|
|
$payload = json_decode($json, true);
|
|
|
|
if (!$payload) {
|
|
http_response_code(400);
|
|
$this->sys_error("Invalid JSON");
|
|
exit;
|
|
}
|
|
|
|
$hl7json = $this->mapToXml($payload, "XO"); // XO = Send Order
|
|
|
|
$hl7Message = $this->mapToHL7($hl7json);
|
|
|
|
$result = $this->sendHL7($hl7Message, $this->dcmHost, $this->dcmPort);
|
|
|
|
$this->sys_ok([
|
|
'accession_no' => $payload['accession_no'],
|
|
'study_instance_uid' => $payload['study_instance_uid'],
|
|
'hl7_response' => $result,
|
|
], "Successfully sent order");
|
|
} catch (\Exception $e) {
|
|
$this->sys_error($e->getMessage());
|
|
exit;
|
|
}
|
|
} else {
|
|
http_response_code(405);
|
|
$this->sys_error("Method not allowed");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
public function cancelOrder()
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
try {
|
|
// Get the raw POST data
|
|
$json = file_get_contents('php://input');
|
|
$payload = json_decode($json, true);
|
|
|
|
if (!$payload) {
|
|
http_response_code(400);
|
|
$this->sys_error("Invalid JSON");
|
|
exit;
|
|
}
|
|
|
|
$hl7json = $this->mapToXml($payload, "CA"); // CA = Cancel Order
|
|
|
|
$hl7Message = $this->mapToHL7($hl7json);
|
|
|
|
$result = $this->sendHL7($hl7Message, $this->dcmHost, $this->dcmPort);
|
|
|
|
$this->sys_ok([
|
|
'accession_no' => $payload['accession_no'],
|
|
'study_instance_uid' => $payload['study_instance_uid'],
|
|
'hl7_response' => $result,
|
|
], "Successfully canceled order");
|
|
} catch (\Exception $e) {
|
|
$this->sys_error($e->getMessage());
|
|
exit;
|
|
}
|
|
} else {
|
|
http_response_code(405);
|
|
$this->sys_error("Method not allowed");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
private function mapToXml($payload, $act)
|
|
{
|
|
// Extract variables from JSON payload
|
|
$DepartemenID = isset($payload['departemen_id']) ? $payload['departemen_id'] : "";
|
|
$message_seq_id = "2006 - 1010";
|
|
$patient_mrn = isset($payload['patient_mrn']) ? $payload['patient_mrn'] : "";
|
|
$patient_nm = isset($payload['patient_nm']) ? $payload['patient_nm'] : "";
|
|
$patient_birth_dt = isset($payload['patient_birth_dt']) ? $payload['patient_birth_dt'] : "";
|
|
$patient_gender = isset($payload['patient_gender']) ? $payload['patient_gender'] : "";
|
|
$referring_physician_id = isset($payload['referring_physician_id']) ? $payload['referring_physician_id'] : "";
|
|
$referring_physician_nm = isset($payload['referring_physician_nm']) ? $payload['referring_physician_nm'] : "";
|
|
$admission_dttm = isset($payload['admission_dttm']) ? $payload['admission_dttm'] : "";
|
|
$admission_id = isset($payload['admission_id']) ? $payload['admission_id'] : "";
|
|
$ordering_physician_id = isset($payload['ordering_physician_id']) ? $payload['ordering_physician_id'] : "";
|
|
$ordering_physician_nm = isset($payload['ordering_physician_nm']) ? $payload['ordering_physician_nm'] : "";
|
|
$NamaDepartemen = isset($payload['nama_departemen']) ? $payload['nama_departemen'] : "";
|
|
$placer_order_id = isset($payload['placer_order_id']) ? $payload['placer_order_id'] : "";
|
|
$filler_order_id = isset($payload['filler_order_id']) ? $payload['filler_order_id'] : "";
|
|
$procedure_cd = isset($payload['procedure_cd']) ? $payload['procedure_cd'] : "";
|
|
$procedure_nm = isset($payload['procedure_nm']) ? $payload['procedure_nm'] : "";
|
|
$order_dttm = isset($payload['order_dttm']) ? $payload['order_dttm'] : "";
|
|
$accession_no = isset($payload['accession_no']) ? $payload['accession_no'] : "";
|
|
$requested_proc_id = isset($payload['requested_proc_id']) ? $payload['requested_proc_id'] : "";
|
|
$scheduled_proc_step_id = isset($payload['scheduled_proc_step_id']) ? $payload['scheduled_proc_step_id'] : "";
|
|
$modality_cd = isset($payload['modality_cd']) ? $payload['modality_cd'] : "";
|
|
$study_instance_uid = isset($payload['study_instance_uid']) ? $payload['study_instance_uid'] : "";
|
|
$scheduled_station_ae = isset($payload['scheduled_station_ae']) ? $payload['scheduled_station_ae'] : "";
|
|
$scheduled_station_name = isset($payload['scheduled_station_name']) ? $payload['scheduled_station_name'] : "";
|
|
$trxLayananID = isset($payload['trx_layanan_id']) ? $payload['trx_layanan_id'] : "";
|
|
|
|
$hl7json = [
|
|
"MSH" => [
|
|
"MSH.3.1" => "HIMS", // Sending Application ID
|
|
"MSH.4.1" => $DepartemenID, // Sending Facility ID
|
|
"MSH.5.1" => "ABPACS", // Receiving Application ID
|
|
"MSH.6.1" => "RAD", // Receiving Facility ID
|
|
"MSH.7.1" => $this->getCurrentDate("YmdHis"),
|
|
"MSH.10" => $message_seq_id, /// Messeage Sequence ID
|
|
],
|
|
"PID" => [
|
|
"PID.3.1" => $patient_mrn,
|
|
"PID.5.1" => $patient_nm,
|
|
"PID.7.1" => date("Ymd", strtotime($patient_birth_dt)),
|
|
"PID.8.1" => $patient_gender,
|
|
"PID.10.1" => "2038-8",
|
|
"PID.11.1" => "", // Street Address
|
|
"PID.11.2" => "", // Other Designation
|
|
"PID.11.3" => "", // City
|
|
"PID.11.4" => "", // State or Province
|
|
"PID.11.5" => "", // Zip Code
|
|
"PID.13.1" => "", // Phone Number
|
|
"PID.18.1" => "", // Patient Account Number
|
|
],
|
|
"PV1" => [
|
|
"PV1.8.1" => $referring_physician_id,
|
|
"PV1.8.2" => $referring_physician_nm,
|
|
"PV1.44.1" => date("YmdHis", strtotime($admission_dttm)),
|
|
"PV1.19.1" => $admission_id, // (0038,0010) Admission ID / RegID
|
|
"PV1.19.4" => "RSAB", // (0038,0011) Issuer of Admission ID
|
|
],
|
|
"ORC" => [
|
|
"ORC.1.1" => $act, // Action Code
|
|
"ORC.2.1" => $placer_order_id, // Placer Order Number
|
|
"ORC.2.2" => "HIMS", // Placer Order Application Name
|
|
"ORC.3.1" => $filler_order_id, // Filler Order Number
|
|
"ORC.3.2" => "", // Filler Order Application Name
|
|
"ORC.10.1" => "", // User ID created
|
|
"ORC.10.2" => "", // User last name who created
|
|
"ORC.10.3" => "", // User first name who created
|
|
"ORC.12.1" => $ordering_physician_id, // Ordering physician ID
|
|
"ORC.12.2" => $ordering_physician_nm, // Ordering physician name
|
|
"ORC.12.3" => "", // Given name
|
|
"ORC.12.4" => "", // Middle initial
|
|
"ORC.12.5" => "", // Suffix
|
|
"ORC.12.6" => "", // Prefix
|
|
"ORC.13.1" => $NamaDepartemen, // Enterer's point of care
|
|
"ORC.14.1" => "", // Call back phone number
|
|
"ORC.15.1" => "", // Order effective datetime
|
|
"ORC.21.1" => $NamaDepartemen, // Enterer's location
|
|
],
|
|
"OBR" => [
|
|
"OBR.2.1" => $placer_order_id, // Placer Order Number
|
|
"OBR.2.2" => "HIMS", // Placer Order Number Namespace ID
|
|
"OBR.3.1" => $filler_order_id, // Filler Order Number
|
|
"OBR.3.2" => "", // Filler Order Number Namespace ID
|
|
"OBR.4.1" => $procedure_cd, // Service ID
|
|
"OBR.4.2" => $procedure_nm, // Service Name
|
|
"OBR.4.3" => "RSABPRC", // Coding for Service Identifier
|
|
"OBR.4.4" => $procedure_cd, // Alternate Service ID
|
|
"OBR.4.5" => $procedure_nm, // Alternate Service Name
|
|
"OBR.4.6" => "L", // Name of Alternate Coding System
|
|
"OBR.6.1" => date("YmdHis", strtotime($order_dttm)), // Order Request Datetime
|
|
"OBR.16.1" => $ordering_physician_id, // Ordering physician ID
|
|
"OBR.16.2" => $ordering_physician_nm, // Ordering physician name
|
|
"OBR.18.1" => $accession_no, // Accession Number
|
|
"OBR.19.1" => $trxLayananID, // Requested Procedure ID
|
|
"OBR.20.1" => $scheduled_proc_step_id, // Scheduled Procedure Step ID
|
|
"OBR.24.1" => $modality_cd, // Modality Code
|
|
"OBR.44.1" => $procedure_cd, // Procedure Code
|
|
"OBR.44.2" => $procedure_nm, // Study Description
|
|
"OBR.44.3" => "RSAB-PCS", // Procedure Coding System
|
|
],
|
|
"ZDS" => [
|
|
"ZDS.1.1" => $study_instance_uid, // (0020,000D) Study Instance UID
|
|
"ZDS.2.1" => $scheduled_station_ae, // (0040,0001) Scheduled Station AE Title
|
|
"ZDS.3.1" => $scheduled_station_name, /// (0040,0010) Scheduled Station Name
|
|
],
|
|
"TQ1" => [
|
|
"TQ1.7.1" => date("YmdHis", strtotime($order_dttm)),
|
|
"TQ1.9.1" => "S", // Priority S = Stat, A = ASAP
|
|
"TQ1.12.1" => "C", // C = Time is Actuation Time
|
|
],
|
|
];
|
|
|
|
return $hl7json;
|
|
}
|
|
|
|
private function mapToHL7($hl7json)
|
|
{
|
|
$segments = [];
|
|
|
|
// MSH Segment
|
|
$segments[] = "MSH|^~\\&|" .
|
|
$hl7json["MSH"]["MSH.3.1"] . "|" .
|
|
$hl7json["MSH"]["MSH.4.1"] . "|" .
|
|
$hl7json["MSH"]["MSH.5.1"] . "|" .
|
|
$hl7json["MSH"]["MSH.6.1"] . "|" .
|
|
$hl7json["MSH"]["MSH.7.1"] . "||" .
|
|
"ORM^O01|" .
|
|
$hl7json["MSH"]["MSH.10"] . "|P|2.3.1";
|
|
|
|
// PID Segment
|
|
$segments[] = "PID|||" .
|
|
$hl7json["PID"]["PID.3.1"] . "^^^ADT1||" .
|
|
$hl7json["PID"]["PID.5.1"] . "||" .
|
|
$hl7json["PID"]["PID.7.1"] . "|" .
|
|
$hl7json["PID"]["PID.8.1"] . "||" .
|
|
$hl7json["PID"]["PID.10.1"] . "|" .
|
|
implode("^", [
|
|
$hl7json["PID"]["PID.11.1"],
|
|
$hl7json["PID"]["PID.11.2"],
|
|
$hl7json["PID"]["PID.11.3"],
|
|
$hl7json["PID"]["PID.11.4"],
|
|
$hl7json["PID"]["PID.11.5"]
|
|
]) . "|" .
|
|
$hl7json["PID"]["PID.13.1"] . "|" .
|
|
$hl7json["PID"]["PID.18.1"];
|
|
|
|
// PV1 Segment
|
|
$segments[] = "PV1||O||||||" .
|
|
$hl7json["PV1"]["PV1.8.1"] . "^" .
|
|
$hl7json["PV1"]["PV1.8.2"] . "|||||||||||" .
|
|
$hl7json["PV1"]["PV1.19.1"] . "^^^" .
|
|
$hl7json["PV1"]["PV1.19.4"] . "||||||||" .
|
|
$hl7json["PV1"]["PV1.44.1"];
|
|
|
|
// ORC Segment
|
|
$segments[] = "ORC|" . $hl7json["ORC"]["ORC.1.1"] . "|" .
|
|
$hl7json["ORC"]["ORC.2.1"] . "^" . $hl7json["ORC"]["ORC.2.2"] . "|" .
|
|
$hl7json["ORC"]["ORC.3.1"] . "^" . $hl7json["ORC"]["ORC.3.2"] . "||||||||" .
|
|
$hl7json["ORC"]["ORC.10.1"] . "^" .
|
|
$hl7json["ORC"]["ORC.10.2"] . "^" .
|
|
$hl7json["ORC"]["ORC.10.3"] . "|||" .
|
|
$hl7json["ORC"]["ORC.12.1"] . "^" .
|
|
$hl7json["ORC"]["ORC.12.2"] . "^" .
|
|
$hl7json["ORC"]["ORC.12.3"] . "^" .
|
|
$hl7json["ORC"]["ORC.12.4"] . "^" .
|
|
$hl7json["ORC"]["ORC.12.5"] . "^" .
|
|
$hl7json["ORC"]["ORC.12.6"] . "|" .
|
|
$hl7json["ORC"]["ORC.13.1"];
|
|
|
|
// OBR Segment
|
|
$segments[] = "OBR|1|" .
|
|
$hl7json["OBR"]["OBR.2.1"] . "^" . $hl7json["OBR"]["OBR.2.2"] . "|" .
|
|
$hl7json["OBR"]["OBR.3.1"] . "^" . $hl7json["OBR"]["OBR.3.2"] . "|" .
|
|
$hl7json["OBR"]["OBR.4.1"] . "^" .
|
|
$hl7json["OBR"]["OBR.4.2"] . "^" .
|
|
$hl7json["OBR"]["OBR.4.3"] . "^" .
|
|
$hl7json["OBR"]["OBR.4.4"] . "^" .
|
|
$hl7json["OBR"]["OBR.4.5"] . "^" .
|
|
$hl7json["OBR"]["OBR.4.6"] . "||" .
|
|
$hl7json["OBR"]["OBR.6.1"] . "||||||||" .
|
|
$hl7json["OBR"]["OBR.16.1"] . "^" .
|
|
$hl7json["OBR"]["OBR.16.2"] . "||||" .
|
|
$hl7json["OBR"]["OBR.18.1"] . "|" .
|
|
$hl7json["OBR"]["OBR.19.1"] . "|" .
|
|
$hl7json["OBR"]["OBR.20.1"] . "|||||||||" .
|
|
$hl7json["OBR"]["OBR.24.1"] . "|||||||" .
|
|
$hl7json["OBR"]["OBR.44.1"] . "^" .
|
|
$hl7json["OBR"]["OBR.44.2"] . "^" .
|
|
$hl7json["OBR"]["OBR.44.3"];
|
|
|
|
// ZDS Segment
|
|
$segments[] = "ZDS|" .
|
|
$hl7json["ZDS"]["ZDS.1.1"] . "^100^Application^DICOM|" .
|
|
$hl7json["ZDS"]["ZDS.2.1"] . "|" .
|
|
$hl7json["ZDS"]["ZDS.3.1"];
|
|
|
|
// TQ1 Segment
|
|
$segments[] = "TQ1|||||||" .
|
|
$hl7json["TQ1"]["TQ1.7.1"] . "||" .
|
|
$hl7json["TQ1"]["TQ1.9.1"] . "|||" .
|
|
$hl7json["TQ1"]["TQ1.12.1"];
|
|
|
|
// Join all segments with newline
|
|
return implode("\n", $segments);
|
|
}
|
|
|
|
private function sendHL7($hl7Message, $host, $port)
|
|
{
|
|
// MLLP delimiters
|
|
define('MLLP_START', "\x0b"); // <VT>
|
|
define('MLLP_END', "\x1c\x0d"); // <FS><CR>
|
|
|
|
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
|
if ($socket === false) {
|
|
$this->sys_error("Socket creation failed: " . socket_strerror(socket_last_error()));
|
|
exit;
|
|
}
|
|
|
|
$result = socket_connect($socket, $host, $port);
|
|
if ($result === false) {
|
|
$this->sys_error("Connection failed: " . socket_strerror(socket_last_error()));
|
|
exit;
|
|
}
|
|
|
|
// Wrap message in MLLP container
|
|
$mlllpMessage = MLLP_START . $hl7Message . MLLP_END;
|
|
|
|
// Send message
|
|
$sentBytes = socket_write($socket, $mlllpMessage, strlen($mlllpMessage));
|
|
if ($sentBytes === false) {
|
|
$this->sys_error("Send failed: " . socket_strerror(socket_last_error()));
|
|
exit;
|
|
}
|
|
|
|
$response = '';
|
|
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ["sec" => 5, "usec" => 0]); // Set timeout
|
|
|
|
while (($buf = socket_read($socket, 1024))) {
|
|
$response .= $buf;
|
|
|
|
if (strpos($response, MLLP_END)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$returned = '';
|
|
if (strpos($response, "MSA|AA") !== false) {
|
|
$returned = "Application Accepted"; // Acknowledgment Accepted
|
|
} elseif (strpos($response, "MSA|AR") !== false) {
|
|
$returned = "Application Rejected"; // Acknowledgment Rejected
|
|
} elseif (strpos($response, "MSA|AE") !== false) {
|
|
$returned = "Application Error"; // Application Error
|
|
} else {
|
|
$returned = "Unknown Response"; // In case none of the expected segments are found
|
|
}
|
|
|
|
return $returned;
|
|
}
|
|
|
|
public function getCurrentDate($format)
|
|
{
|
|
return date($format);
|
|
}
|
|
|
|
public function sys_ok($data, $message = "")
|
|
{
|
|
$result = array(
|
|
'status' => 'OK',
|
|
'message' => $message,
|
|
'data' => $data
|
|
);
|
|
echo json_encode($result);
|
|
}
|
|
|
|
public function sys_error($message)
|
|
{
|
|
$result = array(
|
|
'status' => 'ERROR',
|
|
'message' => $message,
|
|
'data' => ''
|
|
);
|
|
echo json_encode($result);
|
|
}
|
|
}
|