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.csv_formatter import CSVFormatter
|
||||
from output.writers.file_writer import FileWriter
|
||||
try:
|
||||
from tqdm import tqdm
|
||||
TQDM_AVAILABLE = True
|
||||
except ImportError:
|
||||
TQDM_AVAILABLE = False
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -56,6 +61,12 @@ class HeadlessSimulationEngine:
|
||||
'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
|
||||
signal.signal(signal.SIGINT, self._signal_handler)
|
||||
signal.signal(signal.SIGTERM, self._signal_handler)
|
||||
@ -88,6 +99,120 @@ class HeadlessSimulationEngine:
|
||||
|
||||
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]:
|
||||
"""Run the headless simulation."""
|
||||
# Determine if we should run at max speed
|
||||
@ -107,6 +232,9 @@ class HeadlessSimulationEngine:
|
||||
self.start_time = time.time()
|
||||
self.simulation_core.start()
|
||||
|
||||
# Initialize progress bar
|
||||
self._init_progress_bar()
|
||||
|
||||
# Enable sprint mode for maximum speed if not real-time mode
|
||||
if max_speed_mode:
|
||||
self.simulation_core.timing.set_sprint_mode(True)
|
||||
@ -137,6 +265,9 @@ class HeadlessSimulationEngine:
|
||||
self._write_batch_data()
|
||||
last_batch_time = time.time()
|
||||
|
||||
# Update progress bar
|
||||
self._update_progress_bar()
|
||||
|
||||
# Real-time delay if needed
|
||||
if self.config.real_time:
|
||||
time.sleep(0.016) # ~60 FPS
|
||||
@ -206,6 +337,7 @@ class HeadlessSimulationEngine:
|
||||
}
|
||||
formatted_data = formatter.format(combined_data)
|
||||
self.file_writer.write(formatted_data, filename)
|
||||
self.files_written += 1
|
||||
|
||||
# Clear written data
|
||||
data_list.clear()
|
||||
@ -214,6 +346,10 @@ class HeadlessSimulationEngine:
|
||||
|
||||
def _finalize(self):
|
||||
"""Finalize simulation and write remaining data."""
|
||||
|
||||
# Close progress bar
|
||||
self._close_progress_bar()
|
||||
|
||||
print("Finalizing simulation...")
|
||||
|
||||
# Write any remaining data
|
||||
@ -224,12 +360,14 @@ class HeadlessSimulationEngine:
|
||||
if 'json' in self.formatters:
|
||||
summary_data = self.formatters['json'].format(summary)
|
||||
self.file_writer.write(summary_data, "simulation_summary.json")
|
||||
self.files_written += 1
|
||||
|
||||
# Stop simulation
|
||||
self.simulation_core.stop()
|
||||
self.file_writer.close()
|
||||
|
||||
print("Simulation completed")
|
||||
print(f"Total files written: {self.files_written}")
|
||||
|
||||
def _get_summary(self) -> Dict[str, Any]:
|
||||
"""Get simulation summary."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user