Enable GenExpert auto polling and flag eligible TCM orders
This commit is contained in:
@@ -1255,7 +1255,13 @@ class FrontpageController extends Controller
|
||||
'nmpendaftar' => $clientIp,
|
||||
'orderid' => $notransaksi,
|
||||
]);
|
||||
$pesan = $nofoto;
|
||||
$pesan = $nofoto;
|
||||
$daftaridtcm = [79,80,81,82,83,84,203, 14,28,55,62,94,104,105,124,138,146];
|
||||
if (in_array($poli_id, $daftaridtcm)){
|
||||
$genexpert = true;
|
||||
} else {
|
||||
$genexpert = false;
|
||||
}
|
||||
Paslab::updateOrCreate(
|
||||
[
|
||||
'rnoreg' => $nofoto,
|
||||
@@ -1278,9 +1284,9 @@ class FrontpageController extends Controller
|
||||
'flg_vitek2' => false,
|
||||
'flg_bd1' => false,
|
||||
'flg_bd2' => false,
|
||||
'flg_gxp1' => false,
|
||||
'flg_gxp2' => false,
|
||||
'flg_gxp3' => false,
|
||||
'flg_gxp1' => $genexpert,
|
||||
'flg_gxp2' => $genexpert,
|
||||
'flg_gxp3' => $genexpert,
|
||||
'flg_vitek3' => false,
|
||||
]
|
||||
);
|
||||
@@ -1478,6 +1484,13 @@ class FrontpageController extends Controller
|
||||
'orderid' => $notransaksi,
|
||||
]);
|
||||
$pesan = $nofoto;
|
||||
$daftaridtcm = [79,80,81,82,83,84,203, 14,28,55,62,94,104,105,124,138,146];
|
||||
if (in_array($poli_id, $daftaridtcm)){
|
||||
$genexpert = true;
|
||||
} else {
|
||||
$genexpert = false;
|
||||
}
|
||||
|
||||
Paslab::updateOrCreate(
|
||||
[
|
||||
'rnoreg' => $nofoto,
|
||||
@@ -1500,9 +1513,9 @@ class FrontpageController extends Controller
|
||||
'flg_vitek2' => false,
|
||||
'flg_bd1' => false,
|
||||
'flg_bd2' => false,
|
||||
'flg_gxp1' => false,
|
||||
'flg_gxp2' => false,
|
||||
'flg_gxp3' => false,
|
||||
'flg_gxp1' => $genexpert,
|
||||
'flg_gxp2' => $genexpert,
|
||||
'flg_gxp3' => $genexpert,
|
||||
'flg_vitek3' => false,
|
||||
]
|
||||
);
|
||||
@@ -1729,6 +1742,13 @@ class FrontpageController extends Controller
|
||||
'pendaftar' => Session('previlage'),
|
||||
'nmpendaftar' => Session('nama'),
|
||||
]);
|
||||
$daftaridtcm = [79,80,81,82,83,84,203, 14,28,55,62,94,104,105,124,138,146];
|
||||
if (in_array($poli_id, $daftaridtcm)){
|
||||
$genexpert = true;
|
||||
} else {
|
||||
$genexpert = false;
|
||||
}
|
||||
|
||||
Paslab::updateOrCreate(
|
||||
[
|
||||
'rnoreg' => $nofoto,
|
||||
@@ -1751,9 +1771,9 @@ class FrontpageController extends Controller
|
||||
'flg_vitek2' => false,
|
||||
'flg_bd1' => false,
|
||||
'flg_bd2' => false,
|
||||
'flg_gxp1' => false,
|
||||
'flg_gxp2' => false,
|
||||
'flg_gxp3' => false,
|
||||
'flg_gxp1' => $genexpert,
|
||||
'flg_gxp2' => $genexpert,
|
||||
'flg_gxp3' => $genexpert,
|
||||
'flg_vitek3' => false,
|
||||
]
|
||||
);
|
||||
@@ -1876,9 +1896,9 @@ class FrontpageController extends Controller
|
||||
'flg_vitek2' => false,
|
||||
'flg_bd1' => false,
|
||||
'flg_bd2' => false,
|
||||
'flg_gxp1' => false,
|
||||
'flg_gxp2' => false,
|
||||
'flg_gxp3' => false,
|
||||
'flg_gxp1' => $genexpert,
|
||||
'flg_gxp2' => $genexpert,
|
||||
'flg_gxp3' => $genexpert,
|
||||
'flg_vitek3' => false,
|
||||
]
|
||||
);
|
||||
@@ -1945,9 +1965,9 @@ class FrontpageController extends Controller
|
||||
'flg_vitek2' => false,
|
||||
'flg_bd1' => false,
|
||||
'flg_bd2' => false,
|
||||
'flg_gxp1' => false,
|
||||
'flg_gxp2' => false,
|
||||
'flg_gxp3' => false,
|
||||
'flg_gxp1' => $genexpert,
|
||||
'flg_gxp2' => $genexpert,
|
||||
'flg_gxp3' => $genexpert,
|
||||
'flg_vitek3' => false,
|
||||
]
|
||||
);
|
||||
|
||||
+214
-11
@@ -39,10 +39,15 @@ active_genexpert_connections = {}
|
||||
connection_lock = threading.Lock()
|
||||
pending_result_queries = {}
|
||||
pending_query_lock = threading.Lock()
|
||||
scheduled_result_queries = {}
|
||||
scheduled_result_query_lock = threading.Lock()
|
||||
# Network Configuration
|
||||
TCP_LISTENER_PORT = 6001 # PC GeneXpert set ke mode Client, konek ke IP:PORT ini
|
||||
SERVER_HOST = '0.0.0.0' # Listen di semua interface
|
||||
HTTP_API_PORT = 6002 # Endpoint trigger dari Laravel -> Python
|
||||
GENEXPERT_RESULT_QUERY_INITIAL_DELAY_SECONDS = 60
|
||||
GENEXPERT_RESULT_QUERY_INTERVAL_SECONDS = 120
|
||||
GENEXPERT_RESULT_QUERY_MAX_DURATION_SECONDS = 21600
|
||||
# Mapping Flag ke IP Address GeneXpert
|
||||
# Pastikan IP ini SESUAI dengan settingan "Server IP" di masing-masing alat (Client Mode)
|
||||
TARGET_MAPPING = {
|
||||
@@ -220,6 +225,7 @@ def get_pending_orders(ip_addr):
|
||||
def send_all_orders(conn, ip_addr, hl7_msg, msg_id):
|
||||
orders = get_pending_orders(ip_addr)
|
||||
session = SessionLocal()
|
||||
scheduled_orders = []
|
||||
try:
|
||||
if not orders:
|
||||
print(f"[GENEXPERT] Tidak ada order pending untuk {ip_addr}")
|
||||
@@ -242,8 +248,19 @@ def send_all_orders(conn, ip_addr, hl7_msg, msg_id):
|
||||
flag = get_flag_by_device(ip_addr)
|
||||
setattr(order, flag, True)
|
||||
session.add(order)
|
||||
scheduled_orders.append({
|
||||
"accnumber": str(order.rnoreg or "").strip(),
|
||||
"register_no": str(order.rnoreg or "").strip(),
|
||||
"target_ip": ip_addr,
|
||||
})
|
||||
|
||||
session.commit()
|
||||
for scheduled_order in scheduled_orders:
|
||||
schedule_result_query_for_order(
|
||||
accnumber=scheduled_order["accnumber"],
|
||||
register_no=scheduled_order["register_no"],
|
||||
target_ip=scheduled_order["target_ip"],
|
||||
)
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
@@ -257,6 +274,16 @@ def extract_msg_control_id(hl7_message):
|
||||
except:
|
||||
return None
|
||||
|
||||
def extract_message_type(hl7_message):
|
||||
try:
|
||||
segments = hl7_message.split('\r')
|
||||
msh = segments[0].split('|')
|
||||
if len(msh) > 8:
|
||||
return msh[8].strip()
|
||||
return ""
|
||||
except:
|
||||
return ""
|
||||
|
||||
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.
|
||||
@@ -270,6 +297,164 @@ def get_active_genexpert_ips():
|
||||
with connection_lock:
|
||||
return list(active_genexpert_connections.keys())
|
||||
|
||||
def stop_scheduled_result_query(accnumber, reason="completed"):
|
||||
accnumber = str(accnumber or "").strip()
|
||||
if not accnumber:
|
||||
return False
|
||||
|
||||
with scheduled_result_query_lock:
|
||||
state = scheduled_result_queries.get(accnumber)
|
||||
if not state:
|
||||
return False
|
||||
state["status"] = reason
|
||||
stop_event = state.get("stop_event")
|
||||
if stop_event:
|
||||
stop_event.set()
|
||||
scheduled_result_queries.pop(accnumber, None)
|
||||
|
||||
logging.info(f"[GENEXPERT-SCHEDULER] Stop accnumber={accnumber}, reason={reason}")
|
||||
print(f"[GENEXPERT-SCHEDULER] Stop accnumber={accnumber}, reason={reason}")
|
||||
return True
|
||||
|
||||
def scheduled_result_query_worker(accnumber):
|
||||
while True:
|
||||
with scheduled_result_query_lock:
|
||||
state = scheduled_result_queries.get(accnumber)
|
||||
if not state:
|
||||
return
|
||||
stop_event = state["stop_event"]
|
||||
target_ip = state.get("target_ip")
|
||||
register_no = state.get("register_no") or accnumber
|
||||
attempt = int(state.get("attempt", 0))
|
||||
created_at = state.get("created_at") or datetime.datetime.now()
|
||||
max_duration_seconds = max(
|
||||
int(state.get("max_duration_seconds", GENEXPERT_RESULT_QUERY_MAX_DURATION_SECONDS)),
|
||||
1
|
||||
)
|
||||
age_seconds = max(int((datetime.datetime.now() - created_at).total_seconds()), 0)
|
||||
if age_seconds >= max_duration_seconds:
|
||||
state["status"] = "expired"
|
||||
state["expired_at"] = datetime.datetime.now()
|
||||
state["age_seconds"] = age_seconds
|
||||
stop_event.set()
|
||||
scheduled_result_queries.pop(accnumber, None)
|
||||
logging.info(
|
||||
f"[GENEXPERT-SCHEDULER] Expired accnumber={accnumber}, "
|
||||
f"age={age_seconds}s, max_duration={max_duration_seconds}s"
|
||||
)
|
||||
print(
|
||||
f"[GENEXPERT-SCHEDULER] Expired accnumber={accnumber}, "
|
||||
f"age={age_seconds}s, max_duration={max_duration_seconds}s"
|
||||
)
|
||||
return
|
||||
next_delay = max(
|
||||
int(
|
||||
state.get(
|
||||
"initial_delay_seconds" if attempt == 0 else "interval_seconds",
|
||||
GENEXPERT_RESULT_QUERY_INITIAL_DELAY_SECONDS if attempt == 0 else GENEXPERT_RESULT_QUERY_INTERVAL_SECONDS
|
||||
)
|
||||
),
|
||||
1
|
||||
)
|
||||
|
||||
if stop_event.wait(next_delay):
|
||||
return
|
||||
|
||||
with scheduled_result_query_lock:
|
||||
state = scheduled_result_queries.get(accnumber)
|
||||
if not state:
|
||||
return
|
||||
state["attempt"] = int(state.get("attempt", 0)) + 1
|
||||
state["last_requested_at"] = datetime.datetime.now()
|
||||
attempt_no = state["attempt"]
|
||||
|
||||
logging.info(
|
||||
f"[GENEXPERT-SCHEDULER] Trigger query hasil accnumber={accnumber}, "
|
||||
f"register_no={register_no}, target_ip={target_ip}, attempt={attempt_no}"
|
||||
)
|
||||
print(
|
||||
f"[GENEXPERT-SCHEDULER] Trigger query hasil accnumber={accnumber}, "
|
||||
f"register_no={register_no}, target_ip={target_ip}, attempt={attempt_no}"
|
||||
)
|
||||
|
||||
result = trigger_result_query_to_genexpert(
|
||||
accnumber=accnumber,
|
||||
register_no=register_no,
|
||||
target_ip=target_ip,
|
||||
wait_seconds=0,
|
||||
)
|
||||
|
||||
with scheduled_result_query_lock:
|
||||
state = scheduled_result_queries.get(accnumber)
|
||||
if not state:
|
||||
return
|
||||
state["last_result"] = result
|
||||
|
||||
def schedule_result_query_for_order(
|
||||
accnumber,
|
||||
register_no,
|
||||
target_ip=None,
|
||||
initial_delay_seconds=GENEXPERT_RESULT_QUERY_INITIAL_DELAY_SECONDS,
|
||||
interval_seconds=GENEXPERT_RESULT_QUERY_INTERVAL_SECONDS,
|
||||
max_duration_seconds=GENEXPERT_RESULT_QUERY_MAX_DURATION_SECONDS,
|
||||
):
|
||||
accnumber = str(accnumber or "").strip()
|
||||
register_no = str(register_no or accnumber).strip()
|
||||
target_ip = str(target_ip or "").strip() or None
|
||||
|
||||
if not accnumber:
|
||||
logging.warning("[GENEXPERT-SCHEDULER] Jadwal query hasil dilewati karena accnumber kosong.")
|
||||
print("[GENEXPERT-SCHEDULER] Jadwal query hasil dilewati karena accnumber kosong.")
|
||||
return False
|
||||
|
||||
with scheduled_result_query_lock:
|
||||
existing = scheduled_result_queries.get(accnumber)
|
||||
if existing:
|
||||
existing["register_no"] = register_no
|
||||
existing["target_ip"] = target_ip
|
||||
existing["initial_delay_seconds"] = initial_delay_seconds
|
||||
existing["interval_seconds"] = interval_seconds
|
||||
existing["max_duration_seconds"] = max_duration_seconds
|
||||
existing["status"] = "active"
|
||||
logging.info(f"[GENEXPERT-SCHEDULER] Jadwal sudah aktif untuk accnumber={accnumber}")
|
||||
print(f"[GENEXPERT-SCHEDULER] Jadwal sudah aktif untuk accnumber={accnumber}")
|
||||
return True
|
||||
|
||||
stop_event = threading.Event()
|
||||
worker = threading.Thread(
|
||||
target=scheduled_result_query_worker,
|
||||
args=(accnumber,),
|
||||
name=f"GeneXpertResultQuery-{accnumber}",
|
||||
daemon=True,
|
||||
)
|
||||
scheduled_result_queries[accnumber] = {
|
||||
"register_no": register_no,
|
||||
"target_ip": target_ip,
|
||||
"initial_delay_seconds": initial_delay_seconds,
|
||||
"interval_seconds": interval_seconds,
|
||||
"max_duration_seconds": max_duration_seconds,
|
||||
"attempt": 0,
|
||||
"status": "active",
|
||||
"created_at": datetime.datetime.now(),
|
||||
"stop_event": stop_event,
|
||||
"thread_name": worker.name,
|
||||
}
|
||||
|
||||
worker.start()
|
||||
logging.info(
|
||||
f"[GENEXPERT-SCHEDULER] Jadwal dibuat accnumber={accnumber}, "
|
||||
f"register_no={register_no}, target_ip={target_ip}, "
|
||||
f"initial_delay={initial_delay_seconds}s, interval={interval_seconds}s, "
|
||||
f"max_duration={max_duration_seconds}s"
|
||||
)
|
||||
print(
|
||||
f"[GENEXPERT-SCHEDULER] Jadwal dibuat accnumber={accnumber}, "
|
||||
f"register_no={register_no}, target_ip={target_ip}, "
|
||||
f"initial_delay={initial_delay_seconds}s, interval={interval_seconds}s, "
|
||||
f"max_duration={max_duration_seconds}s"
|
||||
)
|
||||
return True
|
||||
|
||||
def trigger_result_query_to_genexpert(accnumber, register_no, target_ip=None, wait_seconds=20):
|
||||
active_ips = get_active_genexpert_ips()
|
||||
if not active_ips:
|
||||
@@ -395,6 +580,7 @@ def parse_hl7_result(conn, msg_id, hl7_message, device_name="GeneXpert"):
|
||||
patient_name = "" # rnmpas
|
||||
result_date = None # tgl_data
|
||||
results_list = [] # untuk organisme
|
||||
message_type = ""
|
||||
|
||||
# Waktu default jika tidak ada di pesan
|
||||
result_date = datetime.datetime.now()
|
||||
@@ -408,6 +594,8 @@ def parse_hl7_result(conn, msg_id, hl7_message, device_name="GeneXpert"):
|
||||
|
||||
# --- MSH (Header) ---
|
||||
if seg_type == 'MSH':
|
||||
if len(fields) > 8 and fields[8]:
|
||||
message_type = fields[8].strip()
|
||||
# Ambil tanggal pesan (Field 7) Format: YYYYMMDDHHMMSS
|
||||
if len(fields) > 6 and fields[6]:
|
||||
try:
|
||||
@@ -456,19 +644,23 @@ def parse_hl7_result(conn, msg_id, hl7_message, device_name="GeneXpert"):
|
||||
|
||||
# 4. Validasi Data Penting
|
||||
if not sample_id:
|
||||
source_ip = ""
|
||||
try:
|
||||
source_ip = str(conn.getpeername()[0]).strip()
|
||||
except Exception:
|
||||
source_ip = str(device_name).replace("GeneXpert-", "").replace(",", " ").strip()
|
||||
if str(message_type).startswith("ORU^"):
|
||||
source_ip = ""
|
||||
try:
|
||||
source_ip = str(conn.getpeername()[0]).strip()
|
||||
except Exception:
|
||||
source_ip = str(device_name).replace("GeneXpert-", "").replace(",", " ").strip()
|
||||
|
||||
if source_ip:
|
||||
logging.info(f"[HL7 Parser] ORU tanpa sample_id dari {source_ip}. Anggap sebagai request order.")
|
||||
print(f"[HL7 Parser] ORU tanpa sample_id dari {source_ip}. Anggap sebagai request order.")
|
||||
send_all_orders(conn, source_ip, clean_hl7, msg_id)
|
||||
if source_ip:
|
||||
logging.info(f"[HL7 Parser] ORU tanpa sample_id dari {source_ip}. Anggap sebagai request order.")
|
||||
print(f"[HL7 Parser] ORU tanpa sample_id dari {source_ip}. Anggap sebagai request order.")
|
||||
send_all_orders(conn, source_ip, clean_hl7, msg_id)
|
||||
else:
|
||||
logging.warning("[HL7 Parser] ORU tanpa sample_id diterima, tetapi IP sumber tidak dapat diidentifikasi.")
|
||||
print("[HL7 Parser] ORU tanpa sample_id diterima, tetapi IP sumber tidak dapat diidentifikasi.")
|
||||
else:
|
||||
logging.warning("[HL7 Parser] ORU tanpa sample_id diterima, tetapi IP sumber tidak dapat diidentifikasi.")
|
||||
print("[HL7 Parser] ORU tanpa sample_id diterima, tetapi IP sumber tidak dapat diidentifikasi.")
|
||||
logging.info(f"[HL7 Parser] Pesan {message_type or 'UNKNOWN'} tanpa sample_id diabaikan.")
|
||||
print(f"[HL7 Parser] Pesan {message_type or 'UNKNOWN'} tanpa sample_id diabaikan.")
|
||||
return
|
||||
|
||||
mapped_no_id = sample_id
|
||||
@@ -506,6 +698,7 @@ def parse_hl7_result(conn, msg_id, hl7_message, device_name="GeneXpert"):
|
||||
session.commit()
|
||||
logging.info(f"[DB] Berhasil simpan ke LisPhoenix: {sample_id}")
|
||||
print(f"[DB] Berhasil simpan ke LisPhoenix: {sample_id}")
|
||||
stop_scheduled_result_query(sample_id, reason="result-received")
|
||||
if pending_event:
|
||||
pending_event.set()
|
||||
|
||||
@@ -1577,6 +1770,11 @@ def handle_genexpert_client(conn, addr):
|
||||
session.commit()
|
||||
logging.info(f"[UPDATE] {target_flag_col} diset TRUE untuk {search_sample_id}")
|
||||
print(f"[UPDATE] {target_flag_col} diset TRUE untuk {search_sample_id}")
|
||||
schedule_result_query_for_order(
|
||||
accnumber=str(order.rnoreg or "").strip(),
|
||||
register_no=str(order.rnoreg or "").strip(),
|
||||
target_ip=client_ip,
|
||||
)
|
||||
|
||||
else:
|
||||
logging.warning(f"[NOT FOUND/ALREADY SENT] Tidak ada order baru untuk {search_sample_id} di kolom {target_flag_col}")
|
||||
@@ -1621,6 +1819,11 @@ def handle_genexpert_client(conn, addr):
|
||||
send_all_orders(conn, addr[0], clean_hl7, msg_id)
|
||||
continue
|
||||
|
||||
elif "QCN^J01" in clean_hl7:
|
||||
logging.info(f"[GENEXPERT] Menerima konfirmasi query dari {addr[0]}.")
|
||||
print(f"[GENEXPERT] Menerima konfirmasi query dari {addr[0]}.")
|
||||
continue
|
||||
|
||||
# Logging sample data (50 karakter)
|
||||
print(f"[GenExpert_TCP] Pesan Lengkap Diterima: {clean_hl7[:50]}...")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user