""" Multi-Series Setup Utility This utility helps users set up the proper folder structure and configuration for multi-series dongle inference. Features: - Create recommended folder structure - Validate existing folder structure - Generate example configuration files - Provide setup guidance and troubleshooting Usage: python utils/multi_series_setup.py create-structure --path "C:/MyAssets" python utils/multi_series_setup.py validate --path "C:/MyAssets" python utils/multi_series_setup.py help """ import os import sys import argparse from typing import List, Tuple, Dict import json class MultiSeriesSetup: """Utility class for multi-series setup operations""" SUPPORTED_SERIES = ['520', '720', '630', '730', '540'] REQUIRED_FW_FILES = ['fw_scpu.bin', 'fw_ncpu.bin'] @staticmethod def create_folder_structure(base_path: str, series_list: List[str] = None) -> bool: """ Create the recommended folder structure for multi-series assets Args: base_path: Root path where assets folder should be created series_list: List of series to create folders for Returns: bool: Success status """ if series_list is None: series_list = MultiSeriesSetup.SUPPORTED_SERIES try: assets_path = os.path.join(base_path, 'Assets') firmware_path = os.path.join(assets_path, 'Firmware') models_path = os.path.join(assets_path, 'Models') # Create main directories os.makedirs(firmware_path, exist_ok=True) os.makedirs(models_path, exist_ok=True) print(f"āœ“ Created main directories at: {assets_path}") # Create series-specific directories created_series = [] for series in series_list: series_name = f'KL{series}' fw_dir = os.path.join(firmware_path, series_name) model_dir = os.path.join(models_path, series_name) os.makedirs(fw_dir, exist_ok=True) os.makedirs(model_dir, exist_ok=True) created_series.append(series_name) print(f"āœ“ Created series directories: {', '.join(created_series)}") # Create README file explaining the structure readme_content = MultiSeriesSetup._generate_readme_content() readme_path = os.path.join(assets_path, 'README.md') with open(readme_path, 'w') as f: f.write(readme_content) print(f"āœ“ Created README file: {readme_path}") # Create example configuration config_example = MultiSeriesSetup._generate_example_config(assets_path, series_list) config_path = os.path.join(assets_path, 'example_config.json') with open(config_path, 'w') as f: json.dump(config_example, f, indent=2) print(f"āœ“ Created example configuration: {config_path}") print(f"\nšŸŽ‰ Multi-series folder structure created successfully!") print(f"šŸ“ Assets folder: {assets_path}") print("\nšŸ“‹ Next steps:") print("1. Copy your firmware files to the appropriate Firmware/KLxxx/ folders") print("2. Copy your model files to the appropriate Models/KLxxx/ folders") print("3. Configure your model node to use this Assets folder") print("4. Enable multi-series mode and select desired series") return True except Exception as e: print(f"āŒ Error creating folder structure: {e}") return False @staticmethod def validate_folder_structure(assets_path: str) -> Tuple[bool, List[str]]: """ Validate an existing folder structure for multi-series configuration Args: assets_path: Path to the assets folder Returns: Tuple of (is_valid, list_of_issues) """ issues = [] # Check if assets folder exists if not os.path.exists(assets_path): issues.append(f"Assets folder does not exist: {assets_path}") return False, issues # Check for main folders firmware_path = os.path.join(assets_path, 'Firmware') models_path = os.path.join(assets_path, 'Models') if not os.path.exists(firmware_path): issues.append(f"Firmware folder missing: {firmware_path}") if not os.path.exists(models_path): issues.append(f"Models folder missing: {models_path}") if issues: return False, issues # Check series folders and contents found_series = [] for item in os.listdir(firmware_path): if item.startswith('KL') and os.path.isdir(os.path.join(firmware_path, item)): series_name = item series_fw_path = os.path.join(firmware_path, series_name) series_model_path = os.path.join(models_path, series_name) # Check if corresponding model folder exists if not os.path.exists(series_model_path): issues.append(f"Models folder missing for {series_name}: {series_model_path}") continue # Check firmware files fw_issues = [] for fw_file in MultiSeriesSetup.REQUIRED_FW_FILES: fw_file_path = os.path.join(series_fw_path, fw_file) if not os.path.exists(fw_file_path): fw_issues.append(f"{fw_file} missing") if fw_issues: issues.append(f"{series_name} firmware issues: {', '.join(fw_issues)}") # Check for model files model_files = [f for f in os.listdir(series_model_path) if f.endswith('.nef')] if not model_files: issues.append(f"{series_name} has no .nef model files in {series_model_path}") if not fw_issues and model_files: found_series.append(series_name) if not found_series: issues.append("No valid series configurations found") is_valid = len(issues) == 0 # Print validation results if is_valid: print(f"āœ… Validation passed!") print(f"šŸ“ Assets folder: {assets_path}") print(f"šŸŽÆ Valid series found: {', '.join(found_series)}") else: print(f"āŒ Validation failed with {len(issues)} issues:") for i, issue in enumerate(issues, 1): print(f" {i}. {issue}") return is_valid, issues @staticmethod def list_available_series(assets_path: str) -> Dict[str, Dict[str, any]]: """ List all available and configured series in the assets folder Args: assets_path: Path to the assets folder Returns: Dict with series information """ series_info = {} if not os.path.exists(assets_path): return series_info firmware_path = os.path.join(assets_path, 'Firmware') models_path = os.path.join(assets_path, 'Models') if not os.path.exists(firmware_path) or not os.path.exists(models_path): return series_info for item in os.listdir(firmware_path): if item.startswith('KL') and os.path.isdir(os.path.join(firmware_path, item)): series_name = item series_fw_path = os.path.join(firmware_path, series_name) series_model_path = os.path.join(models_path, series_name) # Check firmware files fw_files = {} for fw_file in MultiSeriesSetup.REQUIRED_FW_FILES: fw_file_path = os.path.join(series_fw_path, fw_file) fw_files[fw_file] = os.path.exists(fw_file_path) # Check model files model_files = [] if os.path.exists(series_model_path): model_files = [f for f in os.listdir(series_model_path) if f.endswith('.nef')] # Determine status fw_complete = all(fw_files.values()) has_models = len(model_files) > 0 if fw_complete and has_models: status = "āœ… Ready" elif fw_complete: status = "āš ļø Missing models" elif has_models: status = "āš ļø Missing firmware" else: status = "āŒ Incomplete" series_info[series_name] = { 'status': status, 'firmware_files': fw_files, 'model_files': model_files, 'firmware_path': series_fw_path, 'models_path': series_model_path } return series_info @staticmethod def _generate_readme_content() -> str: """Generate README content for the assets folder""" return ''' # Multi-Series Assets Folder Structure This folder contains firmware and models organized by dongle series for multi-series inference. ## Structure: ``` Assets/ ā”œā”€ā”€ Firmware/ │ ā”œā”€ā”€ KL520/ │ │ ā”œā”€ā”€ fw_scpu.bin │ │ └── fw_ncpu.bin │ ā”œā”€ā”€ KL720/ │ │ ā”œā”€ā”€ fw_scpu.bin │ │ └── fw_ncpu.bin │ └── [other series...] └── Models/ ā”œā”€ā”€ KL520/ │ └── [model.nef files] ā”œā”€ā”€ KL720/ │ └── [model.nef files] └── [other series...] ``` ## Usage: 1. Place firmware files (fw_scpu.bin, fw_ncpu.bin) in the appropriate series subfolder under Firmware/ 2. Place model files (.nef) in the appropriate series subfolder under Models/ 3. Configure your model node to use this Assets folder in multi-series mode 4. Select which series to enable in the model node properties ## Supported Series: - **KL520**: Entry-level performance (3 GOPS) - **KL720**: Mid-range performance (28 GOPS) - **KL630**: High performance (400 GOPS) - **KL730**: Very high performance (1600 GOPS) - **KL540**: Specialized performance (800 GOPS) ## Performance Benefits: The multi-series system automatically load balances inference across all enabled series based on their GOPS capacity for optimal performance. You can expect: - Higher overall throughput by utilizing multiple dongle types simultaneously - Automatic load balancing based on dongle capabilities - Seamless failover if one series becomes unavailable - Scalable performance as you add more dongles ## Validation: Run `python utils/multi_series_setup.py validate --path ` to validate your configuration. ## Troubleshooting: - Ensure all firmware files are exactly named `fw_scpu.bin` and `fw_ncpu.bin` - Model files must have `.nef` extension - Each series must have both firmware and at least one model file - Check file permissions and accessibility '''.strip() @staticmethod def _generate_example_config(assets_path: str, series_list: List[str]) -> Dict: """Generate example configuration for model node""" return { "model_node_properties": { "multi_series_mode": True, "assets_folder": assets_path, "enabled_series": series_list[:2], # Enable first two series by default "max_queue_size": 100, "result_buffer_size": 1000, "batch_size": 1 }, "expected_performance": { "total_gops": sum([ {"520": 3, "720": 28, "630": 400, "730": 1600, "540": 800}.get(series, 0) for series in series_list[:2] ]), "load_balancing": "automatic", "expected_fps_improvement": "2-5x compared to single series" }, "notes": [ "This is an example configuration", "Adjust enabled_series based on your available dongles", "Higher queue sizes may improve performance but use more memory", "Monitor system resources when using multiple series" ] } def main(): """Main CLI interface""" parser = argparse.ArgumentParser(description='Multi-Series Dongle Setup Utility') subparsers = parser.add_subparsers(dest='command', help='Available commands') # Create structure command create_parser = subparsers.add_parser('create-structure', help='Create folder structure') create_parser.add_argument('--path', required=True, help='Base path for assets folder') create_parser.add_argument('--series', nargs='*', default=None, help='Series to set up (default: all)') # Validate command validate_parser = subparsers.add_parser('validate', help='Validate existing structure') validate_parser.add_argument('--path', required=True, help='Path to assets folder') # List command list_parser = subparsers.add_parser('list', help='List available series') list_parser.add_argument('--path', required=True, help='Path to assets folder') # Help command help_parser = subparsers.add_parser('help', help='Show detailed help') args = parser.parse_args() if args.command == 'create-structure': series_list = args.series if args.series else None MultiSeriesSetup.create_folder_structure(args.path, series_list) elif args.command == 'validate': is_valid, issues = MultiSeriesSetup.validate_folder_structure(args.path) sys.exit(0 if is_valid else 1) elif args.command == 'list': series_info = MultiSeriesSetup.list_available_series(args.path) if not series_info: print(f"āŒ No series found in {args.path}") sys.exit(1) print(f"šŸ“ Series configuration in {args.path}:\n") for series_name, info in series_info.items(): print(f" {series_name}: {info['status']}") print(f" šŸ“ Firmware: {info['firmware_path']}") print(f" šŸ“ Models: {info['models_path']}") if info['model_files']: print(f" šŸ“„ Model files: {', '.join(info['model_files'])}") fw_issues = [fw for fw, exists in info['firmware_files'].items() if not exists] if fw_issues: print(f" āš ļø Missing firmware: {', '.join(fw_issues)}") print() elif args.command == 'help': print(""" Multi-Series Dongle Setup Help ============================= This utility helps you set up and manage multi-series dongle configurations for improved inference performance. Commands: --------- create-structure --path [--series KL520 KL720 ...] Creates the recommended folder structure for multi-series assets. Example: python utils/multi_series_setup.py create-structure --path "C:/MyAssets" python utils/multi_series_setup.py create-structure --path "C:/MyAssets" --series 520 720 validate --path Validates an existing assets folder structure. Example: python utils/multi_series_setup.py validate --path "C:/MyAssets/Assets" list --path Lists all available series and their status in an assets folder. Example: python utils/multi_series_setup.py list --path "C:/MyAssets/Assets" Setup Workflow: -------------- 1. Create folder structure: create-structure --path "C:/MyProject" 2. Copy firmware files to Assets/Firmware/KLxxx/ folders 3. Copy model files to Assets/Models/KLxxx/ folders 4. Validate configuration: validate --path "C:/MyProject/Assets" 5. Configure model node in UI to use Assets folder 6. Enable multi-series mode and select desired series Performance Benefits: ------------------- - 2-5x throughput improvement with multiple series - Automatic load balancing based on dongle GOPS - Seamless scaling as you add more dongles - Fault tolerance if some dongles become unavailable Troubleshooting: --------------- - Ensure exact firmware file names: fw_scpu.bin, fw_ncpu.bin - Model files must have .nef extension - Check file permissions and paths - Verify dongle connectivity with single-series mode first - Use validate command to check configuration For more help, see Assets/README.md after creating the structure. """) else: parser.print_help() if __name__ == '__main__': main()