"""Example MainRoutine plugin to demonstrate the plugin architecture.""" import xml.etree.ElementTree as ET from typing import List from ..plugin_system import RoutinePlugin class MainRoutinePlugin(RoutinePlugin): """Plugin for generating the MainRoutine.""" # Plugin metadata name = "main_routine" description = "Generates the main program routine with JSR calls" version = "1.0.0" category = "core" dependencies = [] def can_generate(self) -> bool: """Check if we can generate the main routine.""" # Main routine can always be generated return True def validate_data(self) -> List[str]: """Validate required data.""" errors = [] # Check if routines element exists if self.context.routines_element is None: errors.append("Routines element is required") return errors def generate(self) -> bool: """Generate the MainRoutine with JSR calls to created routines.""" try: self.logger.info("Generating MainRoutine with JSR calls") # Resolve routine names from config mapping try: nm = self.context.config.routines.name_map except Exception: nm = {} # If a routine plan is present, honor its order for MainProgram calls: List[str] = [] try: plan = getattr(self.context.config, 'routine_plan', []) or [] ordered = [e for e in plan if getattr(e, 'enabled', True) and getattr(e, 'program', 'MainProgram') == 'MainProgram'] ordered.sort(key=lambda e: getattr(e, 'order', 100)) for entry in ordered: # Skip generating a JSR to ourselves if getattr(entry, 'plugin', '') == 'main_routine' or getattr(entry, 'name', '') == 'main_routine': continue # Map plugin/name to configured routine name plugin_key = getattr(entry, 'plugin', '') or getattr(entry, 'name', '') routine_name = nm.get(plugin_key, nm.get(getattr(entry, 'name', ''), getattr(entry, 'name', ''))) if routine_name: calls.append(routine_name) except Exception: calls = [] # Fallback to comprehensive default order if no plan provided if not calls: defaults = [ nm.get('safety_tag_map', 'R130_SAFETY_TAG_MAP'), nm.get('rack', 'R011_RACK'), nm.get('mcm', 'R010_MCM'), nm.get('dpm', 'R020_DPM'), nm.get('fiom', 'R030_FIOM'), nm.get('fioh', 'R031_FIOH'), nm.get('apf', 'R040_APF'), nm.get('extendo', 'R041_EXTENDO'), nm.get('flow_ctrl', 'R050_FLOW_CTRL'), nm.get('speed_ctrl', 'R051_SPEED_CTRL'), nm.get('d2c_chute', 'R042_D2C_CHUTE'), nm.get('pb_chute', 'R043_PB_CHUTE'), nm.get('station_jr_chute', 'R044_STATION_JR_CHUTE'), nm.get('pmm', 'R060_PMM'), nm.get('cb_monitor', 'R070_CB_MONITOR'), nm.get('station_jr_pb', 'R090_STATION_JR_PB'), nm.get('jpe', 'R100_JPE'), nm.get('fpe', 'R101_FPE'), nm.get('estop_check', 'R120_ESTOP_CHECK'), ] calls = [c for c in defaults if c] # Filter to only include JSRs for routines that actually exist in the program existing = {r.get('Name') for r in self.context.routines_element.findall('Routine')} calls = [c for c in calls if c in existing] # Create the MainRoutine routine = ET.SubElement(self.context.routines_element, "Routine") routine.set("Name", self.context.config.routines.main_routine_name if hasattr(self.context.config, 'routines') else "MainRoutine") routine.set("Type", "RLL") # Create RLL content rll_content = ET.SubElement(routine, "RLLContent") # Single rung with JSR calls in the configured order rung = ET.SubElement(rll_content, "Rung") rung.set("Number", "0") rung.set("Type", "N") text = ET.SubElement(rung, "Text") if calls: text.text = '[' + ' ,'.join(f'JSR({c},0)' for c in calls) + ' ];' else: text.text = 'NOP();' self.logger.info("Successfully generated MainRoutine") return True except Exception as e: self.logger.error(f"Failed to generate MainRoutine: {e}") return False