132 lines
6.0 KiB
Python
132 lines
6.0 KiB
Python
"""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:
|
|
# Check if this is SafetyProgram or MainProgram based on existing routines
|
|
existing = {r.get('Name') for r in self.context.routines_element.findall('Routine')}
|
|
|
|
if any(name.startswith('R010_INPUTS') or name.startswith('R011_OUTPUTS') for name in existing):
|
|
# This is SafetyProgram - use safety routine order
|
|
defaults = [
|
|
nm.get('inputs', 'R010_INPUTS'),
|
|
nm.get('outputs', 'R011_OUTPUTS'),
|
|
nm.get('resets', 'R012_RESETS'),
|
|
nm.get('estops', 'R020_ESTOPS'),
|
|
nm.get('zones', 'R030_ZONES'),
|
|
nm.get('safety_tag_mapping', 'R999_SAFETY_TAG_MAPPING'),
|
|
]
|
|
else:
|
|
# This is MainProgram - use main program routine order
|
|
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'),
|
|
nm.get('taching_belts', 'ZZZ_Taching_Belts'),
|
|
nm.get('belt_tracking', 'ZZZ_BeltTracking'),
|
|
]
|
|
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 |