#!/usr/bin/env python3 """generate_all.py – build SafetyProgram L5X, MainProgram L5X, full tag CSV, and safety-tag mapping. This is the updated version using the new configuration and logging systems. Run from repository root: python generate_all.py [options] """ from __future__ import annotations from pathlib import Path import sys import argparse # If repo root is current dir, add nested "src" folder to sys.path pkg_dir = Path(__file__).resolve().parent / "src" if pkg_dir.is_dir(): sys.path.insert(0, str(pkg_dir)) # Import new configuration and logging systems from src.config import GeneratorConfig, get_config, set_config from src.logging_config import setup_logging, get_logger, FileOperationContext # Import existing generators and writers (these will be updated in later phases) from src.generators.main_program import LimitedMainProgramGenerator as MainProgramGenerator from src.generators.safety_program import LimitedSafetyProgramGenerator as SafetyProgramGenerator from src.data_loader import DataLoader from src.writers.csv_writer import create_limited_csv_with_tags from src.writers.mapping_writer import create_safety_tag_mapping def main() -> None: parser = argparse.ArgumentParser(description="Generate safety-focused PLC program") parser.add_argument( '--config', type=Path, default=Path(__file__).parent.parent / 'generator_config.json', help='Configuration file path' ) parser.add_argument( '--excel-file', type=Path, help='Override Excel file path from config' ) parser.add_argument( '--output-dir', type=Path, help='Override output directory from config' ) parser.add_argument( '--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'], default='INFO', help='Set logging level' ) parser.add_argument( '--log-file', type=Path, help='Optional log file path' ) parser.add_argument( '--desc-ip-mode', action='store_true', help='Legacy flag - DESC_IP mode is now the default' ) args = parser.parse_args() # Setup logging first setup_logging( level=args.log_level, log_file=args.log_file, use_colors=True ) logger = get_logger(__name__) try: logger.info("=== Starting PLC Generation Process ===") # Load configuration logger.debug("Loading configuration", config_file=str(args.config)) config = GeneratorConfig.from_file(args.config) # Override config with command line arguments if args.excel_file: config.files.excel_file = args.excel_file logger.debug("Overridden Excel file", file_path=str(args.excel_file)) if args.output_dir: config.files.output_dir = args.output_dir logger.debug("Overridden output directory", output_dir=str(args.output_dir)) # Set global config set_config(config) # Validate Excel file exists if not config.files.excel_file.exists(): logger.error("Excel file not found", file_path=str(config.files.excel_file)) sys.exit(1) logger.info("Configuration loaded", excel_file=str(config.files.excel_file), controller_name=config.xml.controller_name) # Show deprecation warning for desc-ip-mode flag if args.desc_ip_mode: logger.warning("--desc-ip-mode flag is deprecated. DESC_IP extraction is now the default mode.") # Safety program generation with FileOperationContext(logger, "SafetyProgram generation", config.files.safety_l5x): safety_gen = SafetyProgramGenerator(config.files.excel_file) safety_gen.write(config.files.safety_l5x) logger.info("SafetyProgram generation completed", output_file=config.files.safety_l5x) # Main program generation logger.info("Starting MainProgram generation", stage="main_generation") with FileOperationContext(logger, "MainProgram generation", config.files.main_l5x): main_gen = MainProgramGenerator(config.files.excel_file) main_gen.write(config.files.main_l5x) logger.info("MainProgram generation completed", output_file=config.files.main_l5x) # Generate CSV tags logger.progress("csv_generation", "Starting CSV tags generation") if not config.files.original_csv.exists(): logger.warning("Original CSV file not found, skipping CSV generation", file_path=str(config.files.original_csv)) else: with FileOperationContext(logger, "CSV generation", config.files.complete_csv): beacon_so_tags = create_limited_csv_with_tags( config.files.excel_file, config.files.original_csv, config.files.complete_csv ) logger.info("CSV generation completed", output_file=config.files.complete_csv, beacon_tags=len(beacon_so_tags) if beacon_so_tags else 0) # Generate Safety Tag Mapping logger.progress("mapping_generation", "Starting safety tag mapping generation") with FileOperationContext(logger, "Safety mapping generation", config.files.mapping_txt): # Load data for safety tag extraction loader = DataLoader.from_excel(config.files.excel_file) safety_tags = loader.safety_tags_from_pb logger.data_stats("safety_tags", len(safety_tags)) create_safety_tag_mapping( safety_tags, set(), # No beacon SO tags in limited mode set(), # No beacon SFT tags in limited mode config.files.mapping_txt ) logger.info("Safety tag mapping completed", output_file=config.files.mapping_txt, safety_tag_count=len(safety_tags)) logger.info("=== PLC Generation Process Completed Successfully ===") logger.info("Generated files:", safety_l5x=config.files.safety_l5x, main_l5x=config.files.main_l5x, csv_file=config.files.complete_csv, mapping_file=config.files.mapping_txt) except Exception as e: logger.error("Generation process failed", exception=str(e)) if args.log_level == 'DEBUG': logger.exception("Full traceback") sys.exit(1) if __name__ == '__main__': main()