diff --git a/htdocs/app/Services/AstmMessageService.php b/htdocs/app/Services/AstmMessageService.php
index 65252650..df1b2beb 100644
--- a/htdocs/app/Services/AstmMessageService.php
+++ b/htdocs/app/Services/AstmMessageService.php
@@ -147,6 +147,569 @@ class AstmMessageService
/**
* 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
+ '
'.
+ '
HASIL ALAT MYLA
'.
+ '
'.
+ '| No. Foto | : '.$escape($accessionNumber).' |
'.
+ '| No. Spesimen | : '.$escape($specimen).' |
'.
+ '| Pasien | : '.$escape($patientName).' |
'.
+ '| Jenis Pesan | : '.$escape($messageType).' |
'.
+ '| Test | : '.$escape($testId).' |
'.
+ '| Hasil | : '.$escape($organism).' |
'.
+ '| Abnormal Flag | : '.$escape($abnormalFlag).' |
'.
+ '| Confidence | : '.$escape($confidence).' |
'.
+ '| Status | : '.$escape($status).' |
'.
+ '| Operator | : '.$escape($operator).' |
'.
+ '| Komentar | : '.$escape($comment).' |
'.
+ '| Tanggal Hasil | : '.$escape($resultAt).' |
'.
+ '| Tanggal Diterima LIS | : '.$escape($receivedAt).' |
'.
+ '
'.
+ '
';
+ }
+
+ $orderAt = $this->astmToDateTime($parsed['ORC'][0][8] ?? null) ?? '-';
+ $queryId = $qid[1] ?? ($msh[9] ?? '-');
+
+ return
+ ''.
+ '
HASIL ALAT GENEXPERT
'.
+ '
'.
+ '| No. Foto | : '.$escape($accessionNumber).' |
'.
+ '| Pasien | : '.$escape($patientName).' |
'.
+ '| Jenis Pesan | : '.$escape($messageType).' |
'.
+ '| Test/Assay | : '.$escape($testId).' |
'.
+ '| Order/Run ID | : '.$escape($queryId).' |
'.
+ '| Tanggal Order | : '.$escape($orderAt).' |
'.
+ '| Tanggal Diterima LIS | : '.$escape($receivedAt).' |
'.
+ '| Catatan | : Payload alat disimpan sebagai raw untuk ditinjau analis. |
'.
+ '
'.
+ '
';
+ }
+ 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)
+ {
+ // 1. Normalisasi karakter pembatas baris (\r\n, \r, atau \n diubah jadi \n)
+ $normalizedData = str_replace(["\r\n", "\r"], "\n", $response);
+ $segments = explode("\n", $normalizedData);
+
+ $patient_id = '';
+ $patient_name = '';
+ $accession_number = ''; // NoReg
+ $hasil_list = [];
+
+ // 2. Looping setiap baris data ASTM
+ foreach ($segments as $rsegmen) {
+ $cekdata = explode('|', $rsegmen);
+ if (empty($cekdata[0])) continue;
+
+ $recordType = preg_replace("/[^a-zA-Z]/", "", $cekdata[0]);
+
+ // Ekstrak Data Pasien (P)
+ if ($recordType === 'P') {
+ $patient_id = $cekdata[3] ?? ($cekdata[4] ?? '');
+ if (isset($cekdata[5])) {
+ $patient_name = trim(str_replace('^', ' ', $cekdata[5]));
+ }
+ }
+
+ // Ekstrak Nomor Order (O)
+ if ($recordType === 'O') {
+ $accession_number = $cekdata[2] ?? '';
+ }
+
+ // Ekstrak Hasil Analisa (R)
+ if ($recordType === 'R') {
+ if (isset($cekdata[2]) && isset($cekdata[3])) {
+ $test_info = $cekdata[2]; // Format contoh: ^^^MTB-RIF_ULTRA 2^^^MTB^
+ $result_val = trim(str_replace('^', '', $cekdata[3])); // Contoh: DETECTED / PASS / INVALID
+
+ // [KUNCI]: Buang baris yang berisi kurva angka (Ct / EndPt) agar tidak nyampah dan kepanjangan
+ if (strpos($rsegmen, 'Ct|') === false && strpos($rsegmen, 'EndPt|') === false && $result_val !== '') {
+
+ // Ambil nama target spesifik (misal 'MTB' atau 'RIF Resistance' atau 'SPC')
+ $target_match = explode('^^^', $test_info);
+ $target_name = '';
+ if (count($target_match) > 1) {
+ $target_name = trim(str_replace('^', '', end($target_match)));
+ }
+
+ // Simpan ke array jika valid
+ if ($target_name && $result_val) {
+ $hasil_list[] = $target_name . ": " . $result_val;
+ }
+ }
+ }
+ }
+ }
+
+ if (empty($accession_number)) {
+ Log::warning("GeneXpert ASTM: Accession number (NoReg) tidak ditemukan.", ['alat' => $alat]);
+ return false;
+ }
+
+ // 3. Gabungkan hasil dari array menjadi 1 string ("MTB: DETECTED | RIF Resistance: NOT DETECTED")
+ $kesimpulan = implode(" | ", $hasil_list);
+
+ // [PENTING] Potong text agar TIDAK ERROR di Postgres (Varchar 100)
+ $kesimpulan_safe = substr($kesimpulan, 0, 100);
+
+ try {
+ // Update status tabel Periksa
+ Periksa::where('nofoto', $accession_number)
+ ->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])
+ ->update([
+ 'status' => 'Data GeneXpert di Terima'
+ ]);
+
+ // Update atau Insert ke Komponen Jawaban
+ if (!empty($kesimpulan)) {
+ KomponenJawaban::updateOrCreate(
+ [
+ 'accnumber' => $accession_number,
+ 'komponen' => 'genexpert_result', // Ganti key ini sesuai dengan penamaan di frontend Anda
+ ],
+ [
+ 'isidata' => substr($kesimpulan, 0, 500), // Di tabel ini biasanya lebih panjang
+ 'template' => 'GeneXpert',
+ 'created_by' => 'GeneXpert Middleware'
+ ]
+ );
+ }
+
+ // Opsional: Simpan log sejarah hasil di ResultSample seperti alat Vitek/BD
+ $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; // Limit varchar(100) ditaati di sini
+ $resultSample->additional_result = json_encode($hasil_list);
+ $resultSample->message_datetime = date('Y-m-d H:i:s');
+ $resultSample->save();
+
+ Log::info("GeneXpert Result Disimpan", ["NoReg" => $accession_number, "Hasil" => $kesimpulan_safe]);
+
+ // Kembalikan TRUE agar update 'processed' => 1 di method processAstmMessagesLokal Anda tereksekusi
+ 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);
@@ -648,459 +1211,6 @@ class AstmMessageService
}
}
}
- 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
- ''.
- '
HASIL ALAT MYLA
'.
- '
'.
- '| No. Foto | : '.$escape($accessionNumber).' |
'.
- '| No. Spesimen | : '.$escape($specimen).' |
'.
- '| Pasien | : '.$escape($patientName).' |
'.
- '| Jenis Pesan | : '.$escape($messageType).' |
'.
- '| Test | : '.$escape($testId).' |
'.
- '| Hasil | : '.$escape($organism).' |
'.
- '| Abnormal Flag | : '.$escape($abnormalFlag).' |
'.
- '| Confidence | : '.$escape($confidence).' |
'.
- '| Status | : '.$escape($status).' |
'.
- '| Operator | : '.$escape($operator).' |
'.
- '| Komentar | : '.$escape($comment).' |
'.
- '| Tanggal Hasil | : '.$escape($resultAt).' |
'.
- '| Tanggal Diterima LIS | : '.$escape($receivedAt).' |
'.
- '
'.
- '
';
- }
-
- $orderAt = $this->astmToDateTime($parsed['ORC'][0][8] ?? null) ?? '-';
- $queryId = $qid[1] ?? ($msh[9] ?? '-');
-
- return
- ''.
- '
HASIL ALAT GENEXPERT
'.
- '
'.
- '| No. Foto | : '.$escape($accessionNumber).' |
'.
- '| Pasien | : '.$escape($patientName).' |
'.
- '| Jenis Pesan | : '.$escape($messageType).' |
'.
- '| Test/Assay | : '.$escape($testId).' |
'.
- '| Order/Run ID | : '.$escape($queryId).' |
'.
- '| Tanggal Order | : '.$escape($orderAt).' |
'.
- '| Tanggal Diterima LIS | : '.$escape($receivedAt).' |
'.
- '| Catatan | : Payload alat disimpan sebagai raw untuk ditinjau analis. |
'.
- '
'.
- '
';
- }
- 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 processAstmMessagesLokal($dataListener) {
foreach ($dataListener as $data) {
try {
@@ -1128,15 +1238,16 @@ class AstmMessageService
'created_by' => 'BD'
]
);
- } else if (
- $data->alat == 'MYLA-10.10.120.89'
- OR $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 == '10.10.120.75'
- OR $data->alat == '10.10.120.74'
- OR $data->alat == '10.10.120.73'
- ){
+ } 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;