1440 lines
66 KiB
PHP
1440 lines
66 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Carbon\Carbon;
|
|
use App\ResultSample;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
use Exception;
|
|
use App\DataListiner;
|
|
use App\Periksa;
|
|
use App\RekapAntibiotik;
|
|
use App\PendaftaranOnListiner;
|
|
use App\Organisms;
|
|
use App\KomponenJawaban;
|
|
use App\Riwayat;
|
|
|
|
// Proses untuk Growth and Detection Result
|
|
function processGrowthDetectionResult($rawData){
|
|
// Format: R|1| ^ ^ ^GND_MGIT^430100001234|INST_POSITIVE ^87| ...
|
|
$resultData = explode('|', $rawData);
|
|
|
|
return [
|
|
'test_type' => 'Growth/Detection',
|
|
'accession_number' => $resultData[2],
|
|
'status' => $resultData[3], // Positif atau Negatif
|
|
'completion_time' => $resultData[7]
|
|
];
|
|
}
|
|
// Proses untuk Isolate Result
|
|
function processIsolateResult($rawData){
|
|
$resultData = explode('|', $rawData);
|
|
return [
|
|
'test_type' => 'Isolate',
|
|
'isolate_result' => $resultData[3] ?? null,
|
|
'antibiotic' => $resultData[4] ?? null,
|
|
'value' => $resultData[5] ?? null,
|
|
'status' => $resultData[6] ?? null,
|
|
'completion_time' => $resultData[10] ?? null
|
|
];
|
|
}
|
|
// Proses untuk Other Test Result
|
|
function processOtherResult($rawData){
|
|
// Format: R|1| ^ ^ ^OTHER^Seq123|Complete| ...
|
|
$resultData = explode('|', $rawData);
|
|
|
|
return [
|
|
'test_type' => 'Other',
|
|
'sequence_number' => $resultData[3],
|
|
'result' => $resultData[4],
|
|
'description' => $resultData[5],
|
|
'completion_time' => $resultData[6]
|
|
];
|
|
}
|
|
|
|
class AstmMessageService
|
|
{
|
|
/**
|
|
* Membersihkan string dari karakter non-printable
|
|
*/
|
|
private function cleanString($string)
|
|
{
|
|
return preg_replace('/[^\x20-\x7E|]+/', '', $string);
|
|
}
|
|
|
|
/**
|
|
* Parsing data berdasarkan format yang diberikan
|
|
*/
|
|
private function parseResultData($rawData, $format)
|
|
{
|
|
$resultData = explode('|', $rawData);
|
|
$parsedData = [];
|
|
|
|
foreach ($format as $key => $index) {
|
|
$parsedData[$key] = $resultData[$index] ?? null;
|
|
}
|
|
|
|
return $parsedData;
|
|
}
|
|
/**
|
|
* Proses hasil AST Record
|
|
*/
|
|
public function processAstResult($rawData)
|
|
{
|
|
$format = [
|
|
'test_type' => 0,
|
|
'accession_number' => 2,
|
|
'antibiotic' => 3,
|
|
'susceptibility' => 4,
|
|
'value' => 5,
|
|
'status' => 6,
|
|
'completion_time' => 10
|
|
];
|
|
|
|
return $this->parseResultData($rawData, $format);
|
|
}
|
|
/**
|
|
* Proses hasil ID Record
|
|
*/
|
|
public function processIdResult($rawData)
|
|
{
|
|
$format = [
|
|
'test_type' => 0,
|
|
'accession_number' => 2,
|
|
'organism_name' => 3,
|
|
'status' => 6,
|
|
'completion_time' => 10
|
|
];
|
|
|
|
return $this->parseResultData($rawData, $format);
|
|
}
|
|
public function createHeader($senderName, $versionNumber)
|
|
{
|
|
$messageDateTime = Carbon::now()->format('YmdHis');
|
|
return "H|{$senderName}|{$versionNumber}|{$messageDateTime}";
|
|
}
|
|
|
|
|
|
// Fungsi untuk membuat Patient Record (P)
|
|
public function createPatientRecord($patient)
|
|
{
|
|
// Extract fields from $patient array, adjust field positions based on mapping
|
|
return "P|{$patient['id']}|{$patient['last_name']}|{$patient['first_name']}|{$patient['middle_name']}|{$patient['suffix']}|{$patient['dob']}|{$patient['sex']}|{$patient['address']}||{$patient['phone']}|{$patient['admitting_physician']}";
|
|
}
|
|
|
|
// Fungsi untuk membuat Order Record (O)
|
|
public function createOrderRecord($order)
|
|
{
|
|
// Order fields based on mapping (Accession Number, Test ID, etc.)
|
|
return "O|{$order['accession_number']}|{$order['isolate_number']}|{$order['organism']}|{$order['exclude_isolate']}|{$order['priority']}|{$order['collection_date_time']}|{$order['collected_by']}|{$order['received_by']}|{$order['specimen_action_code']}|{$order['isolate_source_test']}|{$order['isolate_source_test_start_time']}|{$order['receipt_date_time']}|{$order['specimen_type']}|{$order['body_site']}|{$order['ordering_physician']}|{$order['ordering_physician_phone']}|{$order['ordering_physician_fax']}|{$order['ordering_physician_pager']}|{$order['specimen_user_field_1']}|{$order['specimen_user_field_2']}|{$order['specimen_user_field_3']}|{$order['specimen_user_field_4']}|{$order['specimen_user_field_5']}|{$order['finalized_date_time']}|{$order['specimen_reimbursement_value']}|{$order['test_reimbursement_value']}|{$order['isolate_classification']}"; }
|
|
public function createTerminatorRecord($order)
|
|
{
|
|
// Order fields based on mapping (Accession Number, Test ID, etc.)
|
|
return "L|1|N";
|
|
}
|
|
// Fungsi untuk membuat pesan lengkap (header + patient + order)
|
|
public function createMessage($senderName, $versionNumber, $patient, $order)
|
|
{
|
|
$header = $this->createHeader($senderName, $versionNumber);
|
|
$patientRecord = $this->createPatientRecord($patient);
|
|
$orderRecord = $this->createOrderRecord($order);
|
|
$terimatorRecord= $this->createTerminatorRecord($order);
|
|
return implode("\n", [$header, $patientRecord, $orderRecord, $terimatorRecord]);
|
|
}
|
|
|
|
/**
|
|
* Proses data ASTM Response
|
|
*/
|
|
protected function astmToDateTime(?string $value): ?string
|
|
{
|
|
if (!$value) return null;
|
|
|
|
if (preg_match('/(\d{14})/', $value, $m)) {
|
|
return Carbon::createFromFormat(
|
|
'YmdHis',
|
|
$m[1]
|
|
)->format('Y-m-d H:i:s');
|
|
}
|
|
|
|
return null;
|
|
}
|
|
protected function splitBDAstmMessages(string $raw): array
|
|
{
|
|
// bersihkan karakter kontrol
|
|
$clean = preg_replace('/[\x02\x03\x04\x17]/', '', $raw);
|
|
|
|
$lines = preg_split("/\r\n|\n|\r/", $clean);
|
|
|
|
$messages = [];
|
|
$current = [];
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
// Ketemu H baru → simpan pesan sebelumnya
|
|
if (str_starts_with($line, 'H|') && !empty($current)) {
|
|
$messages[] = implode("\n", $current);
|
|
$current = [];
|
|
}
|
|
|
|
$current[] = $line;
|
|
|
|
// Akhir pesan
|
|
if (str_starts_with($line, 'L|')) {
|
|
$messages[] = implode("\n", $current);
|
|
$current = [];
|
|
}
|
|
}
|
|
|
|
// sisa buffer
|
|
if (!empty($current)) {
|
|
$messages[] = implode("\n", $current);
|
|
}
|
|
|
|
return $messages;
|
|
}
|
|
protected function reassembleAstmFrames(string $raw): string
|
|
{
|
|
$buffer = '';
|
|
$frames = explode("\x02", $raw);
|
|
|
|
foreach ($frames as $frame) {
|
|
if ($frame === '') {
|
|
continue;
|
|
}
|
|
|
|
$content = $frame;
|
|
$etxPos = strpos($content, "\x03");
|
|
$etbPos = strpos($content, "\x17");
|
|
|
|
if ($etxPos !== false && ($etbPos === false || $etxPos < $etbPos)) {
|
|
$content = substr($content, 0, $etxPos);
|
|
} elseif ($etbPos !== false) {
|
|
$content = substr($content, 0, $etbPos);
|
|
}
|
|
|
|
if ($content !== '' && ctype_digit($content[0])) {
|
|
$content = substr($content, 1);
|
|
}
|
|
|
|
$buffer .= $content;
|
|
}
|
|
|
|
return trim($buffer) !== '' ? trim($buffer) : trim($raw);
|
|
}
|
|
protected function normalizeBDResult(?string $value): ?string
|
|
{
|
|
$value = trim((string) $value);
|
|
if ($value === '') {
|
|
return null;
|
|
}
|
|
|
|
if (stripos($value, 'NEGATIVE') !== false) {
|
|
return 'NEGATIVE';
|
|
}
|
|
|
|
if (stripos($value, 'POSITIVE') !== false) {
|
|
return 'POSITIVE';
|
|
}
|
|
|
|
return explode('^', $value)[0] ?: null;
|
|
}
|
|
protected function processBDAstmResponse(string $raw, $data): bool
|
|
{
|
|
try {
|
|
// =========================
|
|
// PREPARE
|
|
// =========================
|
|
$accnumber= '';
|
|
$resultDateTime = null;
|
|
$bdResult = null;
|
|
$rawClean = preg_replace('/[\x02\x03\x04\x17]/', '', $this->reassembleAstmFrames($raw));
|
|
$lines = preg_split("/\r\n|\n|\r/", $rawClean);
|
|
|
|
$parsed = [
|
|
'header' => [],
|
|
'patient'=> [],
|
|
'order' => [],
|
|
'result' => [],
|
|
'instrument' => []
|
|
];
|
|
|
|
// =========================
|
|
// PARSE LOOP
|
|
// =========================
|
|
foreach ($lines as $line) {
|
|
|
|
if (str_starts_with($line, 'H|')) {
|
|
$f = explode('|', $line);
|
|
$resultDateTime = $this->astmToDateTime($f[13] ?? null);
|
|
|
|
$parsed['header'] = [
|
|
'sender_name' => $data->alat ?? ($f[4] ?? 'BD Instrument'),
|
|
'version_number' => $f[12] ?? 'V1.0',
|
|
'message_datetime' => $this->astmToDateTime($f[13] ?? null)
|
|
];
|
|
} elseif (str_starts_with($line, 'P|')) {
|
|
$f = explode('|', $line);
|
|
$name = explode(' ', $f[5] ?? '');
|
|
|
|
$parsed['patient'] = [
|
|
'patient_id' => $f[3] ?? null,
|
|
'patient_name_last' => $name[0] ?? null,
|
|
'patient_name_first'=> $name[1] ?? null,
|
|
'patient_dob' => $this->astmToDateTime($f[7] ?? null),
|
|
'patient_sex' => $f[8] ?? null,
|
|
'patient_phone' => $f[13] ?? null,
|
|
'address_street' => $f[11] ?? null,
|
|
'hospital_client' => $f[29] ?? null,
|
|
];
|
|
} elseif (str_starts_with($line, 'O|')) {
|
|
$f = explode('|', $line);
|
|
$test = explode('^', $f[4] ?? '');
|
|
$accnumber = $f[2] ?? null;
|
|
$parsed['order'] = [
|
|
'accession_number' => $f[2] ?? null,
|
|
'test_id' => $test[3] ?? null,
|
|
'specimen_type' => $f[15] ?? null,
|
|
'body_site' => $f[16] ?? null,
|
|
'test_start_datetime'=> $this->astmToDateTime($f[7] ?? null),
|
|
];
|
|
} elseif (str_starts_with($line, 'R|')) {
|
|
$f = explode('|', $line);
|
|
$test = explode('^', $f[2] ?? '');
|
|
$bdResult = $this->normalizeBDResult($f[3] ?? null) ?? $bdResult;
|
|
$resultDateTime = $this->astmToDateTime($f[12] ?? null)
|
|
?? $this->astmToDateTime($f[11] ?? null)
|
|
?? $resultDateTime;
|
|
|
|
// Instrument detail di akhir baris
|
|
preg_match('/(MGIT960|BACTECFX)[^|]*/', $line, $inst);
|
|
|
|
$parsed['result'] = [
|
|
'organism' => $test[2] ?? null,
|
|
'test_status' => $bdResult ?? explode('^', $f[3] ?? '')[0],
|
|
'result_status_datetime' => $resultDateTime,
|
|
];
|
|
|
|
$parsed['instrument'] = [
|
|
'instrument_type' => str_contains($line, 'MGIT960') ? 'MGIT960' : 'BACTECFX',
|
|
'instrument_number' => $inst[0] ?? null,
|
|
];
|
|
} else {
|
|
$cekline = explode('|F|', $line);
|
|
if (isset($cekline[1])){
|
|
$f = explode('|', $cekline[1]);
|
|
$resultDateTime2 = $this->astmToDateTime($f[3] ?? null);
|
|
if ($resultDateTime2){
|
|
$parsed['result'] = array_merge($parsed['result'], [
|
|
'result_status_datetime' => $resultDateTime2,
|
|
]);
|
|
$parsed['instrument'] = array_merge($parsed['instrument'], [
|
|
'instrument_type' => $f[4] ?? null,
|
|
]);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// =========================
|
|
// SAVE TO DATABASE
|
|
// =========================
|
|
$resultSample = new ResultSample();
|
|
$resultSample->fill(array_merge(
|
|
$parsed['header'],
|
|
$parsed['patient'],
|
|
$parsed['order'],
|
|
$parsed['result'],
|
|
$parsed['instrument']
|
|
));
|
|
|
|
$resultSample->additional_result = [
|
|
'raw_astm' => $raw,
|
|
'source' => 'BD'
|
|
];
|
|
|
|
$resultSample->save();
|
|
if ($accnumber != '' AND isset($resultDateTime)){
|
|
KomponenJawaban::updateOrCreate(
|
|
[
|
|
'accnumber' => $accnumber,
|
|
'komponen' => 'bd_result_date',
|
|
'isidata' => $resultDateTime,
|
|
],
|
|
[
|
|
'template' => 'Kultur',
|
|
'created_by' => 'BD'
|
|
]
|
|
);
|
|
}
|
|
if ($accnumber != '' && $bdResult){
|
|
KomponenJawaban::updateOrCreate(
|
|
[
|
|
'accnumber' => $accnumber,
|
|
'komponen' => 'bd_result',
|
|
'isidata' => $bdResult,
|
|
],
|
|
[
|
|
'template' => 'Kultur',
|
|
'created_by' => 'BD'
|
|
]
|
|
);
|
|
}
|
|
//Log::info("Data BD ASTM Berhasil di Parse dan di simpan ", $resultSample->toArray());
|
|
|
|
|
|
return true;
|
|
|
|
} catch (\Throwable $e) {
|
|
Log::error('BD ASTM parse error', [
|
|
'msg' => $e->getMessage()
|
|
]);
|
|
return false;
|
|
}
|
|
}
|
|
protected function splitHl7LikeMessages(string $raw): array
|
|
{
|
|
$normalized = str_replace(["\r\n", "\r"], "\n", $raw);
|
|
$normalized = preg_replace('/[\x00-\x08\x0B-\x1F\x7F]/', '', $normalized);
|
|
$normalized = preg_replace('/(?=MSH\|)/', "\n", $normalized);
|
|
$parts = preg_split('/\n\s*\n+/', trim($normalized)) ?: [];
|
|
|
|
$messages = [];
|
|
foreach ($parts as $part) {
|
|
$part = trim($part);
|
|
if ($part === '' || !str_contains($part, 'MSH|')) {
|
|
continue;
|
|
}
|
|
|
|
$start = strpos($part, 'MSH|');
|
|
if ($start !== false) {
|
|
$part = substr($part, $start);
|
|
}
|
|
|
|
$messages[] = trim($part);
|
|
}
|
|
|
|
return $messages;
|
|
}
|
|
protected function formatLocalInstrumentText(array $parsed, string $alat, ?string $accessionNumber, ?string $messageDateTime): string
|
|
{
|
|
$msh = $parsed['MSH'][0] ?? [];
|
|
$pid = $parsed['PID'][0] ?? [];
|
|
$obr = $parsed['OBR'][0] ?? [];
|
|
$obx = $parsed['OBX'][0] ?? [];
|
|
$nte = $parsed['NTE'][0] ?? [];
|
|
$qpd = $parsed['QPD'][0] ?? [];
|
|
$qid = $parsed['QID'][0] ?? [];
|
|
$spm = $parsed['SPM'][0] ?? [];
|
|
|
|
$messageType = $msh[8] ?? '-';
|
|
$patientName = $pid[3] ?? $pid[5] ?? '-';
|
|
$patientName = trim((string) $patientName, "\" \t\n\r\0\x0B");
|
|
$testId = $obr[4] ?? ($qpd[1] ?? '-');
|
|
$receivedAt = $messageDateTime ?? '-';
|
|
$escape = static fn ($value) => e($value ?: '-');
|
|
|
|
if (str_starts_with($alat, 'MYLA')) {
|
|
$organism = $obx[5] ?? '-';
|
|
$abnormalFlag = $obx[8] ?? '-';
|
|
$confidence = $obx[9] ?? '-';
|
|
$status = $obx[11] ?? '-';
|
|
$operator = $obx[16] ?? '-';
|
|
$resultAt = $this->astmToDateTime($obx[14] ?? null) ?? '-';
|
|
$comment = $nte[3] ?? '-';
|
|
$specimen = $spm[3] ?? '-';
|
|
|
|
return
|
|
'<div class="instrument-result instrument-result-myla">'.
|
|
'<div><strong>HASIL ALAT MYLA</strong></div>'.
|
|
'<table>'.
|
|
'<tr><td>No. Foto</td><td>: '.$escape($accessionNumber).'</td></tr>'.
|
|
'<tr><td>No. Spesimen</td><td>: '.$escape($specimen).'</td></tr>'.
|
|
'<tr><td>Pasien</td><td>: '.$escape($patientName).'</td></tr>'.
|
|
'<tr><td>Jenis Pesan</td><td>: '.$escape($messageType).'</td></tr>'.
|
|
'<tr><td>Test</td><td>: '.$escape($testId).'</td></tr>'.
|
|
'<tr><td>Hasil</td><td>: '.$escape($organism).'</td></tr>'.
|
|
'<tr><td>Abnormal Flag</td><td>: '.$escape($abnormalFlag).'</td></tr>'.
|
|
'<tr><td>Confidence</td><td>: '.$escape($confidence).'</td></tr>'.
|
|
'<tr><td>Status</td><td>: '.$escape($status).'</td></tr>'.
|
|
'<tr><td>Operator</td><td>: '.$escape($operator).'</td></tr>'.
|
|
'<tr><td>Komentar</td><td>: '.$escape($comment).'</td></tr>'.
|
|
'<tr><td>Tanggal Hasil</td><td>: '.$escape($resultAt).'</td></tr>'.
|
|
'<tr><td>Tanggal Diterima LIS</td><td>: '.$escape($receivedAt).'</td></tr>'.
|
|
'</table>'.
|
|
'</div>';
|
|
}
|
|
|
|
$orderAt = $this->astmToDateTime($parsed['ORC'][0][8] ?? null) ?? '-';
|
|
$queryId = $qid[1] ?? ($msh[9] ?? '-');
|
|
|
|
return
|
|
'<div class="instrument-result instrument-result-genexpert">'.
|
|
'<div><strong>HASIL ALAT GENEXPERT</strong></div>'.
|
|
'<table>'.
|
|
'<tr><td>No. Foto</td><td>: '.$escape($accessionNumber).'</td></tr>'.
|
|
'<tr><td>Pasien</td><td>: '.$escape($patientName).'</td></tr>'.
|
|
'<tr><td>Jenis Pesan</td><td>: '.$escape($messageType).'</td></tr>'.
|
|
'<tr><td>Test/Assay</td><td>: '.$escape($testId).'</td></tr>'.
|
|
'<tr><td>Order/Run ID</td><td>: '.$escape($queryId).'</td></tr>'.
|
|
'<tr><td>Tanggal Order</td><td>: '.$escape($orderAt).'</td></tr>'.
|
|
'<tr><td>Tanggal Diterima LIS</td><td>: '.$escape($receivedAt).'</td></tr>'.
|
|
'<tr><td>Catatan</td><td>: Payload alat disimpan sebagai raw untuk ditinjau analis.</td></tr>'.
|
|
'</table>'.
|
|
'</div>';
|
|
}
|
|
protected function saveInstrumentRawResult(string $raw, string $alat): bool
|
|
{
|
|
try {
|
|
$segments = preg_split("/\r\n|\n|\r/", trim($raw)) ?: [];
|
|
$parsed = [];
|
|
|
|
foreach ($segments as $segment) {
|
|
$segment = trim($segment);
|
|
if ($segment === '' || !str_contains($segment, '|')) {
|
|
continue;
|
|
}
|
|
|
|
$fields = explode('|', $segment);
|
|
$segmentType = strtoupper(trim($fields[0] ?? ''));
|
|
if ($segmentType !== '') {
|
|
$parsed[$segmentType][] = $fields;
|
|
}
|
|
}
|
|
|
|
$msh = $parsed['MSH'][0] ?? [];
|
|
$pid = $parsed['PID'][0] ?? [];
|
|
$obr = $parsed['OBR'][0] ?? [];
|
|
$obx = $parsed['OBX'][0] ?? [];
|
|
$spm = $parsed['SPM'][0] ?? [];
|
|
$nte = $parsed['NTE'][0] ?? [];
|
|
$qpd = $parsed['QPD'][0] ?? [];
|
|
$qid = $parsed['QID'][0] ?? [];
|
|
|
|
$messageDateTime = $this->astmToDateTime($msh[6] ?? null);
|
|
$accessionNumber = $spm[3] ?? $pid[2] ?? $pid[3] ?? $qpd[3] ?? $qid[1] ?? null;
|
|
if ($accessionNumber) {
|
|
$accessionNumber = trim((string) $accessionNumber, "\"@ \t\n\r\0\x0B");
|
|
}
|
|
|
|
$patientName = $pid[3] ?? $pid[5] ?? null;
|
|
$patientName = $patientName ? trim((string) $patientName, "\" \t\n\r\0\x0B") : null;
|
|
$organism = $obx[5] ?? null;
|
|
$resultSample = new ResultSample();
|
|
$resultSample->sender_name = $alat;
|
|
$resultSample->version_number = $msh[11] ?? null;
|
|
$resultSample->message_datetime = $messageDateTime;
|
|
$resultSample->patient_id = $accessionNumber;
|
|
$resultSample->patient_name_last = $patientName;
|
|
$resultSample->accession_number = $accessionNumber;
|
|
$resultSample->test_id = $obr[4] ?? ($qpd[1] ?? null);
|
|
$resultSample->result_type_code = $obx[3] ?? ($msh[8] ?? null);
|
|
$resultSample->organism = $organism;
|
|
$resultSample->test_status = $obx[11] ?? null;
|
|
$resultSample->result_status_datetime = $this->astmToDateTime($obx[14] ?? null);
|
|
$resultSample->instrument_type = $alat;
|
|
$resultSample->protocol_name = $obr[4] ?? null;
|
|
$resultSample->result_data = $obx ? json_encode($obx) : null;
|
|
$resultSample->additional_result = [
|
|
'source' => 'local_instrument_fallback',
|
|
'raw_astm' => $raw,
|
|
'segments' => array_keys($parsed),
|
|
'nte' => $nte,
|
|
'qpd' => $qpd,
|
|
'qid' => $qid,
|
|
];
|
|
$resultSample->save();
|
|
|
|
if ($accessionNumber) {
|
|
$teksHasil = $this->formatLocalInstrumentText($parsed, $alat, $accessionNumber, $messageDateTime);
|
|
if ($organism !== '') {
|
|
$getnama = explode('^', $organism);
|
|
$organism = $getnama[1] ?? $organism;
|
|
$sudah = Organisms::where('name', $organism)->count();
|
|
if ($sudah == 0) {
|
|
Organisms::create([
|
|
'kelompok' => 'biakankultur',
|
|
'name' => $organism,
|
|
'category' => $alat,
|
|
]);
|
|
}
|
|
KomponenJawaban::updateOrCreate(
|
|
[
|
|
'accnumber' => $accessionNumber,
|
|
'komponen' => 'id_bakteri01',
|
|
'isidata' => $organism,
|
|
],
|
|
[
|
|
'template' => 'all',
|
|
'created_by' => $alat
|
|
]
|
|
);
|
|
if ($alat == 'MYLA-10.10.120.89') {
|
|
$status = 'Data Malditof di Terima';
|
|
} else {
|
|
$status = 'Data Genexpert di Terima';
|
|
}
|
|
Periksa::where('nofoto', $accessionNumber)->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])->update([
|
|
'status' => $status,
|
|
]);
|
|
}
|
|
|
|
Riwayat::create([
|
|
'nofoto' => $accessionNumber,
|
|
'jawaban' => $teksHasil,
|
|
'inputor' => $alat,
|
|
'keterangan' => $messageDateTime ?: date('Y-m-d H:i:s'),
|
|
'verifikasi' => '',
|
|
]);
|
|
}
|
|
|
|
return true;
|
|
} catch (\Throwable $e) {
|
|
Log::error('Local instrument fallback parse error', [
|
|
'alat' => $alat,
|
|
'msg' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function processAstmMessagesgenExpert($response, $alat)
|
|
{
|
|
$normalizedData = str_replace(["\r\n", "\r"], "\n", $response);
|
|
$segments = explode("\n", $normalizedData);
|
|
$patient_id = '';
|
|
$patient_name = '';
|
|
$accession_number = '';
|
|
|
|
// Array untuk menyimpan data terstruktur
|
|
$parsedResults = [];
|
|
$hasil_list = []; // Tetap dipertahankan untuk kebutuhan legacy/summary
|
|
|
|
foreach ($segments as $rsegmen) {
|
|
$cekdata = explode('|', $rsegmen);
|
|
if (empty($cekdata[0])) continue;
|
|
|
|
$recordType = preg_replace("/[^a-zA-Z]/", "", $cekdata[0]);
|
|
|
|
if ($recordType === 'P') {
|
|
$patient_id = $cekdata[3] ?? ($cekdata[4] ?? '');
|
|
if (isset($cekdata[5])) {
|
|
$patient_name = trim(str_replace('^', ' ', $cekdata[5]));
|
|
}
|
|
}
|
|
|
|
if ($recordType === 'O') {
|
|
$accession_number = $cekdata[2] ?? '';
|
|
}
|
|
|
|
if ($recordType === 'R') {
|
|
if (isset($cekdata[2]) && isset($cekdata[3])) {
|
|
$test_info_raw = $cekdata[2];
|
|
$result_val = trim(str_replace('^', '', $cekdata[3]));
|
|
|
|
if ($result_val !== '') {
|
|
// Pecah test info berdasarkan '^'
|
|
$test_info = explode('^', $test_info_raw);
|
|
|
|
// Pada format GeneXpert: index ke-6 biasanya nama target (FII, FII 20210G, dll)
|
|
// index ke-7 biasanya penanda tipe data (Ct, EndPt)
|
|
$target_name = $test_info[6] ?? 'Unknown Target';
|
|
$metric_type = $test_info[7] ?? 'Result'; // Jika tidak ada, berarti ini Result (POS/NEG)
|
|
|
|
// Inisialisasi array untuk target ini jika belum ada
|
|
if (!isset($parsedResults[$target_name])) {
|
|
$parsedResults[$target_name] = [
|
|
'Result' => '-',
|
|
'Ct' => '-',
|
|
'EndPt' => '-'
|
|
];
|
|
}
|
|
|
|
// Masukkan nilai ke dalam struktur data
|
|
$parsedResults[$target_name][$metric_type] = $result_val;
|
|
|
|
// Untuk summary legacy (kolom organism/additional_result)
|
|
if ($metric_type === 'Result') {
|
|
$hasil_list[] = $target_name . ": " . $result_val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (empty($accession_number)) {
|
|
Log::warning("GeneXpert ASTM: Accession number (NoReg) tidak ditemukan.", ['alat' => $alat]);
|
|
return false;
|
|
}
|
|
|
|
// ====================================================================
|
|
// MEMBUAT HTML REPORT & GRAFIK (CHART.JS)
|
|
// ====================================================================
|
|
|
|
// 1. Buat Tabel HTML
|
|
$reporthtml = '<div class="genexpert-report" style="font-family: Arial, sans-serif;">';
|
|
$reporthtml .= '<h4 style="margin-bottom:10px;">GeneXpert Assay Result</h4>';
|
|
$reporthtml .= '<table border="1" cellpadding="5" style="width: 100%; border-collapse: collapse; text-align: left; margin-bottom: 20px;">';
|
|
$reporthtml .= '<tr style="background-color: #f4f4f4;">
|
|
<th>Target/Analyte</th>
|
|
<th>Result</th>
|
|
<th>Ct Value</th>
|
|
<th>EndPt</th>
|
|
</tr>';
|
|
|
|
$chartLabels = [];
|
|
$chartCtData = [];
|
|
$chartEndPtData = [];
|
|
|
|
foreach ($parsedResults as $target => $data) {
|
|
$reporthtml .= "<tr>
|
|
<td><strong>{$target}</strong></td>
|
|
<td>{$data['Result']}</td>
|
|
<td>{$data['Ct']}</td>
|
|
<td>{$data['EndPt']}</td>
|
|
</tr>";
|
|
|
|
// Siapkan data untuk grafik (hanya ambil target yang memiliki nilai numerik Ct/EndPt)
|
|
if (is_numeric($data['Ct']) || is_numeric($data['EndPt'])) {
|
|
$chartLabels[] = $target;
|
|
$chartCtData[] = is_numeric($data['Ct']) ? (float)$data['Ct'] : 0;
|
|
$chartEndPtData[] = is_numeric($data['EndPt']) ? (float)$data['EndPt'] : 0;
|
|
}
|
|
}
|
|
$reporthtml .= '</table>';
|
|
|
|
// 2. Buat Grafik menggunakan Chart.js (Inject Script)
|
|
if (!empty($chartLabels)) {
|
|
$chartId = 'gxChart_' . preg_replace('/[^a-zA-Z0-9]/', '', $accession_number);
|
|
$labelsJson = json_encode($chartLabels);
|
|
$ctJson = json_encode($chartCtData);
|
|
$endPtJson = json_encode($chartEndPtData);
|
|
|
|
// Tambahkan Canvas untuk grafik
|
|
$reporthtml .= '<div style="width: 100%; max-width: 800px; margin: auto;">';
|
|
$reporthtml .= '<canvas id="'.$chartId.'"></canvas>';
|
|
$reporthtml .= '</div>';
|
|
|
|
// Tambahkan script Chart.js (asumsi CDN) dan inisialisasi
|
|
$reporthtml .= "
|
|
<script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>
|
|
<script>
|
|
// Gunakan DOMContentLoaded agar script jalan setelah HTML di-render di frontend
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
var ctx = document.getElementById('{$chartId}').getContext('2d');
|
|
new Chart(ctx, {
|
|
type: 'bar', // Menggunakan kombinasi Bar & Line chart
|
|
data: {
|
|
labels: {$labelsJson},
|
|
datasets: [
|
|
{
|
|
label: 'Ct Value',
|
|
data: {$ctJson},
|
|
backgroundColor: 'rgba(54, 162, 235, 0.6)',
|
|
borderColor: 'rgba(54, 162, 235, 1)',
|
|
borderWidth: 1,
|
|
yAxisID: 'y'
|
|
},
|
|
{
|
|
label: 'End Point',
|
|
data: {$endPtJson},
|
|
type: 'line',
|
|
borderColor: 'rgba(255, 99, 132, 1)',
|
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
|
borderWidth: 2,
|
|
yAxisID: 'y1'
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
scales: {
|
|
y: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'left',
|
|
title: { display: true, text: 'Ct Value' }
|
|
},
|
|
y1: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'right',
|
|
title: { display: true, text: 'EndPt' },
|
|
grid: { drawOnChartArea: false } // Agar garis grid tidak bertumpuk
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
";
|
|
}
|
|
$reporthtml .= '</div>';
|
|
|
|
// ====================================================================
|
|
// SIMPAN KE DATABASE
|
|
// ====================================================================
|
|
|
|
$kesimpulan = implode(" | ", $hasil_list);
|
|
$kesimpulan_safe = substr($kesimpulan, 0, 100);
|
|
|
|
try {
|
|
Periksa::where('nofoto', $accession_number)
|
|
->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])
|
|
->update([
|
|
'status' => 'Data GeneXpert di Terima'
|
|
]);
|
|
|
|
|
|
Riwayat::create([
|
|
'nofoto' => $accession_number,
|
|
'jawaban' => $reporthtml,
|
|
'inputor' => $alat,
|
|
'keterangan' => date('Y-m-d H:i:s'),
|
|
'verifikasi' => '',
|
|
]);
|
|
|
|
$resultSample = ResultSample::firstOrNew(['accession_number' => $accession_number]);
|
|
$resultSample->sender_name = $alat;
|
|
$resultSample->patient_id = substr($patient_id, 0, 50);
|
|
$resultSample->patient_name_first = substr($patient_name, 0, 100);
|
|
$resultSample->organism = $kesimpulan_safe; // Tetap menyimpan text summary
|
|
$resultSample->additional_result = json_encode($parsedResults); // Menyimpan JSON utuh agar lebih informatif
|
|
$resultSample->message_datetime = date('Y-m-d H:i:s');
|
|
$resultSample->save();
|
|
Log::info("GeneXpert Result Disimpan", ["NoReg" => $accession_number, "Hasil" => $kesimpulan_safe]);
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error("GeneXpert DB Save Error: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
public function processAstmResponse($response, $alat) {
|
|
// Bersihkan data
|
|
$astmData = $this->cleanString($response);
|
|
// Validasi awal: response tidak kosong
|
|
if (empty($astmData)) {
|
|
Log::error("ASTM data kosong atau tidak valid.");
|
|
}
|
|
$jsonantibiotik = array(
|
|
'Oxacillin-OX',
|
|
'Cefoxitin-FOX',
|
|
'Benzylpenicillin-P',
|
|
'Ampicillin-AM',
|
|
'Azithromycin-AZM',
|
|
'Erythromycin-ERY',
|
|
'Cefazolin-CZO',
|
|
'Cefepime-FEP',
|
|
'Cefixime-CFM',
|
|
'Cefotaxime-CTX',
|
|
'Cefuroxime-CXM',
|
|
'Ceftazidime-CAZ',
|
|
'Ceftriaxone-CRO',
|
|
'Ceftazidime/Avibactam-CZA',
|
|
'Piperacilin/Tazobactam-TZP',
|
|
'Ampicillin/Sulbactam-SAM',
|
|
'Amoxicillin/Clavulanate-AMC',
|
|
'Cefoperazon/Sulbactam-SCF',
|
|
'Aztreonam-ATM',
|
|
'Ceftaroline-CPT',
|
|
'Ciprofloxacin-CIP',
|
|
'Levofloxacin-LEV',
|
|
'Moxifloxacin-MFX',
|
|
'Clindamycin-CLI',
|
|
'Colistin-CS',
|
|
'Tetracyclin-TCY',
|
|
'Tigecycline-TGC',
|
|
'Gentamicin-GM',
|
|
'Amikacin-AN',
|
|
'Meropenem-MEM',
|
|
'Imipenem-IPM',
|
|
'Doripenem-DOR',
|
|
'Ertapenem-ETP',
|
|
'Minocycline-MNO',
|
|
'Doxycycline-DOX',
|
|
'Spectinomycin-SPT',
|
|
'Tigecycline-TGC',
|
|
'Trimethoprim/Sulfamethoxazole-SXT',
|
|
'Fosfomycin-FOS',
|
|
'Vancomycin-VAN',
|
|
'Linezolid-LNZ',
|
|
'Fluconazole',
|
|
'Voriconazole',
|
|
'Caspofungin',
|
|
'Micafungin',
|
|
'Amphotericin B',
|
|
'Flucytosine'
|
|
);
|
|
$headerData = [];
|
|
$patientData = [];
|
|
$orderData = [];
|
|
$resultData = [];
|
|
$instrumenDT = [];
|
|
$mltrData = [];
|
|
$isolate = null;
|
|
$accession_number = null;
|
|
$segments = explode("\n", $astmData);
|
|
foreach ($segments as $rsegmen){
|
|
$cekdata = explode('|', $rsegmen);
|
|
$datapertama = $cekdata[0];
|
|
$cleankode = preg_replace("/[^a-zA-Z]/", "", $datapertama);
|
|
if ($cleankode == 'H'){
|
|
$headerData = array_merge([$cleankode], array_slice($cekdata, 1));
|
|
}
|
|
if ($cleankode == 'P'){
|
|
$patientData = array_merge([$cleankode], array_slice($cekdata, 1));
|
|
}
|
|
if ($cleankode == 'O'){
|
|
$orderData = array_merge([$cleankode], array_slice($cekdata, 1));
|
|
}
|
|
if ($cleankode == 'R'){
|
|
$resultData = array_merge([$cleankode], array_slice($cekdata, 1));
|
|
}
|
|
if ($cleankode == 'I'){
|
|
$instrumenDT = array_merge([$cleankode], array_slice($cekdata, 1));
|
|
}
|
|
if ($cleankode == 'mtrsl'){
|
|
$mltrData = array_merge([$cleankode], array_slice($cekdata, 1));
|
|
}
|
|
}
|
|
if (!empty($patientData) && !empty($headerData)) {
|
|
$resultSample = new ResultSample();
|
|
$noregister = $patientData[4] ?? null;
|
|
$resultSample->sender_name = $alat;
|
|
$resultSample->version_number = $headerData[11] ?? $patientData[12] ?? null;
|
|
$resultSample->message_datetime = $resultData[13] ?? null; // '20241130152734'
|
|
// Patient Record (Segment P)
|
|
$resultSample->patient_id = $patientData[4] ?? null; // '11607396'
|
|
$resultSample->patient_name_last = $patientData[6] ?? null; // 'ZAKIYATUN'
|
|
$resultSample->patient_name_first = $patientData[7] ?? null; // 'O'
|
|
$resultSample->patient_name_middle = $patientData[8] ?? null;
|
|
$resultSample->patient_name_suffix = $patientData[9] ?? null;
|
|
$resultSample->patient_name_title = $patientData[10] ?? null;
|
|
$resultSample->patient_dob = $resultData[11] ?? null; // '19800101'
|
|
$resultSample->patient_sex = $patientData[12] ?? null; // 'F'
|
|
$resultSample->address_street = $patientData[13] ?? null; // 'Street ABC'
|
|
$resultSample->address_city = $patientData[14] ?? null;
|
|
$resultSample->address_state = $patientData[15] ?? null;
|
|
$resultSample->address_zip = $patientData[16] ?? null;
|
|
$resultSample->address_country = $patientData[17] ?? null;
|
|
$resultSample->patient_phone = $patientData[18] ?? null; // '081234567890'
|
|
|
|
// Order Record (Segment O)
|
|
$resultSample->accession_number = $orderData[4] ?? null; // '12345'
|
|
$resultSample->isolate_number = $orderData[5] ?? null; // '67890'
|
|
$resultSample->organism = $orderData[6] ?? null; // 'E. coli'
|
|
$resultSample->exclude_isolate_from_statistics = $isolate; // 'false'
|
|
$resultSample->test_id = $orderData[8] ?? null; // 'Test123'
|
|
|
|
// Result Record (Segment R)
|
|
$resultSample->result_type_code = $resultData[4] ?? null; // 'S'
|
|
$resultSample->antibiotic = $resultData[5] ?? null; // 'Amoxicillin'
|
|
$resultSample->antibiotic_concentration = $resultData[6] ?? null; // '100mg'
|
|
$resultSample->antibiotic_concentration_units = $resultData[7] ?? null; // 'mg'
|
|
$resultSample->test_status = $resultData[8] ?? null; // 'Completed'
|
|
$resultSample->result_data = json_encode(array_slice($resultData, 9)); // Additional data (if any)
|
|
|
|
$resultSample->preliminary_final_status = $resultData[13] ?? null;
|
|
$resultSample->test_start_datetime = $resultData[14] ?? null;
|
|
$resultSample->result_status_datetime = $resultData[15] ?? null;
|
|
$resultSample->test_complete_datetime = $resultData[16] ?? null;
|
|
// Periksa apakah data duplikat berdasarkan accession_number
|
|
//if (ResultSample::where('accession_number', $resultSample->accession_number)->exists()) {
|
|
// return response()->json(['message' => 'Data sudah ada.'], 409);
|
|
//}
|
|
|
|
// Simpan ke database
|
|
if ($noregister){
|
|
Periksa::where('nofoto', $accession_number)->update([
|
|
'status' => 'Data Vitek di Terima',
|
|
]);
|
|
//Log::info("Data berhasil disimpan:", $resultSample->toArray());
|
|
$resultSample->save();
|
|
}
|
|
return response()->json(['message' => 'Data berhasil diproses dan disimpan.']);
|
|
|
|
} else {
|
|
if (!empty($mltrData)){
|
|
$mulaikirim = 0;
|
|
$tanggalmasuk = '';
|
|
$tanggalkeluar = '';
|
|
$organism = '';
|
|
$parsedData = [
|
|
'patient_info' => [],
|
|
'antibiotics' => []
|
|
];
|
|
$resultSample = new ResultSample();
|
|
for ($i = 0; $i < count($mltrData); $i++) {
|
|
$field = $mltrData[$i];
|
|
$kodeduadigit = substr($field, 0, 2);
|
|
|
|
if ($field == 'ra') {
|
|
$current_antibiotic = [];
|
|
for ($j = 1; $j <= 5; $j++) {
|
|
if (isset($mltrData[$i + $j])) {
|
|
$subField = $mltrData[$i + $j];
|
|
$code = substr($subField, 0, 2);
|
|
$value = substr($subField, 2);
|
|
|
|
switch ($code) {
|
|
case 'a1':
|
|
$current_antibiotic['instrument_code'] = $value; // Kode dari alat
|
|
break;
|
|
case 'a2':
|
|
$current_antibiotic['antibiotic_name'] = $value; // Nama antibiotik
|
|
break;
|
|
case 'a3':
|
|
$current_antibiotic['value'] = $value; // Nilai MIC
|
|
break;
|
|
case 'a4':
|
|
$current_antibiotic['interpretation'] = $value; // S, I, atau R
|
|
break;
|
|
case 'an':
|
|
$current_antibiotic['resistance_note'] = $value; // Catatan resistansi
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
$antibioticName = $current_antibiotic['antibiotic_name'] ?? null;
|
|
if ($antibioticName && isset($kodevitek[$antibioticName])) {
|
|
$current_antibiotic['lis_code'] = $kodevitek[$antibioticName];
|
|
} else {
|
|
$current_antibiotic['lis_code'] = $current_antibiotic['instrument_code'] ?? 'NOT_FOUND';
|
|
}
|
|
$parsedData['antibiotics'][] = $current_antibiotic;
|
|
$i += 5;
|
|
} else {
|
|
if ($kodeduadigit == 'mt'){
|
|
$resultSample->sender_name = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'ii'){
|
|
$resultSample->version_number = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'rr'){
|
|
$resultSample->isolate_number = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'ci'){
|
|
$accession_number = substr($field, 2);
|
|
$resultSample->accession_number = $accession_number;
|
|
}
|
|
if ($kodeduadigit == 'pi'){
|
|
$resultSample->patient_id = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'pn'){
|
|
$patientNames = substr($field, 2);
|
|
$patientNames = explode(',', $patientNames ?? null);
|
|
if (count($patientNames) > 0) {
|
|
$resultSample->patient_name_first = trim($patientNames[0]);
|
|
}
|
|
if (count($patientNames) > 1) {
|
|
$resultSample->patient_name_last = trim($patientNames[1]);
|
|
}
|
|
if (count($patientNames) > 2) {
|
|
$resultSample->patient_name_middle = trim($patientNames[2]);
|
|
}
|
|
}
|
|
|
|
if ($kodeduadigit == 'pl'){
|
|
$resultSample->hospital_service = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'p2'){
|
|
$resultSample->hospital_client = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'ss'){
|
|
$resultSample->specimen_type = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 'o2'){
|
|
$organism = substr($field, 2);
|
|
$resultSample->organism = $organism;
|
|
}
|
|
if ($kodeduadigit == 's1'){
|
|
$tanggalmasuk = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 's2'){
|
|
$tanggalmasuk = $tanggalmasuk.' '.substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 's3'){
|
|
$tanggalkeluar = substr($field, 2);
|
|
}
|
|
if ($kodeduadigit == 's4'){
|
|
$tanggalkeluar = $tanggalkeluar.' '.substr($field, 2);
|
|
}
|
|
}
|
|
}
|
|
$resultSample->additional_result = json_encode($parsedData['antibiotics']);
|
|
if ($tanggalmasuk != ''){
|
|
try {
|
|
$resultSample->message_datetime = Carbon::parse($tanggalmasuk)->format('Y-m-d H:i:s') ?? null;
|
|
}catch (Exception $e) {
|
|
$resultSample->message_datetime = date('Y-m-d H:i:s');
|
|
}
|
|
}
|
|
if ($tanggalkeluar != ''){
|
|
try {
|
|
$resultSample->test_complete_datetime = Carbon::parse($tanggalkeluar)->format('Y-m-d H:i:s') ?? null;
|
|
}catch (Exception $e) {
|
|
$resultSample->test_complete_datetime = date('Y-m-d H:i:s');
|
|
}
|
|
}
|
|
$resultSample->save();
|
|
if ($accession_number){
|
|
$accession_number = substr($accession_number, 0, 9);
|
|
if ($organism !== ''){
|
|
$sudah = Organisms::where('name', $organism)->count();
|
|
if ($sudah == 0){
|
|
Organisms::create([
|
|
'kelompok' => 'biakankultur',
|
|
'name' => $organism,
|
|
'category' => 'Vitek',
|
|
]);
|
|
}
|
|
KomponenJawaban::updateOrCreate(
|
|
[
|
|
'accnumber' => $accession_number,
|
|
'komponen' => 'bakteri',
|
|
'isidata' => $organism,
|
|
],
|
|
[
|
|
'template' => 'all',
|
|
'created_by' => 'Vitek'
|
|
]
|
|
);
|
|
}
|
|
$cekdataperiksa = Periksa::where('nofoto', $accession_number)->first();
|
|
if (isset($cekdataperiksa->id)){
|
|
if (is_array($parsedData['antibiotics'])) {
|
|
foreach ($parsedData['antibiotics'] as $item) {
|
|
$glassreportname = in_array($item['antibiotic_name'], $jsonantibiotik);
|
|
RekapAntibiotik::updateOrCreate(
|
|
[
|
|
'kuman' => $organism,
|
|
'orderid' => $cekdataperiksa->id,
|
|
'antibiotic' => $item['antibiotic_name'],
|
|
],
|
|
[
|
|
'resistance' => $item['resistance_note'] ?? null,
|
|
'value' => $item['value'] ?? null,
|
|
'interpretation' => $item['interpretation'] ?? null,
|
|
'glassreportname' => $glassreportname,
|
|
'printrow' => true,
|
|
'printcol' => false,
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Periksa::where('nofoto', $accession_number)->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])->update([
|
|
'status' => 'Data Vitek di Terima',
|
|
]);
|
|
}
|
|
//Log::info("Data MTRL Berhasil di Parse dan di simpan ", $resultSample->toArray());
|
|
return response()->json(['message' => 'Data berhasil diproses dan disimpan.']);
|
|
} else {
|
|
$headerData = explode("|", $astmData);
|
|
if (isset($headerData[3])){
|
|
$accnumber = $headerData[47] ?? null;
|
|
$noregister = $headerData[16] ?? null;
|
|
if (empty($accnumber)) {
|
|
Log::warning('ASTM BD message skipped because accession number is missing.', [
|
|
'header' => $headerData,
|
|
]);
|
|
return false;
|
|
}
|
|
|
|
$resultSample = ResultSample::firstOrNew([
|
|
'accession_number' => $accnumber,
|
|
]);
|
|
$nama = $headerData[18] ?? null;
|
|
$urgensi = $headerData[50] ?? null; //A Critical R Normal
|
|
$iddokter = $headerData[33] ?? null;
|
|
$test_start_datetime = $headerData[52] ?? null;
|
|
$result_status_datetime = $headerData[59] ?? null;
|
|
$specimen_type = $headerData[60] ?? null;
|
|
$resulttype = $headerData[34] ?? null;
|
|
$resultstatus = $headerData[63] ?? null;
|
|
$preliminary = $headerData[40] ?? null; //P
|
|
$test_complete_datetime = $headerData[44] ?? null;
|
|
$tesid = $headerData[62] ?? null;
|
|
$alamat = $headerData[23] ?? null;
|
|
$instrumen = $resultData[2] ?? null;
|
|
$body_site = '';
|
|
$tengah = '';
|
|
$depan = '';
|
|
$akhir = '';
|
|
$suffix = '';
|
|
$title = '';
|
|
$concentration_unit = '';
|
|
$kode = '';
|
|
$antibiotic = '';
|
|
$city = '';
|
|
$state = '';
|
|
$zipcode = '';
|
|
$country = '';
|
|
if ($resulttype){
|
|
$getdataresult = explode('^', $resulttype);
|
|
$kode = $getdataresult[3] ?? '';
|
|
$concentration_unit = $getdataresult[4] ?? '';
|
|
}
|
|
if ($specimen_type){
|
|
$getdata = explode('\n', $specimen_type);
|
|
$specimen_type = $getdata[0];
|
|
$body_site = $getdata[1] ?? '';
|
|
}
|
|
if ($tesid){
|
|
$gettgl = explode('^', $tesid);
|
|
$tesid = $gettgl[3] ?? null;
|
|
}
|
|
if ($instrumen){
|
|
$gettgl = explode('^', $instrumen);
|
|
$instrumen = $gettgl[0];
|
|
$media_assay_type = $gettgl[1] ?? null;
|
|
$protocol_length = $gettgl[2] ?? null;
|
|
$instrument_number = $gettgl[3] ?? null;
|
|
$instrument_location = $gettgl[4] ?? null;
|
|
$protocol_name = $gettgl[5] ?? null;
|
|
} else {
|
|
$media_assay_type = null;
|
|
$protocol_length = null;
|
|
$instrument_number = null;
|
|
$instrument_location = null;
|
|
$protocol_name = null;
|
|
}
|
|
if ($isolate == '' OR $isolate == '0' OR is_null($isolate)){
|
|
$isolate = false;
|
|
} else { $isolate = true; }
|
|
if ($test_complete_datetime){
|
|
$test_complete_datetime = substr($test_complete_datetime, 0, 14);
|
|
//$test_complete_datetime = Carbon::parse($test_complete_datetime)->format('Y-m-d H:i:s');
|
|
}
|
|
if ($result_status_datetime){
|
|
$result_status_datetime = substr($result_status_datetime, 0, 14);
|
|
//$result_status_datetime = Carbon::parse($result_status_datetime)->format('Y-m-d H:i:s');
|
|
}
|
|
if ($test_start_datetime){
|
|
$test_start_datetime = substr($test_start_datetime, 0, 14);
|
|
//$test_start_datetime = Carbon::parse($test_start_datetime)->format('Y-m-d H:i:s');
|
|
}
|
|
if ($nama){
|
|
$getnama = explode('\015', $nama);
|
|
$nama = $getnama[0];
|
|
$getnama = explode('\n', $nama);
|
|
$nama = $getnama[0];
|
|
|
|
$akhir = $getnama[1] ?? '';
|
|
$depan = $getnama[2] ?? '';
|
|
$tengah = $getnama[3] ?? '';
|
|
$suffix = $getnama[4] ?? '';
|
|
$title = $getnama[5] ?? '';
|
|
|
|
$nama = $nama.$akhir;
|
|
}
|
|
if ($alamat){
|
|
$getnama = explode('^', $alamat);
|
|
$alamat = $getnama[0];
|
|
$city = $getnama[1] ?? '';
|
|
$state = $getnama[2] ?? '';
|
|
$zipcode = $getnama[3] ?? '';
|
|
$country = $getnama[4] ?? '';
|
|
}
|
|
if ($test_start_datetime == ''){ $test_start_datetime = null; }
|
|
if ($result_status_datetime == ''){ $result_status_datetime = null; }
|
|
if ($test_complete_datetime == ''){ $test_complete_datetime = null; }
|
|
|
|
$resultSample->sender_name = $headerData[4] ?? 'Unkown';
|
|
$resultSample->version_number = $headerData[12] ?? 'V0.0';
|
|
$resultSample->message_datetime = $test_start_datetime;
|
|
// Patient Record (Segment P)
|
|
$resultSample->patient_id = $noregister; // '11607396'
|
|
$resultSample->patient_name_last = $nama; // 'ZAKIYATUN'
|
|
$resultSample->patient_name_first = $depan; // 'O'
|
|
$resultSample->patient_name_middle = $tengah;
|
|
$resultSample->patient_name_suffix = $suffix;
|
|
$resultSample->patient_name_title = $title;
|
|
$resultSample->patient_dob = $headerData[20] ?? null;
|
|
$resultSample->patient_sex = $headerData[21] ?? null;
|
|
$resultSample->address_street = $alamat;
|
|
$resultSample->address_city = $city;
|
|
$resultSample->address_state = $state;
|
|
$resultSample->address_zip = $zipcode;
|
|
$resultSample->address_country = $country;
|
|
$resultSample->patient_phone = $headerData[25] ?? null;
|
|
$resultSample->admitting_physician = $headerData[27] ?? null;
|
|
$resultSample->patient_diagnosis = $headerData[33] ?? null;
|
|
$resultSample->patient_therapy = json_encode($headerData);
|
|
$resultSample->admit_datetime = null;
|
|
$resultSample->room_number = $headerData[46] ?? '00001';
|
|
$resultSample->hospital_service = $headerData[45] ?? 'RSSA Malang';
|
|
$resultSample->hospital_client = $orderData[4] ?? 'Lab Mikro';
|
|
|
|
// Order Record (Segment O)
|
|
$resultSample->accession_number = $accnumber; // '12345'
|
|
$resultSample->isolate_number = $orderData[5] ?? null; // '67890'
|
|
$resultSample->organism = $orderData[6] ?? null; // 'E. coli'
|
|
$resultSample->exclude_isolate_from_statistics = $isolate; // 'false'
|
|
$resultSample->specimen_type = $specimen_type;
|
|
$resultSample->body_site = $body_site;
|
|
$resultSample->test_id = $tesid; // 'Test123'
|
|
$resultSample->urgensi = $urgensi; //A Critical R Normal
|
|
|
|
// Result Record (Segment R)
|
|
$resultSample->result_type_code = $kode;
|
|
$resultSample->antibiotic = $antibiotic;
|
|
$resultSample->antibiotic_concentration = $concentration_unit; // '100mg'
|
|
$resultSample->antibiotic_concentration_units = $concentration_unit; // 'mg'
|
|
$resultSample->test_status = $resultstatus; // 'Completed'
|
|
$resultSample->result_data = json_encode($orderData); // Additional data (if any)
|
|
|
|
$resultSample->preliminary_final_status = $preliminary;
|
|
$resultSample->test_start_datetime = $test_start_datetime;
|
|
$resultSample->result_status_datetime = $result_status_datetime;
|
|
$resultSample->test_complete_datetime = $test_complete_datetime;
|
|
|
|
$resultSample->instrument_type = $instrumen;
|
|
$resultSample->media_assay_type = $media_assay_type;
|
|
$resultSample->protocol_length = $protocol_length;
|
|
$resultSample->instrument_number = $instrument_number;
|
|
$resultSample->instrument_location = $instrument_location;
|
|
$resultSample->protocol_name = $protocol_name;
|
|
$resultSample->additional_result = json_encode($resultData);
|
|
if ($noregister){
|
|
$resultSample->save();
|
|
//Log::info("Data ASTM Berhasil di Parse dan di simpan ", $resultSample->toArray());
|
|
return response()->json(['message' => 'Data berhasil diproses dan disimpan.']);
|
|
}
|
|
} else {
|
|
Log::debug("Abaikan Data Berikut ". json_encode($headerData));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public function processAstmMessagesLokal($dataListener) {
|
|
foreach ($dataListener as $data) {
|
|
try {
|
|
$response = $data->rawdt;
|
|
if ($data->alat == 'MGIT' OR $data->alat == 'COM5' OR $data->alat == 'BACTEC'){
|
|
$assembled = $this->reassembleAstmFrames($data->rawdt);
|
|
$messages = $this->splitBDAstmMessages($assembled);
|
|
foreach ($messages as $msg) {
|
|
$ok = $this->processBDAstmResponse($msg, $data);
|
|
}
|
|
DB::table('lis_phoenix')->where('id', $data->id)->update([
|
|
'processed' => $ok ? 1 : 9
|
|
]);
|
|
Periksa::where('nofoto', $data->no_id)->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])->update([
|
|
'status' => 'Data BD di Terima ('.$data->organisme.')',
|
|
]);
|
|
KomponenJawaban::updateOrCreate(
|
|
[
|
|
'accnumber' => $data->no_id,
|
|
'komponen' => 'bd_result',
|
|
'isidata' => $data->organisme,
|
|
],
|
|
[
|
|
'template' => 'Kultur',
|
|
'created_by' => 'BD'
|
|
]
|
|
);
|
|
} else if ( $data->alat == 'GeneXpert-10.10.120.75' OR $data->alat == 'GeneXpert-10.10.120.74' OR $data->alat == 'GeneXpert-10.10.120.73' OR $data->alat == 'GeneXpert-10.10.120.13' OR $data->alat == '10.10.120.75' OR $data->alat == '10.10.120.74' OR $data->alat == '10.10.120.73' OR $data->alat == '10.10.120.13') {
|
|
$result = $this->processAstmMessagesgenExpert($data->rawdt, $data->alat);
|
|
if ($result) {
|
|
DB::table('lis_phoenix')->where('id', $data->id)->update([
|
|
'processed' => 1
|
|
]);
|
|
} else {
|
|
Log::debug($result);
|
|
}
|
|
} else if ( $data->alat == 'MYLA-10.10.120.89' ){
|
|
$messages = $this->splitHl7LikeMessages($data->rawdt);
|
|
$ok = false;
|
|
|
|
if (empty($messages)) {
|
|
$ok = $this->saveInstrumentRawResult($data->rawdt, $data->alat);
|
|
} else {
|
|
foreach ($messages as $message) {
|
|
$saved = $this->saveInstrumentRawResult($message, $data->alat);
|
|
$ok = $ok || $saved;
|
|
}
|
|
}
|
|
|
|
DB::table('lis_phoenix')->where('id', $data->id)->update([
|
|
'processed' => $ok ? 1 : 9
|
|
]);
|
|
} else {
|
|
$result = $this->processAstmResponse($data->rawdt, $data->alat);
|
|
if ($result) {
|
|
DB::table('lis_phoenix')->where('id', $data->id)->update([
|
|
'processed' => 1
|
|
]);
|
|
} else {
|
|
Log::debug($result);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::critical($e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Proses data ASTM Response
|
|
*/
|
|
public function processAstmMessages($dataListener) {
|
|
foreach ($dataListener as $data) {
|
|
try {
|
|
// Ambil pesan ASTM dari kolom 'message' atau yang relevan
|
|
$response = $data->rawdt;
|
|
if ($data->alat == 'BD Mikro 1'){
|
|
if ($data->no_id != ''){
|
|
$assembled = $this->reassembleAstmFrames($data->rawdt);
|
|
$messages = $this->splitBDAstmMessages($assembled);
|
|
foreach ($messages as $msg) {
|
|
$ok = $this->processBDAstmResponse($msg, $data);
|
|
}
|
|
DataListiner::where('urut', $data->urut)->update([
|
|
'processed' => $ok ? 1 : 9
|
|
]);
|
|
Periksa::where('nofoto', $data->no_id)->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])->update([
|
|
'status' => 'Data BD di Terima ('.$data->organisme.')',
|
|
]);
|
|
KomponenJawaban::updateOrCreate(
|
|
[
|
|
'accnumber' => $data->no_id,
|
|
'komponen' => 'bd_result',
|
|
'isidata' => $data->organisme,
|
|
],
|
|
[
|
|
'template' => 'Kultur',
|
|
'created_by' => 'BD'
|
|
]
|
|
);
|
|
|
|
} else {
|
|
DataListiner::where('urut', $data->urut)->update([
|
|
'processed' => 9
|
|
]);
|
|
}
|
|
} else {
|
|
// Lakukan parsing menggunakan method/fungsi yang sudah dibuat
|
|
$result = $this->processAstmResponse($response, $data->alat);
|
|
// Jika berhasil, tandai data sudah diproses
|
|
if ($result) {
|
|
DataListiner::where('urut', $data->urut)->update([
|
|
'processed' => 1
|
|
]);
|
|
} else {
|
|
Log::debug($result);
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
Log::critical($e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|