This commit is contained in:
Dwi Swandhana
2026-01-31 06:45:48 +07:00
parent d8ff19c9b2
commit 11bc7e172f
+112 -95
View File
@@ -679,6 +679,7 @@ def parse_and_save_vitek_result(raw_data, port_name="VITEK"):
session.rollback() session.rollback()
finally: finally:
session.close() session.close()
def create_vitek_order_message(order): def create_vitek_order_message(order):
""" """
Membuat Frame Order Vitek sesuai Manual Ref 514937. Membuat Frame Order Vitek sesuai Manual Ref 514937.
@@ -741,9 +742,17 @@ def manage_vitek_port(config):
with serial.Serial( with serial.Serial(
port=port_name, port=port_name,
baudrate=config['baud_rate'], baudrate=config['baud_rate'],
timeout=1 timeout=2,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
xonxoff=False,
rtscts=False, # Pastikan flow control mati agar tidak blocking hardware
dsrdtr=False
) as ser: ) as ser:
ser.reset_input_buffer()
ser.reset_output_buffer()
print(f"[{port_name}] Port Connected. Waiting activity...")
while True: while True:
has_activity = False # Penanda agar kita sleep kalau sepi has_activity = False # Penanda agar kita sleep kalau sepi
@@ -753,47 +762,63 @@ def manage_vitek_port(config):
try: try:
if ser.in_waiting > 0: if ser.in_waiting > 0:
has_activity = True has_activity = True
data_chunk = ser.read(ser.in_waiting or 1024) char = ser.read(1)
#data_chunk = ser.read(ser.in_waiting or 1024)
# 1. HANDLE HANDSHAKE (ENQ -> ACK)
if char == b'\x05': # ENQ
logging.info(f"[{port_name}] Terima ENQ. Kirim ACK.")
print(f"[{port_name}] Terima ENQ. Kirim ACK.")
ser.write(b'\x06') # ACK
rx_buffer = b"" # Siap terima frame baru
continue
if data_chunk: # 2. HANDLE END OF TRANSMISSION (EOT)
# 1. Handle Handshake Awal (ENQ) - Alat minta izin kirim elif char == b'\x04': # EOT
if b'\x05' in data_chunk: logging.info(f"[{port_name}] Sesi Selesai (EOT).")
logging.info(f"[{port_name}] Terima ENQ. Kirim ACK.") print(f"[{port_name}] Sesi Selesai (EOT).")
print(f"[{port_name}] Terima ENQ. Kirim ACK.") # Jika masih ada sisa buffer yang belum diproses (jarang terjadi jika logic benar), proses disini
ser.write(b'\x06') # ACK rx_buffer = b""
rx_buffer = b"" # Reset buffer bytes continue
continue
# 2. Tampung Data # 3. HANDLE DATA FRAME
rx_buffer += data_chunk else:
rx_buffer += char
if ser.in_waiting:
rx_buffer += ser.read(ser.in_waiting)
if b'\x03' in rx_buffer:
# Cek apakah sudah ada CRLF (akhir mutlak)
if rx_buffer.endswith(b'\n'):
logging.info(f"[{port_name}] Frame Lengkap diterima. KIRIM ACK!")
print(f"[{port_name}] Frame Lengkap diterima. KIRIM ACK!")
# --- KRITIKAL: KIRIM ACK SEKARANG JUGA ---
# Jangan tunggu EOT. Kirim ACK per frame agar Vitek lanjut ke pesan berikutnya (antibiotik)
ser.write(b'\x06')
# -----------------------------------------
# 3. Cek apakah Frame Selesai? # Proses Data
# Vitek biasanya mengirim per baris diakhiri LF (\n) atau CR (\r) try:
# Atau Frame diakhiri ETX (\x03) full_str = rx_buffer.decode('latin-1', errors='ignore')
# Kita kirim ACK setiap kali ada tanda akhir baris/frame agar alat lanjut kirim logging.info(f"[{port_name}] Data: {full_str[:50]}...")
if b'\x03' in data_chunk or b'\n' in data_chunk or b'\r' in data_chunk: print(f"[{port_name}] Data: {full_str[:50]}...")
ser.write(b'\x06') # ACK data yang baru masuk # Panggil parser Vitek
if len(full_str) > 5:
# 4. Handle Akhir Transmisi (EOT) - Selesai parse_and_save_vitek_result(full_str, alat_name)
if b'\x04' in data_chunk: except Exception as e:
logging.info(f"[{port_name}] Terima EOT. Memproses data...") logging.error(f"Error Parse: {e}")
print(f"Error Parse: {e}")
# Decode saat data sudah lengkap (gunakan latin-1) # Kosongkan buffer untuk frame berikutnya
try: rx_buffer = b""
full_str = rx_buffer.decode('latin-1', errors='ignore')
parse_and_save_vitek_result(full_str, alat_name)
except Exception as e:
logging.error(f"[{port_name}] Error Decoding/Parsing: {e}")
print(f"[{port_name}] Error Decoding/Parsing: {e}")
rx_buffer = b"" # Bersihkan buffer
continue
# Safety: Jika buffer kepenuhan sampah (lebih dari 4KB) tanpa framing, buang.
if len(rx_buffer) > 5000:
logging.warning(f"[{port_name}] Buffer overflow/Garbage. Reset.")
print(f"[{port_name}] Buffer overflow/Garbage. Reset.")
rx_buffer = b""
except Exception as e: except Exception as e:
logging.error(f"[{port_name}] Error Reading: {e}") logging.error(f"[{port_name}] Error Reading: {e}")
print(f"[{port_name}] Error Reading: {e}") print(f"[{port_name}] Error Reading: {e}")
rx_buffer = b"" # Reset jika error parah rx_buffer = b"" # Reset jika error parah
# ========================================== # ==========================================
# PHASE 2: SENDING ORDER (JIKA BUFFER KOSONG) # PHASE 2: SENDING ORDER (JIKA BUFFER KOSONG)
# ========================================== # ==========================================
@@ -810,80 +835,72 @@ def manage_vitek_port(config):
has_activity = True # Jangan sleep lama-lama has_activity = True # Jangan sleep lama-lama
logging.info(f"[{port_name}] Menemukan Order: {pending_order.rnoreg}. Memulai Handshake...") logging.info(f"[{port_name}] Menemukan Order: {pending_order.rnoreg}. Memulai Handshake...")
print(f"[{port_name}] Menemukan Order: {pending_order.rnoreg}. Memulai Handshake...") print(f"[{port_name}] Menemukan Order: {pending_order.rnoreg}. Memulai Handshake...")
# --- STEP 1: HANDSHAKE (ENQ) --- handshake_success = False
ser.reset_input_buffer() handshake_attempts = 0
ser.write(b'\x05') while handshake_attempts < 3:
time.sleep(0.5) # --- STEP 1: HANDSHAKE (ENQ) ---
ser.reset_input_buffer()
ser.write(b'\x05')
time.sleep(0.5)
response = ser.read(1)
# Baca balasan (tunggu ACK) if response == b'\x06': # ACK received
ack_response = ser.read(1) handshake_success = True
break
elif response == b'\x15': # NAK (Alat nolak)
logging.warning(f"[{port_name}] Dibalas NAK. Tunggu 2 detik...")
time.sleep(2)
else:
# Timeout atau respon aneh
# logging.debug(f"[{port_name}] No ACK (Got: {response}). Retry {handshake_attempts+1}...")
pass
if ack_response == b'\x06': handshake_attempts += 1
logging.info(f"[{port_name}] Handshake dengan {alat_name}Sukses. Kirim Data...") time.sleep(1) # Jeda antar percobaan
print(f"[{port_name}] Handshake dengan {alat_name} Sukses. Kirim Data...")
time.sleep(1.0) # Jeda aman
if handshake_success:
# === SUKSES HANDSHAKE, KIRIM DATA ===
logging.info(f"[{port_name}] Handshake OK. Sending Frames...")
frames = create_vitek_order_message(pending_order) frames = create_vitek_order_message(pending_order)
all_success = True all_frames_sent = True
# --- STEP 2: SEND FRAMES --- for frame in frames:
for i, frame in enumerate(frames): ser.write(frame)
retry = 0 # Tunggu ACK Frame
frame_sent = False t_wait = time.time()
while retry < 3: got_ack = False
logging.info(f"[{port_name}] Kirim Frame (Try {retry+1}): {frame}") while time.time() - t_wait < 3: # 3 detik timeout per frame
print(f"[{port_name}] Kirim Frame (Try {retry+1}): {frame}") if ser.in_waiting:
ser.write(frame) if ser.read(1) == b'\x06':
got_ack = True
# Tunggu ACK
start_wait = time.time()
response = None
while time.time() - start_wait < 3: # Timeout 3 detik
if ser.in_waiting:
response = ser.read(1)
break break
if response == b'\x06': # ACK if not got_ack:
logging.info(f"[{port_name}] Frame ACK (OK).") logging.error(f"[{port_name}] Frame Timeout/NoACK. Abort.")
print(f"[{port_name}] Frame ACK (OK).") all_frames_sent = False
frame_sent = True
break
elif response == b'\x15': # NAK
logging.warning(f"[{port_name}] Dibalas NAK (Checksum/Format Salah).")
print(f"[{port_name}] Dibalas NAK (Checksum/Format Salah).")
time.sleep(1)
retry += 1
elif response is None: # TIMEOUT
logging.warning(f"[{port_name}] Timeout (Alat tidak membalas). Cek Framing/Kabel.")
print(f"[{port_name}] Timeout (Alat tidak membalas). Cek Framing/Kabel.")
retry += 1
else: # Respon Aneh
logging.warning(f"[{port_name}] Respon aneh: {response}")
print(f"[{port_name}] Respon aneh: {response}")
retry += 1
if not frame_sent:
all_success = False
break break
# --- STEP 3: TERMINATE (EOT) --- # Tutup Sesi
ser.write(b'\x04') ser.write(b'\x04') # EOT
if all_success: if all_frames_sent:
logging.info(f"[{port_name}] Order {pending_order.rnoreg} SELESAI.") logging.info(f"[{port_name}] Order Sukses Terkirim.")
print(f"[{port_name}] Order {pending_order.rnoreg} SELESAI.")
setattr(pending_order, flag_col, True) setattr(pending_order, flag_col, True)
session.commit() session.commit()
else: else:
logging.error(f"[{port_name}] Gagal kirim order {pending_order.rnoreg}.") logging.warning(f"[{port_name}] Order Gagal Tengah Jalan.")
print(f"[{port_name}] Gagal kirim order {pending_order.rnoreg}.")
else:
# Handshake gagal (Alat sibuk/Mati)
# logging.debug(f"[{port_name}] Alat sibuk/tidak balas ENQ.")
print(f"[{port_name}] Alat sibuk/tidak balas ENQ.")
pass
else:
# === GAGAL HANDSHAKE 3x -> FORCE RESET ALAT ===
# Inilah solusi masalah "Stuck" Anda
logging.warning(f"[{port_name}] ALAT STUCK/SIBUK. Mengirim FORCE EOT (Reset State)...")
print(f"[{port_name}] ALAT STUCK -> FORCE RESET.")
ser.write(b'\x04') # Kirim EOT paksa
time.sleep(2) # Beri waktu alat bernafas
ser.reset_input_buffer() # Buang sampah sisa
# Kita tidak update DB, biar loop berikutnya coba lagi
session.close() session.close()
except Exception as e: except Exception as e:
@@ -897,7 +914,7 @@ def manage_vitek_port(config):
# Jika tidak ada data masuk dan tidak ada order keluar, tidur sebentar # Jika tidak ada data masuk dan tidak ada order keluar, tidur sebentar
# Ini penting agar CPU tidak 100% dan DB tidak jebol # Ini penting agar CPU tidak 100% dan DB tidak jebol
if not has_activity: if not has_activity:
time.sleep(1.0) time.sleep(0.5)
except Exception as e: except Exception as e:
logging.critical(f"[{port_name}] Gagal connect Serial: {e}") logging.critical(f"[{port_name}] Gagal connect Serial: {e}")