This commit is contained in:
Dwi Swandhana
2026-02-04 08:31:33 +07:00
parent 9ab8aa2f52
commit cd14cdfdb4
+119 -61
View File
@@ -227,6 +227,13 @@ def parse_hl7_result(hl7_message, device_name="GeneXpert"):
try:
# 1. Bersihkan dan Split Pesan
# HL7 dipisahkan oleh \r (Carriage Return)
if "MSH|" not in hl7_message:
logging.warning("[HL7] Data tidak mengandung segmen MSH valid.")
print("[HL7] Data tidak mengandung segmen MSH valid.")
return
start_idx = hl7_message.find("MSH|")
hl7_message = hl7_message[start_idx:]
segments = hl7_message.strip().split('\r')
# Variabel penampung
@@ -294,23 +301,28 @@ def parse_hl7_result(hl7_message, device_name="GeneXpert"):
else:
final_result = "; ".join(results_list)
# 4. Validasi Data Penting
# 4. Validasi Data Penting (DIMODIFIKASI)
if not sample_id:
logging.warning("[HL7 Parser] Sample ID tidak ditemukan. Data tidak disimpan.")
print("[HL7 Parser] Sample ID tidak ditemukan. Data tidak disimpan.")
return
# JANGAN RETURN, tapi buat ID palsu agar tetap bisa masuk database
timestamp_err = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
sample_id = f"ERR_{timestamp_err}"
logging.warning(f"[HL7 Parser] Sample ID kosong. Disimpan sebagai {sample_id} untuk audit.")
print(f"[HL7 Parser] Sample ID kosong. Disimpan sebagai {sample_id} untuk audit.")
# Tambahkan penanda di hasil agar Anda tahu ini error
final_result = f"[PARSE FAIL] {final_result}"
logging.info(f"[HL7 Parser] Menyimpan hasil untuk Sample: {sample_id} - {patient_name}")
print(f"[HL7 Parser] Menyimpan hasil untuk Sample: {sample_id} - {patient_name}")
# 5. Simpan ke Database (Tabel LisPhoenix)
logging.info(f"[HL7 Parser] Menyimpan hasil untuk Sample: {sample_id}")
print(f"[HL7 Parser] Menyimpan hasil untuk Sample: {sample_id}")
# 5. Simpan ke Database
new_data = LisPhoenix(
no_id=sample_id, # OBR-3
seq_no=patient_id, # PID-3
rnmpas=patient_name, # PID-5
tgl_data=result_date, # MSH-7
rawdt=hl7_message, # Pesan Asli
organisme=final_result, # Gabungan OBX-5
alat=device_name # Parameter fungsi
no_id=sample_id,
seq_no=patient_id,
rnmpas=patient_name,
tgl_data=result_date,
rawdt=hl7_message,
organisme=final_result,
alat=device_name
)
session.add(new_data)
@@ -407,61 +419,107 @@ def send_mllp_message(sock, hl7_msg):
sock.sendall(mllp_msg)
def handle_genexpert_client(conn, addr):
ip_sender = addr[0]
logging.info(f"[CONNECT] GeneXpert terkoneksi dari IP: {ip_sender}")
print(f"[CONNECT] GeneXpert terkoneksi dari IP: {ip_sender}")
with connection_lock:
active_genexpert_connections[ip_sender] = conn
print(f"[TCP] Koneksi baru dari {addr}")
buffer = b""
try:
while True:
data = conn.recv(4096)
if not data:
break
buffer += data
# Cek apakah paket MLLP lengkap (diawali \x0b dan diakhiri \x1c\r)
# Logic sederhana: cari penutup \x1c\r
while b'\x1c\r' in buffer:
# Ekstrak pesan
start_marker = buffer.find(b'\x0b')
end_marker = buffer.find(b'\x1c\r')
if start_marker != -1 and end_marker != -1:
raw_msg = buffer[start_marker+1 : end_marker]
hl7_str = raw_msg.decode('utf-8')
logging.info(f"[RECV] Dari {ip_sender}: {hl7_str[:50]}...") # Log 50 karakter awal
print(f"[RECV] Dari {ip_sender}: {hl7_str[:50]}...") # Print 50 karakter awal
# Cek Tipe Pesan (MSH field 9)
if "ORU^R01" in hl7_str:
# Ini adalah Hasil
hasil = parse_hl7_result(hl7_str, device_name=addr[0] if addr else "GeneXpert")
logging.info(f"[RESULT] {hasil}")
print(f"[RESULT] {hasil}")
# TODO: Simpan 'hasil' ke database berdasarkan Sample ID di HL7
# Kirim ACK (Terima Kasih) ke Alat
# ACK dinamis
msg_control_id = hl7_str.split('|')[9] # Ambil ID pesan asli
ack_msg = f"MSH|^~\\&|LIS|LAB|GeneXpert|Cepheid|{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}||ACK|{msg_control_id}|P|2.3\rMSA|AA|{msg_control_id}\r"
send_mllp_message(conn, ack_msg)
# Hapus pesan yang sudah diproses dari buffer
buffer = buffer[end_marker+2:]
else:
try:
data = conn.recv(4096)
if not data:
break
buffer += data
# --- 1. HANDLE HANDSHAKE (ENQ) ---
# Jika alat kirim ENQ (\x05/♣), langsung balas ACK (\x06)
if b'\x05' in buffer:
# logging.info(f"[TCP] Terima ENQ dari {addr}, kirim ACK.")
conn.sendall(b'\x06')
# Hapus ENQ dari buffer agar tidak mengganggu
buffer = buffer.replace(b'\x05', b'')
# --- 2. CEK APAKAH PESAN SUDAH LENGKAP? ---
# Kita cari tanda akhir pesan umum:
# - \x1c (End Block MLLP)
# - \x03 (ETX - End Text ASTM)
# - \x04 (EOT - End Transmission ASTM)
msg_complete = False
end_marker_pos = -1
if b'\x1c' in buffer: # Pola MLLP Standard
end_marker_pos = buffer.find(b'\x1c')
msg_complete = True
elif b'\x03' in buffer: # Pola ASTM (Ada Checksum setelahnya)
# ASTM: <STX>...<ETX>CS<CR><LF>
# Kita cari \x03, lalu tambah 4 byte (CS + CRLF) untuk aman
pos = buffer.find(b'\x03')
if len(buffer) >= pos + 4:
end_marker_pos = pos + 4
msg_complete = True
elif b'\x04' in buffer: # Pola EOT (Putus Koneksi/Selesai)
end_marker_pos = buffer.find(b'\x04')
msg_complete = True
# --- 3. PROSES JIKA LENGKAP ---
if msg_complete:
# Ambil pesan dari awal sampai marker
# (Gunakan slice sampai end_marker_pos+1 agar karakter penutup ikut terambil/dibuang)
if end_marker_pos == -1: end_marker_pos = len(buffer)
full_message_bytes = buffer[:end_marker_pos]
# Sisa buffer (jika ada paket nempel di belakangnya) disimpan untuk loop berikutnya
buffer = buffer[end_marker_pos:]
# Bersihkan buffer dari sisa marker di awal (jika loop sebelumnya menyisakan sampah)
buffer = buffer.lstrip(b'\x04').lstrip(b'\r').lstrip(b'\n')
# Decode ke string
temp_str = full_message_bytes.decode('latin-1', errors='ignore')
# --- SANITIZING (PEMBERSIHAN) ---
# Cari MSH pertama
if "MSH|" in temp_str:
msh_index = temp_str.find("MSH|")
clean_hl7 = temp_str[msh_index:]
# Logging sample data (50 karakter)
print(f"[TCP] Pesan Lengkap Diterima: {clean_hl7[:50]}...")
# Panggil Parser
parse_hl7_result(clean_hl7, device_name=f"GeneXpert-{addr[0]}")
# --- KIRIM ACK ---
try:
lines = clean_hl7.split('\r')
msh_fields = lines[0].split('|')
if len(msh_fields) > 9:
msg_control_id = msh_fields[9]
ack_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
ack_msg = f"MSH|^~\\&|LIS|LAB|GeneXpert|Cepheid|{ack_time}||ACK|{msg_control_id}|P|2.5\rMSA|AA|{msg_control_id}\r"
# Kirim ACK format MLLP
full_ack = f"\x0b{ack_msg}\x1c\r"
conn.sendall(full_ack.encode('utf-8'))
print(f"[ACK] Terkirim untuk ID {msg_control_id}")
except Exception as e:
print(f"Gagal kirim ACK: {e}")
else:
# Jika pesan lengkap tapi tidak ada MSH (misal cuma EOT doang)
pass
except Exception as e:
print(f"[Loop Error] {e}")
# Jangan break, lanjut terima data
time.sleep(0.1)
except Exception as e:
logging.error(f"[ERROR] Koneksi {ip_sender} terputus: {e}")
logging.error(f"[TCP Error] Koneksi {addr} terputus: {e}")
finally:
with connection_lock:
if ip_sender in active_genexpert_connections:
del active_genexpert_connections[ip_sender]
conn.close()
logging.info(f"[DISCONNECT] Koneksi dengan {ip_sender} ditutup.")
logging.info(f"[TCP] Koneksi {addr} ditutup.")
def run_tcp_server():
"""Loop utama TCP Server"""