From f301de8cb5674faf4effc0c943bb6e19e8a02b26 Mon Sep 17 00:00:00 2001 From: Dwi Swandhana Date: Thu, 16 Apr 2026 17:56:50 +0700 Subject: [PATCH] Support ASTM framing for GeneXpert responses --- listener/app.py | 51 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/listener/app.py b/listener/app.py index 57191272..90bdb398 100644 --- a/listener/app.py +++ b/listener/app.py @@ -439,15 +439,13 @@ def create_genexpert_rsp_z02_response(orders, incoming_hl7, ip_addr=None): return "\r".join(segments) + "\r" -def send_all_orders(conn, ip_addr, hl7_msg, msg_id): +def send_all_orders(conn, ip_addr, hl7_msg, msg_id, response_framing="mllp"): orders = get_genexpert_query_orders(ip_addr, hl7_msg) scheduled_orders = [] if not orders: print(f"[GENEXPERT] Tidak ada order pending untuk {ip_addr}") rsp = create_genexpert_rsp_z02_response([], hl7_msg, ip_addr=ip_addr) - log_genexpert_hl7("OUT", ip_addr, rsp, label="qbp-empty") - log_genexpert_hl7_full("OUT", ip_addr, rsp, label="qbp-empty") - conn.sendall(f"\x0b{rsp}\x1c\r".encode('utf-8')) + send_genexpert_response(conn, ip_addr, rsp, response_framing, label="qbp-empty") return qpd_segment = extract_segment(hl7_msg, "QPD") @@ -457,11 +455,8 @@ def send_all_orders(conn, ip_addr, hl7_msg, msg_id): ) print(f"[GENEXPERT] Mengirim {len(orders)} order ke {ip_addr}") rsp = create_genexpert_rsp_z02_response(orders, hl7_msg, ip_addr=ip_addr) - mllp = f"\x0b{rsp}\x1c\r" first_accnumber = str(orders[0].rnoreg or "").strip() if orders else "" - log_genexpert_hl7("OUT", ip_addr, rsp, label=f"qbp-order:{first_accnumber}") - log_genexpert_hl7_full("OUT", ip_addr, rsp, label=f"qbp-order:{first_accnumber}") - conn.sendall(mllp.encode('utf-8')) + send_genexpert_response(conn, ip_addr, rsp, response_framing, label=f"qbp-order:{first_accnumber}") for order in orders: print(f"[GENEXPERT] Order ditawarkan ke {ip_addr}: {order.rnoreg}") @@ -530,6 +525,34 @@ def log_genexpert_hl7_full(direction, ip_addr, hl7_message, label=""): f"[GENEXPERT-HL7-{direction}-FULL] ip={ip_addr}, type={message_type}, control_id={control_id}{suffix}, payload={payload}" ) +def detect_genexpert_message_framing(message_bytes): + raw = message_bytes or b"" + if b"\x1c" in raw or raw.startswith(b"\x0b"): + return "mllp" + if b"\x03" in raw: + return "astm" + return "plain" + +def frame_genexpert_response(hl7_message, framing): + message = str(hl7_message or "") + if framing == "astm": + frame_body = f"{message}\x03" + chk = calculate_astm_checksum(frame_body) + return f"\x02{frame_body}{chk}\r\n".encode("latin-1") + if framing == "mllp": + return f"\x0b{message}\x1c\r".encode("utf-8") + return message.encode("utf-8") + +def send_genexpert_response(conn, ip_addr, hl7_message, framing, label=""): + log_genexpert_hl7("OUT", ip_addr, hl7_message, label=label) + log_genexpert_hl7_full("OUT", ip_addr, hl7_message, label=label) + payload = frame_genexpert_response(hl7_message, framing) + print( + f"[GENEXPERT-DEBUG] Send response ip={ip_addr}, framing={framing}, " + f"label={label}, bytes={len(payload)}" + ) + conn.sendall(payload) + def build_genexpert_result_query(accnumber, msg_control_id): ts = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # Query hasil berbasis accession number di QRD-8. @@ -2004,6 +2027,7 @@ def handle_genexpert_client(conn, addr): if end_marker_pos == -1: end_marker_pos = len(buffer) full_message_bytes = buffer[:end_marker_pos] + response_framing = detect_genexpert_message_framing(full_message_bytes) # Sisa buffer (jika ada paket nempel di belakangnya) disimpan untuk loop berikutnya buffer = buffer[end_marker_pos:] @@ -2045,10 +2069,7 @@ def handle_genexpert_client(conn, addr): # 2. Kirim ACK (Terima Kasih) ack_msg = create_genexpert_ack_r01_response(clean_hl7) - full_ack = f"\x0b{ack_msg}\x1c\r" - log_genexpert_hl7("OUT", addr[0], ack_msg, label="oru-ack") - log_genexpert_hl7_full("OUT", addr[0], ack_msg, label="oru-ack") - conn.sendall(full_ack.encode('utf-8')) + send_genexpert_response(conn, addr[0], ack_msg, response_framing, label="oru-ack") print(f"[ACK SENT] Untuk hasil ID {incoming_control_id}") continue @@ -2058,15 +2079,13 @@ def handle_genexpert_client(conn, addr): elif "QBP^Z01" in clean_hl7 or "QBP^Z03" in clean_hl7: print("[GENEXPERT] Alat meminta ORDER") msg_id = extract_msg_control_id(clean_hl7) - send_all_orders(conn, addr[0], clean_hl7, msg_id) + send_all_orders(conn, addr[0], clean_hl7, msg_id, response_framing=response_framing) continue elif "QCN^J01" in clean_hl7: clear_genexpert_inflight_for_ip(addr[0], reason="query-confirmation") ack_msg = create_genexpert_ack_j01_response(clean_hl7) - log_genexpert_hl7("OUT", addr[0], ack_msg, label="qcn-ack") - log_genexpert_hl7_full("OUT", addr[0], ack_msg, label="qcn-ack") - conn.sendall(f"\x0b{ack_msg}\x1c\r".encode('utf-8')) + send_genexpert_response(conn, addr[0], ack_msg, response_framing, label="qcn-ack") print(f"[GENEXPERT] Menerima konfirmasi query dari {addr[0]}.") continue