PLC_Generation/Routines Generator/generate_safety_only.py
2025-08-05 14:38:54 +04:00

136 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""generate_safety_only.py build limited SafetyProgram and MainProgram L5X with essential safety routines only.
This mode only generates:
- Safety routines: inputs, outputs, estops, zones, resets
- Main routines: safety_tag_map, estop_check (safety checkout)
- Controller tags embedded in MainProgram L5X
- Safety tag mapping
Extracts safety data (RST, STO, EPC) from DESC_IP sheet and uses zones_config.py for zone configuration.
Run from repository root:
python generate_safety_only.py [--desc-ip-mode]
The outputs are written to *_Limited.* filenames in the current directory.
"""
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))
from src.generators import LimitedSafetyProgramGenerator, LimitedMainProgramGenerator
from src.data_loader import DataLoader
from src.writers.mapping_writer import create_safety_tag_mapping
EXCEL_FILE = Path('MCM01_UL1_UL3.xlsx')
DESC_IP_FILE = Path('DESC_IP_MERGED.xlsx')
SAFETY_L5X = 'SafetyProgram_Limited.L5X'
MAIN_L5X = 'MainProgram_Limited.L5X'
MAPPING_TXT = 'SafetyTagMapping_Limited.txt'
def main() -> None:
parser = argparse.ArgumentParser(description="Generate safety-only routines")
parser.add_argument('--desc-ip-mode', action='store_true',
help='Extract safety devices from DESC_IP data instead of separate sheets')
parser.add_argument('--ignore-estop1ok', action='store_true',
help='Ignore ESTOP1OK tags in inputs, estops, and zones routines')
parser.add_argument('--project-name', type=str,
help='Project name to detect MCM type for zones configuration')
args = parser.parse_args()
print("Safety-Only Mode")
print("================")
# Determine which Excel file to use
excel_file = DESC_IP_FILE if args.desc_ip_mode and DESC_IP_FILE.exists() else EXCEL_FILE
if args.desc_ip_mode:
print("Using DESC_IP data extraction mode")
print(f"Using Excel file: {excel_file}")
print("Extracting safety devices from DESC_IP sheet using safety rules\n")
else:
print("Using separate safety sheets mode")
print(f"Using Excel file: {excel_file}")
print("Generating essential safety routines using DESC_IP data extraction and zones_config.py\n")
if args.ignore_estop1ok:
print("INFO: Ignoring ESTOP1OK tags in safety routines generation\n")
# Load zones configuration based on project type
zones_dict = None
try:
# Add parent directory to sys.path to find zones_config.py
sys.path.insert(0, str(Path(__file__).parent.parent))
from zones_config import ZONES_CONFIGS, DEFAULT_ZONES
# Detect project type from project name argument
import re
if args.project_name:
mcm_match = re.search(r"(MCM\d+)", args.project_name, re.IGNORECASE)
if mcm_match:
mcm_type = mcm_match.group(1).upper()
zones_dict = ZONES_CONFIGS.get(mcm_type, DEFAULT_ZONES)
print(f"Detected {mcm_type} project from '{args.project_name}', loaded {len(zones_dict)} zones from zones_config.py")
else:
zones_dict = DEFAULT_ZONES
print(f"No MCM type detected in '{args.project_name}', loaded {len(zones_dict)} default zones from zones_config.py")
else:
zones_dict = DEFAULT_ZONES
print(f"No project name provided, loaded {len(zones_dict)} default zones from zones_config.py")
except ImportError as e:
print(f"Warning: Could not load zones_config.py: {e}")
print("Proceeding without zones configuration...")
# Limited Safety program (inputs, outputs, estops, zones, resets)
print("1. Generating SafetyProgram...")
LimitedSafetyProgramGenerator(excel_file, zones_dict=zones_dict, ignore_estop1ok=args.ignore_estop1ok).write(SAFETY_L5X)
print(f'[SUCCESS] Wrote {SAFETY_L5X}')
print(' - Contains: inputs, outputs, estops, zones, resets routines')
# Limited Main program (safety_tag_map, estop_check) with embedded controller tags
print("\n2. Generating MainProgram with embedded controller tags...")
LimitedMainProgramGenerator(excel_file, zones_dict=zones_dict).write(MAIN_L5X)
print(f'[SUCCESS] Wrote {MAIN_L5X}')
print(' - Contains: safety_tag_map, estop_check routines')
print(' - Contains: Controller-level tags embedded in L5X')
# Safety tag mapping
print("\n3. Generating safety tag mapping...")
loader = DataLoader(excel_file, zones_dict=zones_dict)
# Collect safety tags from appropriate source
safety_tags = loader.safety_tags_from_pb
# For limited mode, we don't have beacon tags, so pass empty sets
create_safety_tag_mapping(safety_tags, set(), set(), MAPPING_TXT)
print(f'[SUCCESS] Wrote {MAPPING_TXT}')
print(f' - Contains: {len(safety_tags)} safety tags ({len(safety_tags) - 1} push buttons + MCM)')
print(f"\nSafety-only generation complete!")
if args.desc_ip_mode:
print(f"Data sources: DESC_IP sheet with safety device extraction")
print(f" - RST devices: DESCA contains 'START' but NOT 'LIGHT'")
print(f" - STO devices: TAGNAME contains 'VFD' or DESCA contains 'STO'")
print(f" - EPC devices: DESCA contains 'EPC' or 'ESTOP'")
else:
print(f"Data sources: DESC_IP extraction (RST, STO, EPC) + zones_config.py")
print(f"Main routines: safety_tag_map, estop_check")
print(f"Output files:")
print(f" - {SAFETY_L5X}")
print(f" - {MAIN_L5X} (with embedded controller tags)")
print(f" - {MAPPING_TXT}")
if __name__ == '__main__':
main()