Add progress bar for simulation tracking and file writing
This commit is contained in:
parent
b7e4c96188
commit
0ce010a12d
@ -12,6 +12,11 @@ from output import MetricsCollector, EntityCollector, EvolutionCollector
|
|||||||
from output.formatters.json_formatter import JSONFormatter
|
from output.formatters.json_formatter import JSONFormatter
|
||||||
from output.formatters.csv_formatter import CSVFormatter
|
from output.formatters.csv_formatter import CSVFormatter
|
||||||
from output.writers.file_writer import FileWriter
|
from output.writers.file_writer import FileWriter
|
||||||
|
try:
|
||||||
|
from tqdm import tqdm
|
||||||
|
TQDM_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
TQDM_AVAILABLE = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -56,6 +61,12 @@ class HeadlessSimulationEngine:
|
|||||||
'evolution': []
|
'evolution': []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Progress tracking
|
||||||
|
self.files_written = 0
|
||||||
|
self.last_progress_update = 0
|
||||||
|
self.progress_update_interval = 1.0 # Update progress every second
|
||||||
|
self.progress_bar = None
|
||||||
|
|
||||||
# Setup signal handlers for graceful shutdown
|
# Setup signal handlers for graceful shutdown
|
||||||
signal.signal(signal.SIGINT, self._signal_handler)
|
signal.signal(signal.SIGINT, self._signal_handler)
|
||||||
signal.signal(signal.SIGTERM, self._signal_handler)
|
signal.signal(signal.SIGTERM, self._signal_handler)
|
||||||
@ -88,6 +99,120 @@ class HeadlessSimulationEngine:
|
|||||||
|
|
||||||
return collectors
|
return collectors
|
||||||
|
|
||||||
|
def _init_progress_bar(self):
|
||||||
|
"""Initialize progress bar for simulation."""
|
||||||
|
if not TQDM_AVAILABLE:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Determine progress total based on configuration
|
||||||
|
if self.config.max_ticks:
|
||||||
|
total = self.config.max_ticks
|
||||||
|
unit = 'ticks'
|
||||||
|
elif self.config.max_duration:
|
||||||
|
total = int(self.config.max_duration)
|
||||||
|
unit = 'sec'
|
||||||
|
else:
|
||||||
|
# No clear total - create indeterminate progress bar
|
||||||
|
total = None
|
||||||
|
unit = 'ticks'
|
||||||
|
|
||||||
|
if total:
|
||||||
|
self.progress_bar = tqdm(
|
||||||
|
total=total,
|
||||||
|
unit=unit,
|
||||||
|
desc="Simulation",
|
||||||
|
leave=True, # Keep the bar when done
|
||||||
|
bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}]'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.progress_bar = tqdm(
|
||||||
|
unit='ticks',
|
||||||
|
desc="Simulation",
|
||||||
|
leave=True,
|
||||||
|
bar_format='{l_bar}{bar}| {n_fmt} [{elapsed}, {rate_fmt}]'
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_progress_bar(self):
|
||||||
|
"""Update progress bar with current status."""
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time - self.last_progress_update < self.progress_update_interval:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_tick = self.simulation_core.state.total_ticks
|
||||||
|
tps = self.simulation_core.state.actual_tps
|
||||||
|
elapsed = current_time - self.start_time
|
||||||
|
|
||||||
|
if TQDM_AVAILABLE and self.progress_bar:
|
||||||
|
# Use tqdm progress bar
|
||||||
|
if self.config.max_ticks:
|
||||||
|
# Update based on tick progress
|
||||||
|
progress = min(current_tick, self.config.max_ticks)
|
||||||
|
self.progress_bar.n = progress
|
||||||
|
self.progress_bar.set_postfix({
|
||||||
|
'TPS': f'{tps:.1f}',
|
||||||
|
'Files': self.files_written
|
||||||
|
})
|
||||||
|
elif self.config.max_duration:
|
||||||
|
# Update based on elapsed time
|
||||||
|
progress = min(elapsed, self.config.max_duration)
|
||||||
|
self.progress_bar.n = int(progress)
|
||||||
|
self.progress_bar.set_postfix({
|
||||||
|
'TPS': f'{tps:.1f}',
|
||||||
|
'Files': self.files_written,
|
||||||
|
'Tick': current_tick
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
# Indeterminate progress
|
||||||
|
self.progress_bar.n = current_tick
|
||||||
|
self.progress_bar.set_postfix({
|
||||||
|
'TPS': f'{tps:.1f}',
|
||||||
|
'Files': self.files_written
|
||||||
|
})
|
||||||
|
|
||||||
|
self.progress_bar.refresh()
|
||||||
|
else:
|
||||||
|
# Simple text-based progress
|
||||||
|
eta_text = ""
|
||||||
|
if self.config.max_ticks and current_tick > 0:
|
||||||
|
tick_rate = current_tick / elapsed if elapsed > 0 else 0
|
||||||
|
remaining_ticks = self.config.max_ticks - current_tick
|
||||||
|
eta_seconds = remaining_ticks / tick_rate if tick_rate > 0 else 0
|
||||||
|
eta_minutes, eta_seconds = divmod(eta_seconds, 60)
|
||||||
|
eta_text = f"ETA: {int(eta_minutes)}m{int(eta_seconds)}s"
|
||||||
|
elif self.config.max_duration:
|
||||||
|
remaining_seconds = self.config.max_duration - elapsed
|
||||||
|
eta_minutes, eta_seconds = divmod(remaining_seconds, 60)
|
||||||
|
eta_text = f"ETA: {int(eta_minutes)}m{int(eta_seconds)}s"
|
||||||
|
|
||||||
|
# Calculate progress percentage if we have a limit
|
||||||
|
progress_pct = ""
|
||||||
|
if self.config.max_ticks:
|
||||||
|
pct = (current_tick / self.config.max_ticks) * 100
|
||||||
|
progress_pct = f"{pct:.1f}%"
|
||||||
|
elif self.config.max_duration:
|
||||||
|
pct = (elapsed / self.config.max_duration) * 100
|
||||||
|
progress_pct = f"{pct:.1f}%"
|
||||||
|
|
||||||
|
progress_line = f"[{current_time - self.start_time:.1f}s] "
|
||||||
|
if progress_pct:
|
||||||
|
progress_line += f"Progress: {progress_pct} "
|
||||||
|
progress_line += f"Tick: {current_tick} TPS: {tps:.1f} Files: {self.files_written}"
|
||||||
|
if eta_text:
|
||||||
|
progress_line += f" {eta_text}"
|
||||||
|
|
||||||
|
# Overwrite the previous line (using carriage return)
|
||||||
|
print(f"\r{progress_line}", end="", flush=True)
|
||||||
|
|
||||||
|
self.last_progress_update = current_time
|
||||||
|
|
||||||
|
def _close_progress_bar(self):
|
||||||
|
"""Close the progress bar."""
|
||||||
|
if not TQDM_AVAILABLE and self.running:
|
||||||
|
# Print a newline to clear the text progress line
|
||||||
|
print()
|
||||||
|
elif TQDM_AVAILABLE and self.progress_bar:
|
||||||
|
self.progress_bar.close()
|
||||||
|
|
||||||
def run(self) -> Dict[str, Any]:
|
def run(self) -> Dict[str, Any]:
|
||||||
"""Run the headless simulation."""
|
"""Run the headless simulation."""
|
||||||
# Determine if we should run at max speed
|
# Determine if we should run at max speed
|
||||||
@ -107,6 +232,9 @@ class HeadlessSimulationEngine:
|
|||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
self.simulation_core.start()
|
self.simulation_core.start()
|
||||||
|
|
||||||
|
# Initialize progress bar
|
||||||
|
self._init_progress_bar()
|
||||||
|
|
||||||
# Enable sprint mode for maximum speed if not real-time mode
|
# Enable sprint mode for maximum speed if not real-time mode
|
||||||
if max_speed_mode:
|
if max_speed_mode:
|
||||||
self.simulation_core.timing.set_sprint_mode(True)
|
self.simulation_core.timing.set_sprint_mode(True)
|
||||||
@ -137,6 +265,9 @@ class HeadlessSimulationEngine:
|
|||||||
self._write_batch_data()
|
self._write_batch_data()
|
||||||
last_batch_time = time.time()
|
last_batch_time = time.time()
|
||||||
|
|
||||||
|
# Update progress bar
|
||||||
|
self._update_progress_bar()
|
||||||
|
|
||||||
# Real-time delay if needed
|
# Real-time delay if needed
|
||||||
if self.config.real_time:
|
if self.config.real_time:
|
||||||
time.sleep(0.016) # ~60 FPS
|
time.sleep(0.016) # ~60 FPS
|
||||||
@ -206,6 +337,7 @@ class HeadlessSimulationEngine:
|
|||||||
}
|
}
|
||||||
formatted_data = formatter.format(combined_data)
|
formatted_data = formatter.format(combined_data)
|
||||||
self.file_writer.write(formatted_data, filename)
|
self.file_writer.write(formatted_data, filename)
|
||||||
|
self.files_written += 1
|
||||||
|
|
||||||
# Clear written data
|
# Clear written data
|
||||||
data_list.clear()
|
data_list.clear()
|
||||||
@ -214,6 +346,10 @@ class HeadlessSimulationEngine:
|
|||||||
|
|
||||||
def _finalize(self):
|
def _finalize(self):
|
||||||
"""Finalize simulation and write remaining data."""
|
"""Finalize simulation and write remaining data."""
|
||||||
|
|
||||||
|
# Close progress bar
|
||||||
|
self._close_progress_bar()
|
||||||
|
|
||||||
print("Finalizing simulation...")
|
print("Finalizing simulation...")
|
||||||
|
|
||||||
# Write any remaining data
|
# Write any remaining data
|
||||||
@ -224,12 +360,14 @@ class HeadlessSimulationEngine:
|
|||||||
if 'json' in self.formatters:
|
if 'json' in self.formatters:
|
||||||
summary_data = self.formatters['json'].format(summary)
|
summary_data = self.formatters['json'].format(summary)
|
||||||
self.file_writer.write(summary_data, "simulation_summary.json")
|
self.file_writer.write(summary_data, "simulation_summary.json")
|
||||||
|
self.files_written += 1
|
||||||
|
|
||||||
# Stop simulation
|
# Stop simulation
|
||||||
self.simulation_core.stop()
|
self.simulation_core.stop()
|
||||||
self.file_writer.close()
|
self.file_writer.close()
|
||||||
|
|
||||||
print("Simulation completed")
|
print("Simulation completed")
|
||||||
|
print(f"Total files written: {self.files_written}")
|
||||||
|
|
||||||
def _get_summary(self) -> Dict[str, Any]:
|
def _get_summary(self) -> Dict[str, Any]:
|
||||||
"""Get simulation summary."""
|
"""Get simulation summary."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user