#!/usr/bin/env python3 """ settings_cli.py - Command line interface for IronOS settings management """ import sys import os import argparse from typing import Tuple # Import local modules from .settings_types import DEFAULT_YAML_PATH, HEX_SUPPORT from .settings_model import Settings from .settings_util import get_base_address from .settings_parser import process_default_values # Import the config_parser module from parent directory sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) try: from config_parser import parse_config except ImportError: def parse_config(model): print( f"Warning: config_parser module not found, BSP configuration for model {model} not available" ) return {} def parse_arguments(): """Parse command line arguments""" parser = argparse.ArgumentParser(description="Edit IronOS settings") parser.add_argument( "-i", "--input", help="Input binary or hex settings file (optional)" ) parser.add_argument( "-o", "--output", help="Output binary settings file or hex file (use .hex extension for Intel HEX format)", required=True, ) parser.add_argument( "-d", "--definitions", help="Settings definitions YAML file", default=DEFAULT_YAML_PATH, ) parser.add_argument( "-m", "--model", help="Device model ID (required for Intel HEX output to set correct base address)", choices=[ "TS100", "TS80", "TS80P", "TS101", "S60", "S60P", "Pinecil", "Pinecilv2", "MHP30", ], ) parser.add_argument( "-n", "--non-interactive", help="Non-interactive mode (uses default values)", action="store_true", ) parser.add_argument( "--use-bsp-defaults", help="Use values from BSP configuration.h for non-numeric settings", action="store_true", ) parser.add_argument("--debug", help="Enable debug output", action="store_true") return parser.parse_args() def handle_input_file(args, settings) -> Tuple[Settings, int]: """Load settings from input file if provided and return base address Args: args: Command line arguments settings: Settings object Returns: The detected base address from the input file (0 if not available) """ input_base_address = 0 bsp_config = None # If model is provided, load the BSP configuration if args.model: try: print(f"Loading BSP configuration for model {args.model}") bsp_config = parse_config(args.model) print(f"Loaded {len(bsp_config)} configuration values from BSP") # Add common default values that might be missing from the BSP config if "QC_VOLTAGE_MAX" not in bsp_config: bsp_config["QC_VOLTAGE_MAX"] = 90 except Exception as e: print(f"Error loading BSP configuration: {e}") print("Will use YAML defaults instead") bsp_config = None # If input file is provided, load settings from it if args.input: file_type = "hex" if args.input.lower().endswith(".hex") else "binary" print(f"Loading settings from {file_type} file {args.input}") success, input_base_address = settings.load_from_binary(args.input) if not success: print("Using default values from settings definitions") process_default_values( settings, bsp_config, args.debug if hasattr(args, "debug") else False ) else: print("No input file provided, using default values from settings definitions") process_default_values( settings, bsp_config, args.debug if hasattr(args, "debug") else False ) return (settings, input_base_address) def run_editing_settings_file_cli(): """Main function to run the CLI""" args = parse_arguments() # Check if settings definitions file exists if not os.path.isfile(args.definitions): print(f"Error: Settings definition file '{args.definitions}' does not exist.") sys.exit(1) # Initialize settings settings = Settings() # Load settings definitions from YAML print(f"Loading settings definitions from {args.definitions}") try: settings.load_from_yaml(args.definitions) except Exception as e: print(f"Error loading settings definitions: {e}") sys.exit(1) # Initialize base_address base_address = 0 input_base_address = 0 # Handle input file and process defaults (settings, input_base_address) = handle_input_file(args, settings) # Determine the base address to use for output # Priority: 1. Model-specified base address, 2. Input hex file base address, 3. Default (0) if args.model: base_address = get_base_address(args.model) print(f"Using base address 0x{base_address:08X} for model {args.model}") elif input_base_address > 0: base_address = input_base_address print(f"Using base address 0x{base_address:08X} from input file") # If we have a model, try to get SETTINGS_START_PAGE from BSP config if args.model and not args.input and base_address == 0: try: bsp_config = parse_config(args.model) if "SETTINGS_START_PAGE" in bsp_config: # Use the settings page address from BSP if available base_address = bsp_config["SETTINGS_START_PAGE"] print(f"Using SETTINGS_START_PAGE from BSP: 0x{base_address:08X}") except Exception as e: print(f"Failed to get flash address from BSP: {e}") # Edit settings if not in non-interactive mode if not args.non_interactive: settings.edit_all_settings() else: print("Running in non-interactive mode, using loaded/default values") versionMarker = 0x55AA if args.model == "Pinecilv2": versionMarker = 0x55AB # Special version marker for Pinecil v2 # Check if output is hex and we need intelhex module if args.output.lower().endswith(".hex"): if not HEX_SUPPORT: print( "Error: Output file has .hex extension but intelhex module is not installed." ) print("Install it with 'pip install intelhex' to generate Intel HEX files.") print( "Please change the output file extension to .bin or install the IntelHex module." ) sys.exit(1) elif not args.model and input_base_address == 0: print("Warning: No base address available for HEX output.") print( "Please specify a model with the --model option or use an input hex file with a valid base address." ) sys.exit(1) # Save settings to binary or hex file print(f"\nSaving settings to {args.output}") if not settings.save_to_binary(args.output, base_address,versionMarker): print("Failed to save settings") sys.exit(1) print("Settings saved successfully")