@ -113,15 +113,72 @@ def run_simulation():
creationflags = subprocess . CREATE_NEW_PROCESS_GROUP ,
)
import re
ansi_escape = re . compile ( r ' \ x1B(?:[@-Z \\ -_]| \ [[0-?]*[ -/]*[@-~]) ' )
full_log = [ ]
log_file_path = None
yield f " data: [INFO] Starting command: { ' ' .join(cmd)} \n \n "
for line in iter ( process . stdout . readline , " " ) :
if line :
yield f " data: {line.rstrip()} \n \n "
def write_to_log ( text ) :
if log_file_path :
try :
with open ( log_file_path , " a " , encoding = " utf-8 " ) as f :
f . write ( text + " \n " )
except Exception :
pass
frame_show_count = 0
for raw_line in iter ( process . stdout . readline , " " ) :
if raw_line :
# Strip ANSI and clean up trailing newlines
clean_line = ansi_escape . sub ( ' ' , raw_line ) . rstrip ( ' \r \n ' )
if not clean_line . strip ( ) :
continue
# ------------------------------------------------------
# SSE Filtering: Sample high-frequency logs for the UI
# ------------------------------------------------------
should_yield = True
if " [FRAME " in clean_line :
frame_show_count + = 1
if frame_show_count % 10 != 1 : # Show 1st, 11th, 21st, etc.
should_yield = False
if should_yield :
yield f " data: {clean_line} \n \n "
# ------------------------------------------------------
# Log Mirroring: Always write every cleaned line to disk
# ------------------------------------------------------
if not log_file_path :
full_log . append ( clean_line )
if " Saving data to: " in clean_line :
try :
# Extract path: "Saving data to: data\showcase_XXXX ---"
# Note: the log says "--- Recorder initialized. Saving data to: data\showcase_2026... ---"
path_part = clean_line . split ( " Saving data to: " ) [ 1 ]
rel_path = path_part . split ( " --- " ) [ 0 ] . strip ( )
log_file_path = os . path . join ( PROJECT_ROOT , rel_path , " console.log " )
os . makedirs ( os . path . dirname ( log_file_path ) , exist_ok = True )
# Flush backlog
for prev in full_log :
write_to_log ( prev )
except Exception :
pass
else :
# Once file path is known, append all new lines
write_to_log ( clean_line )
process . stdout . close ( )
return_code = process . wait ( )
yield f " data: [PROCESS_COMPLETED] Exit Code: {return_code} \n \n "
end_msg = f " [PROCESS_COMPLETED] Exit Code: {return_code} "
yield f " data: {end_msg} \n \n "
write_to_log ( end_msg )
except Exception as e :
yield f " data: [ERROR] Failed to start process: {str(e)} \n \n "