SOmewhat working
This commit is contained in:
parent
e06a57dbc2
commit
d8516dd302
Binary file not shown.
Binary file not shown.
@ -76,15 +76,20 @@ class ControllerBuilder:
|
|||||||
attach extra modules before the project is saved.
|
attach extra modules before the project is saved.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, controller_name: str, skip_chassis_modules: bool = False):
|
def __init__(self, controller_name: str, skip_chassis_modules: bool = False, boilerplate_dir: str = "boilerplate"):
|
||||||
self.controller_name = controller_name
|
self.controller_name = controller_name
|
||||||
self.skip_chassis_modules = skip_chassis_modules
|
self.skip_chassis_modules = skip_chassis_modules
|
||||||
|
self.boilerplate_dir = boilerplate_dir
|
||||||
# Raw base XML snippets to preserve CDATA on save
|
# Raw base XML snippets to preserve CDATA on save
|
||||||
self._raw_base_aoi_xml: Optional[str] = None
|
self._raw_base_aoi_xml: Optional[str] = None
|
||||||
self._raw_base_dtypes_xml: Optional[str] = None
|
self._raw_base_dtypes_xml: Optional[str] = None
|
||||||
|
|
||||||
# 1. Build base controller from boilerplate
|
# 1. Build base controller from boilerplate
|
||||||
controller_cfg = create_l83es_controller(controller_name)
|
controller_cfg = create_l83es_controller(controller_name)
|
||||||
|
# Update boilerplate path to use project-specific directory
|
||||||
|
if hasattr(controller_cfg, 'boilerplate_path'):
|
||||||
|
filename = os.path.basename(controller_cfg.boilerplate_path)
|
||||||
|
controller_cfg.boilerplate_path = os.path.join(self.boilerplate_dir, filename)
|
||||||
gen = L83ESControllerGenerator(controller_cfg)
|
gen = L83ESControllerGenerator(controller_cfg)
|
||||||
gen.load_boilerplate()
|
gen.load_boilerplate()
|
||||||
gen.apply_updates()
|
gen.apply_updates()
|
||||||
@ -112,6 +117,17 @@ class ControllerBuilder:
|
|||||||
raise ValueError("<Modules> section missing – builder initialisation failed")
|
raise ValueError("<Modules> section missing – builder initialisation failed")
|
||||||
return modules
|
return modules
|
||||||
|
|
||||||
|
def _set_generator_boilerplate_dir(self, generator):
|
||||||
|
"""Set the boilerplate directory for a generator if it supports it."""
|
||||||
|
if hasattr(generator, 'boilerplate_path') and hasattr(generator, 'boilerplate_filename'):
|
||||||
|
# Update the boilerplate path to use the project-specific directory
|
||||||
|
generator.boilerplate_path = os.path.join(self.boilerplate_dir, generator.boilerplate_filename)
|
||||||
|
elif hasattr(generator, 'config') and hasattr(generator.config, 'boilerplate_path'):
|
||||||
|
# For generators that store boilerplate_path in config
|
||||||
|
filename = os.path.basename(generator.config.boilerplate_path)
|
||||||
|
generator.config.boilerplate_path = os.path.join(self.boilerplate_dir, filename)
|
||||||
|
return generator
|
||||||
|
|
||||||
def finalise_and_save(self, filename: str):
|
def finalise_and_save(self, filename: str):
|
||||||
"""Complete remaining sections and write the finished L5X file."""
|
"""Complete remaining sections and write the finished L5X file."""
|
||||||
# Add logical program/task scaffolding only once at the end so that any
|
# Add logical program/task scaffolding only once at the end so that any
|
||||||
@ -451,7 +467,7 @@ class ControllerBuilder:
|
|||||||
|
|
||||||
def _add_en4tr_module(self, modules_section: ET.Element):
|
def _add_en4tr_module(self, modules_section: ET.Element):
|
||||||
cfg = create_en4tr_module("SLOT2_EN4TR", self.controller_name)
|
cfg = create_en4tr_module("SLOT2_EN4TR", self.controller_name)
|
||||||
gen = EN4TRModuleGenerator(cfg)
|
gen = self._set_generator_boilerplate_dir(EN4TRModuleGenerator(cfg))
|
||||||
gen.load_boilerplate()
|
gen.load_boilerplate()
|
||||||
gen.apply_updates()
|
gen.apply_updates()
|
||||||
|
|
||||||
@ -466,7 +482,7 @@ class ControllerBuilder:
|
|||||||
|
|
||||||
def _add_ib16_module(self, modules_section: ET.Element):
|
def _add_ib16_module(self, modules_section: ET.Element):
|
||||||
cfg = create_ib16_module("SLOT5_IB16", "5", "Local", "1", None)
|
cfg = create_ib16_module("SLOT5_IB16", "5", "Local", "1", None)
|
||||||
gen = IB16ModuleGenerator(cfg)
|
gen = self._set_generator_boilerplate_dir(IB16ModuleGenerator(cfg))
|
||||||
gen.load_boilerplate()
|
gen.load_boilerplate()
|
||||||
gen.apply_updates()
|
gen.apply_updates()
|
||||||
|
|
||||||
@ -481,7 +497,7 @@ class ControllerBuilder:
|
|||||||
|
|
||||||
def _add_ob16e_module(self, modules_section: ET.Element):
|
def _add_ob16e_module(self, modules_section: ET.Element):
|
||||||
cfg = create_ob16e_module("SLOT6_OB16E", "6", "Local", "1", None)
|
cfg = create_ob16e_module("SLOT6_OB16E", "6", "Local", "1", None)
|
||||||
gen = OB16EModuleGenerator(cfg)
|
gen = self._set_generator_boilerplate_dir(OB16EModuleGenerator(cfg))
|
||||||
gen.load_boilerplate()
|
gen.load_boilerplate()
|
||||||
gen.apply_updates()
|
gen.apply_updates()
|
||||||
|
|
||||||
@ -504,7 +520,7 @@ class ControllerBuilder:
|
|||||||
"16#0000_4c33_031d_8f1b",
|
"16#0000_4c33_031d_8f1b",
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
gen = IB16SModuleGenerator(cfg)
|
gen = self._set_generator_boilerplate_dir(IB16SModuleGenerator(cfg))
|
||||||
gen.load_boilerplate()
|
gen.load_boilerplate()
|
||||||
gen.apply_updates()
|
gen.apply_updates()
|
||||||
|
|
||||||
|
|||||||
@ -38,14 +38,18 @@ from models.sio_boilerplate_model import create_sio_module, SIOModuleGenerator
|
|||||||
class EnhancedMCMGenerator:
|
class EnhancedMCMGenerator:
|
||||||
"""Enhanced MCM generator that processes Excel data and generates complete L5X projects."""
|
"""Enhanced MCM generator that processes Excel data and generates complete L5X projects."""
|
||||||
|
|
||||||
def __init__(self, project_name: str, excel_file: str = "MCM04_Data.xlsx", zones_dict=None):
|
def __init__(self, project_name: str, excel_file: str = "MCM04_Data.xlsx", zones_dict=None, boilerplate_dir: str = "boilerplate"):
|
||||||
self.project_name = project_name
|
self.project_name = project_name
|
||||||
self.controller_name = project_name
|
self.controller_name = project_name
|
||||||
self.excel_file = excel_file
|
self.excel_file = excel_file
|
||||||
self.zones_dict = zones_dict
|
self.zones_dict = zones_dict
|
||||||
|
self.boilerplate_dir = boilerplate_dir
|
||||||
self.generated_dir = "generated_projects"
|
self.generated_dir = "generated_projects"
|
||||||
os.makedirs(self.generated_dir, exist_ok=True)
|
os.makedirs(self.generated_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Set global boilerplate directory for all models to use
|
||||||
|
os.environ['MCM_BOILERPLATE_DIR'] = boilerplate_dir
|
||||||
|
|
||||||
# Initialize data processor
|
# Initialize data processor
|
||||||
self.data_processor = ExcelDataProcessor(excel_file)
|
self.data_processor = ExcelDataProcessor(excel_file)
|
||||||
|
|
||||||
@ -67,6 +71,53 @@ class EnhancedMCMGenerator:
|
|||||||
self.ib16s_modules = []
|
self.ib16s_modules = []
|
||||||
self.ob16e_modules = []
|
self.ob16e_modules = []
|
||||||
|
|
||||||
|
def _set_generator_boilerplate_dir(self, generator):
|
||||||
|
"""Set the boilerplate directory for a generator if it supports it."""
|
||||||
|
if hasattr(generator, 'boilerplate_path') and hasattr(generator, 'boilerplate_filename'):
|
||||||
|
# Update the boilerplate path to use the project-specific directory
|
||||||
|
old_path = generator.boilerplate_path
|
||||||
|
generator.boilerplate_path = os.path.join(self.boilerplate_dir, generator.boilerplate_filename)
|
||||||
|
# Force reset and reload with the new path
|
||||||
|
if hasattr(generator, 'tree'):
|
||||||
|
generator.tree = None
|
||||||
|
if hasattr(generator, 'root'):
|
||||||
|
generator.root = None
|
||||||
|
try:
|
||||||
|
generator.load_boilerplate()
|
||||||
|
print(f" Successfully loaded boilerplate: {generator.boilerplate_path}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
# If project-specific boilerplate doesn't exist, fall back to default
|
||||||
|
print(f" Warning: Project-specific boilerplate not found, using default for {generator.boilerplate_filename}")
|
||||||
|
generator.boilerplate_path = old_path
|
||||||
|
if hasattr(generator, 'tree'):
|
||||||
|
generator.tree = None
|
||||||
|
if hasattr(generator, 'root'):
|
||||||
|
generator.root = None
|
||||||
|
generator.load_boilerplate()
|
||||||
|
elif hasattr(generator, 'config') and hasattr(generator.config, 'boilerplate_path'):
|
||||||
|
# For generators that store boilerplate_path in config
|
||||||
|
filename = os.path.basename(generator.config.boilerplate_path)
|
||||||
|
old_path = generator.config.boilerplate_path
|
||||||
|
generator.config.boilerplate_path = os.path.join(self.boilerplate_dir, filename)
|
||||||
|
# Force reset and reload with the new path
|
||||||
|
if hasattr(generator, 'tree'):
|
||||||
|
generator.tree = None
|
||||||
|
if hasattr(generator, 'root'):
|
||||||
|
generator.root = None
|
||||||
|
try:
|
||||||
|
generator.load_boilerplate()
|
||||||
|
print(f" Successfully loaded boilerplate: {generator.config.boilerplate_path}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
# If project-specific boilerplate doesn't exist, fall back to default
|
||||||
|
print(f" Warning: Project-specific boilerplate not found, using default for {filename}")
|
||||||
|
generator.config.boilerplate_path = old_path
|
||||||
|
if hasattr(generator, 'tree'):
|
||||||
|
generator.tree = None
|
||||||
|
if hasattr(generator, 'root'):
|
||||||
|
generator.root = None
|
||||||
|
generator.load_boilerplate()
|
||||||
|
return generator
|
||||||
|
|
||||||
def _optimize_for_large_projects(self):
|
def _optimize_for_large_projects(self):
|
||||||
"""Apply optimizations for large projects to reduce SDK compilation burden."""
|
"""Apply optimizations for large projects to reduce SDK compilation burden."""
|
||||||
total_modules = sum([
|
total_modules = sum([
|
||||||
@ -383,7 +434,7 @@ class EnhancedMCMGenerator:
|
|||||||
from controller_builder import ControllerBuilder
|
from controller_builder import ControllerBuilder
|
||||||
|
|
||||||
# 1. Initialise builder (creates base controller + fixed chassis modules)
|
# 1. Initialise builder (creates base controller + fixed chassis modules)
|
||||||
builder = ControllerBuilder(self.controller_name, skip_chassis_modules=True)
|
builder = ControllerBuilder(self.controller_name, skip_chassis_modules=True, boilerplate_dir=self.boilerplate_dir)
|
||||||
|
|
||||||
# 2. Append all Excel-derived modules into the builder's <Modules> section
|
# 2. Append all Excel-derived modules into the builder's <Modules> section
|
||||||
modules_section = builder.get_modules_section()
|
modules_section = builder.get_modules_section()
|
||||||
@ -680,7 +731,7 @@ class EnhancedMCMGenerator:
|
|||||||
from controller_builder import ControllerBuilder
|
from controller_builder import ControllerBuilder
|
||||||
|
|
||||||
# Create builder
|
# Create builder
|
||||||
builder = ControllerBuilder(project_name, skip_chassis_modules=True)
|
builder = ControllerBuilder(project_name, skip_chassis_modules=True, boilerplate_dir=self.boilerplate_dir)
|
||||||
modules_section = builder.get_modules_section()
|
modules_section = builder.get_modules_section()
|
||||||
|
|
||||||
# Add modules from the specific groups
|
# Add modules from the specific groups
|
||||||
@ -707,32 +758,32 @@ class EnhancedMCMGenerator:
|
|||||||
"""Add modules from specific groups to the modules section."""
|
"""Add modules from specific groups to the modules section."""
|
||||||
|
|
||||||
factory_map = {
|
factory_map = {
|
||||||
"iolm_modules": lambda entry: M12DRModuleGenerator.from_excel(
|
"iolm_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
M12DRModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"zmx_modules": lambda entry: ZMXModuleGenerator.from_excel(
|
"zmx_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
ZMXModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"extendo_modules": lambda entry: ExtendoModuleGenerator.from_excel(
|
"extendo_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
ExtendoModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"fioh_modules": lambda entry: TurckHubModuleGenerator.from_excel(
|
"fioh_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
TurckHubModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"apf_modules": lambda entry: APFModuleGenerator.from_excel(
|
"apf_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]], hp=entry["hp"]
|
APFModuleGenerator.from_excel(self.data_processor.modules[entry["name"]], hp=entry["hp"])
|
||||||
),
|
),
|
||||||
"vfd_modules": lambda entry: VFDModuleGenerator.from_excel(
|
"vfd_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]], hp=entry["hp"]
|
VFDModuleGenerator.from_excel(self.data_processor.modules[entry["name"]], hp=entry["hp"])
|
||||||
),
|
),
|
||||||
"dpm_modules": lambda entry: DPMModuleGenerator.from_excel(
|
"dpm_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
DPMModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"pmm_modules": lambda entry: PMMModuleGenerator.from_excel(
|
"pmm_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
PMMModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"sio_modules": lambda entry: SIOModuleGenerator.from_excel(
|
"sio_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
SIOModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"beacon_modules": lambda entry: TL70BeaconGenerator.from_mapping(entry),
|
"beacon_modules": lambda entry: TL70BeaconGenerator.from_mapping(entry),
|
||||||
"lpe_modules": lambda entry: LPEBoilerplateGenerator.from_mapping(entry),
|
"lpe_modules": lambda entry: LPEBoilerplateGenerator.from_mapping(entry),
|
||||||
@ -773,6 +824,11 @@ class EnhancedMCMGenerator:
|
|||||||
for entry in entries:
|
for entry in entries:
|
||||||
try:
|
try:
|
||||||
gen = factory(entry)
|
gen = factory(entry)
|
||||||
|
# Ensure boilerplate directory is set for all generators
|
||||||
|
gen = self._set_generator_boilerplate_dir(gen)
|
||||||
|
# Re-apply updates after boilerplate directory change
|
||||||
|
if hasattr(gen, 'apply_updates'):
|
||||||
|
gen.apply_updates()
|
||||||
module_elem = gen.root.find(
|
module_elem = gen.root.find(
|
||||||
f".//Module[@Name='{entry['name']}']"
|
f".//Module[@Name='{entry['name']}']"
|
||||||
)
|
)
|
||||||
@ -996,7 +1052,7 @@ class EnhancedMCMGenerator:
|
|||||||
def _add_en4tr_module(self, modules_section):
|
def _add_en4tr_module(self, modules_section):
|
||||||
"""Add EN4TR module to the Modules section."""
|
"""Add EN4TR module to the Modules section."""
|
||||||
config = create_en4tr_module("SLOT2_EN4TR", self.controller_name)
|
config = create_en4tr_module("SLOT2_EN4TR", self.controller_name)
|
||||||
generator = EN4TRModuleGenerator(config)
|
generator = self._set_generator_boilerplate_dir(EN4TRModuleGenerator(config))
|
||||||
generator.load_boilerplate()
|
generator.load_boilerplate()
|
||||||
generator.apply_updates()
|
generator.apply_updates()
|
||||||
|
|
||||||
@ -1015,32 +1071,32 @@ class EnhancedMCMGenerator:
|
|||||||
# specialised helpers due to their CDATA or dict-based quirks).
|
# specialised helpers due to their CDATA or dict-based quirks).
|
||||||
|
|
||||||
factory_map = {
|
factory_map = {
|
||||||
"iolm_modules": lambda entry: M12DRModuleGenerator.from_excel(
|
"iolm_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
M12DRModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"zmx_modules": lambda entry: ZMXModuleGenerator.from_excel(
|
"zmx_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
ZMXModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"extendo_modules": lambda entry: ExtendoModuleGenerator.from_excel(
|
"extendo_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
ExtendoModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"fioh_modules": lambda entry: TurckHubModuleGenerator.from_excel(
|
"fioh_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
TurckHubModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"apf_modules": lambda entry: APFModuleGenerator.from_excel(
|
"apf_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]], hp=entry["hp"]
|
APFModuleGenerator.from_excel(self.data_processor.modules[entry["name"]], hp=entry["hp"])
|
||||||
),
|
),
|
||||||
"vfd_modules": lambda entry: VFDModuleGenerator.from_excel(
|
"vfd_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]], hp=entry["hp"]
|
VFDModuleGenerator.from_excel(self.data_processor.modules[entry["name"]], hp=entry["hp"])
|
||||||
),
|
),
|
||||||
"dpm_modules": lambda entry: DPMModuleGenerator.from_excel(
|
"dpm_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
DPMModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"pmm_modules": lambda entry: PMMModuleGenerator.from_excel(
|
"pmm_modules": lambda entry: PMMModuleGenerator.from_excel(
|
||||||
self.data_processor.modules[entry["name"]]
|
self.data_processor.modules[entry["name"]]
|
||||||
),
|
),
|
||||||
"sio_modules": lambda entry: SIOModuleGenerator.from_excel(
|
"sio_modules": lambda entry: self._set_generator_boilerplate_dir(
|
||||||
self.data_processor.modules[entry["name"]]
|
SIOModuleGenerator.from_excel(self.data_processor.modules[entry["name"]])
|
||||||
),
|
),
|
||||||
"beacon_modules": lambda entry: TL70BeaconGenerator.from_mapping(entry),
|
"beacon_modules": lambda entry: TL70BeaconGenerator.from_mapping(entry),
|
||||||
"lpe_modules": lambda entry: LPEBoilerplateGenerator.from_mapping(entry),
|
"lpe_modules": lambda entry: LPEBoilerplateGenerator.from_mapping(entry),
|
||||||
@ -1082,9 +1138,11 @@ class EnhancedMCMGenerator:
|
|||||||
for entry in entries:
|
for entry in entries:
|
||||||
try:
|
try:
|
||||||
gen = factory(entry)
|
gen = factory(entry)
|
||||||
# Apply optimizations for large projects
|
# Ensure boilerplate directory is set for all generators
|
||||||
if self._optimize_for_large_projects():
|
gen = self._set_generator_boilerplate_dir(gen)
|
||||||
gen = self._apply_module_optimizations(gen)
|
# Re-apply updates after boilerplate directory change
|
||||||
|
if hasattr(gen, 'apply_updates'):
|
||||||
|
gen.apply_updates()
|
||||||
# Apply optimizations for large projects
|
# Apply optimizations for large projects
|
||||||
if self._optimize_for_large_projects():
|
if self._optimize_for_large_projects():
|
||||||
gen = self._apply_module_optimizations(gen)
|
gen = self._apply_module_optimizations(gen)
|
||||||
@ -1170,21 +1228,25 @@ def main():
|
|||||||
print(f"ERROR: Invalid zones JSON: {e}")
|
print(f"ERROR: Invalid zones JSON: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Allow specifying Excel file and project name via command line
|
# Allow specifying Excel file, project name, and boilerplate directory via command line
|
||||||
# Usage: python enhanced_mcm_generator.py <excel_file> <project_name> [--split] [--zones <json>]
|
# Usage: python enhanced_mcm_generator.py <excel_file> <project_name> [boilerplate_dir] [--split] [--zones <json>]
|
||||||
|
boilerplate_dir = "boilerplate" # Default
|
||||||
if len(sys.argv) > 1 and not sys.argv[1].startswith("--"):
|
if len(sys.argv) > 1 and not sys.argv[1].startswith("--"):
|
||||||
excel_file = sys.argv[1]
|
excel_file = sys.argv[1]
|
||||||
if len(sys.argv) > 2 and not sys.argv[2].startswith("--"):
|
if len(sys.argv) > 2 and not sys.argv[2].startswith("--"):
|
||||||
project_name = sys.argv[2]
|
project_name = sys.argv[2]
|
||||||
|
if len(sys.argv) > 3 and not sys.argv[3].startswith("--"):
|
||||||
|
boilerplate_dir = sys.argv[3]
|
||||||
|
|
||||||
print("Enhanced MCM Generator")
|
print("Enhanced MCM Generator")
|
||||||
print(f"- Project: {project_name}")
|
print(f"- Project: {project_name}")
|
||||||
print(f"- Excel: {excel_file}")
|
print(f"- Excel: {excel_file}")
|
||||||
|
print(f"- Boilerplate: {boilerplate_dir}")
|
||||||
print(f"- Mode: {'Split' if split_mode else 'Single file'}")
|
print(f"- Mode: {'Split' if split_mode else 'Single file'}")
|
||||||
print("-" * 50)
|
print("-" * 50)
|
||||||
|
|
||||||
# Create generator with zones
|
# Create generator with zones and boilerplate directory
|
||||||
generator = EnhancedMCMGenerator(project_name, excel_file, zones_dict)
|
generator = EnhancedMCMGenerator(project_name, excel_file, zones_dict, boilerplate_dir)
|
||||||
|
|
||||||
# Load and process Excel data
|
# Load and process Excel data
|
||||||
if generator.load_and_process_data():
|
if generator.load_and_process_data():
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -60,7 +60,9 @@ class APFModuleGenerator:
|
|||||||
raise ValueError(f"Unsupported HP value: {self.config.hp}. Supported values: 1, 2, 3, 5, 7.5 (or 7_5), 10")
|
raise ValueError(f"Unsupported HP value: {self.config.hp}. Supported values: 1, 2, 3, 5, 7.5 (or 7_5), 10")
|
||||||
|
|
||||||
self.boilerplate_filename = self.HP_BOILERPLATE_MAP[self.config.hp]
|
self.boilerplate_filename = self.HP_BOILERPLATE_MAP[self.config.hp]
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,9 @@ class DPMModuleGenerator:
|
|||||||
|
|
||||||
def __init__(self, config: DPMModuleConfig):
|
def __init__(self, config: DPMModuleConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_path = os.path.join("boilerplate", "DPM_Module.L5X")
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, "DPM_Module.L5X")
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ This module provides functionality to generate EN4TR module L5X files by
|
|||||||
loading a boilerplate template and modifying specific fields.
|
loading a boilerplate template and modifying specific fields.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
||||||
@ -16,7 +17,7 @@ class EN4TRModuleConfig(BaseModuleConfig):
|
|||||||
"""Configuration for an EN4TR module."""
|
"""Configuration for an EN4TR module."""
|
||||||
name: str
|
name: str
|
||||||
ethernet_address: str # e.g., "11.200.1.1"
|
ethernet_address: str # e.g., "11.200.1.1"
|
||||||
boilerplate_path: str = "boilerplate/SLOT2_EN4TR_Module.L5X"
|
boilerplate_path: str = field(default_factory=lambda: os.path.join(os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate'), 'SLOT2_EN4TR_Module.L5X'))
|
||||||
parent_module: str = "Local"
|
parent_module: str = "Local"
|
||||||
parent_port_id: str = "1"
|
parent_port_id: str = "1"
|
||||||
slot_address: str = "2"
|
slot_address: str = "2"
|
||||||
|
|||||||
@ -32,7 +32,9 @@ class ExtendoModuleGenerator:
|
|||||||
def __init__(self, config: ExtendoModuleConfig):
|
def __init__(self, config: ExtendoModuleConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_filename = "EXTENDO_Module.L5X"
|
self.boilerplate_filename = "EXTENDO_Module.L5X"
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,9 @@ class FestoSolenoidGenerator:
|
|||||||
|
|
||||||
def __init__(self, config: FestoSolenoidConfig):
|
def __init__(self, config: FestoSolenoidConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_path = os.path.join("boilerplate", "Festo_Solenoids_Module.L5X")
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, "Festo_Solenoids_Module.L5X")
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,8 @@ This module provides functionality to generate IB16 module L5X files by
|
|||||||
loading a boilerplate template and modifying specific fields.
|
loading a boilerplate template and modifying specific fields.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
import os
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional, Dict
|
from typing import Optional, Dict
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
||||||
@ -16,7 +17,7 @@ from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
|||||||
class IB16ModuleConfig(BaseModuleConfig):
|
class IB16ModuleConfig(BaseModuleConfig):
|
||||||
"""Configuration for an IB16 module."""
|
"""Configuration for an IB16 module."""
|
||||||
name: str
|
name: str
|
||||||
boilerplate_path: str = "boilerplate/SLOT5_IB16_Module.L5X"
|
boilerplate_path: str = field(default_factory=lambda: os.path.join(os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate'), 'SLOT5_IB16_Module.L5X'))
|
||||||
slot_address: str = "5"
|
slot_address: str = "5"
|
||||||
parent_module: str = "Local"
|
parent_module: str = "Local"
|
||||||
parent_port_id: str = "1"
|
parent_port_id: str = "1"
|
||||||
|
|||||||
@ -6,7 +6,8 @@ This module provides functionality to generate IB16S safety module L5X files by
|
|||||||
loading a boilerplate template and modifying specific fields.
|
loading a boilerplate template and modifying specific fields.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
import os
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional, Dict
|
from typing import Optional, Dict
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
||||||
@ -16,7 +17,7 @@ from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
|||||||
class IB16SModuleConfig(BaseModuleConfig):
|
class IB16SModuleConfig(BaseModuleConfig):
|
||||||
"""Configuration for an IB16S safety module."""
|
"""Configuration for an IB16S safety module."""
|
||||||
name: str
|
name: str
|
||||||
boilerplate_path: str = "boilerplate/SLOT7_IB16S_Module.L5X"
|
boilerplate_path: str = field(default_factory=lambda: os.path.join(os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate'), 'SLOT7_IB16S_Module.L5X'))
|
||||||
slot_address: str = "7"
|
slot_address: str = "7"
|
||||||
parent_module: str = "Local"
|
parent_module: str = "Local"
|
||||||
parent_port_id: str = "1"
|
parent_port_id: str = "1"
|
||||||
|
|||||||
@ -6,7 +6,8 @@ This module provides functionality to generate L83ES controller L5X files by
|
|||||||
loading a boilerplate template and modifying specific fields.
|
loading a boilerplate template and modifying specific fields.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
import os
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
||||||
|
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
|||||||
class L83ESControllerConfig(BaseModuleConfig):
|
class L83ESControllerConfig(BaseModuleConfig):
|
||||||
"""Configuration for an L83ES controller."""
|
"""Configuration for an L83ES controller."""
|
||||||
name: str
|
name: str
|
||||||
boilerplate_path: str = "boilerplate/SLOT0_L83ES.L5X"
|
boilerplate_path: str = field(default_factory=lambda: os.path.join(os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate'), 'SLOT0_L83ES.L5X'))
|
||||||
processor_type: str = "1756-L83ES"
|
processor_type: str = "1756-L83ES"
|
||||||
major_rev: str = "36"
|
major_rev: str = "36"
|
||||||
minor_rev: str = "11"
|
minor_rev: str = "11"
|
||||||
|
|||||||
@ -27,7 +27,9 @@ class LPEBoilerplateGenerator:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.parent_module = parent_module
|
self.parent_module = parent_module
|
||||||
self.port_address = port_address
|
self.port_address = port_address
|
||||||
self.boilerplate_path = os.path.join("boilerplate", "LPE_Module.L5X")
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, "LPE_Module.L5X")
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,9 @@ class M12DRModuleGenerator:
|
|||||||
|
|
||||||
# Determine the correct boilerplate file
|
# Determine the correct boilerplate file
|
||||||
self.boilerplate_filename = self._determine_boilerplate_filename()
|
self.boilerplate_filename = self._determine_boilerplate_filename()
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
@ -65,7 +67,9 @@ class M12DRModuleGenerator:
|
|||||||
"""
|
"""
|
||||||
# First, try module-specific boilerplate
|
# First, try module-specific boilerplate
|
||||||
module_specific_filename = f"{self.config.name}_Module.L5X"
|
module_specific_filename = f"{self.config.name}_Module.L5X"
|
||||||
module_specific_path = os.path.join("boilerplate", module_specific_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
module_specific_path = os.path.join(boilerplate_dir, module_specific_filename)
|
||||||
|
|
||||||
if os.path.exists(module_specific_path):
|
if os.path.exists(module_specific_path):
|
||||||
print(f" {self.config.name} (FIO {self.config.variant}): Using module-specific boilerplate {module_specific_filename}")
|
print(f" {self.config.name} (FIO {self.config.variant}): Using module-specific boilerplate {module_specific_filename}")
|
||||||
|
|||||||
@ -6,7 +6,8 @@ This module provides functionality to generate OB16E module L5X files by
|
|||||||
loading a boilerplate template and modifying specific fields.
|
loading a boilerplate template and modifying specific fields.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
import os
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional, Dict
|
from typing import Optional, Dict
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
||||||
@ -16,7 +17,7 @@ from .base_boilerplate_model import BaseModuleConfig, BaseBoilerplateGenerator
|
|||||||
class OB16EModuleConfig(BaseModuleConfig):
|
class OB16EModuleConfig(BaseModuleConfig):
|
||||||
"""Configuration for an OB16E module."""
|
"""Configuration for an OB16E module."""
|
||||||
name: str
|
name: str
|
||||||
boilerplate_path: str = "boilerplate/SLOT6_OB16E_Module.L5X"
|
boilerplate_path: str = field(default_factory=lambda: os.path.join(os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate'), 'SLOT6_OB16E_Module.L5X'))
|
||||||
slot_address: str = "6"
|
slot_address: str = "6"
|
||||||
parent_module: str = "Local"
|
parent_module: str = "Local"
|
||||||
parent_port_id: str = "1"
|
parent_port_id: str = "1"
|
||||||
|
|||||||
@ -32,7 +32,9 @@ class PMMModuleGenerator:
|
|||||||
def __init__(self, config: PMMModuleConfig):
|
def __init__(self, config: PMMModuleConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_filename = "PMM_Module.L5X"
|
self.boilerplate_filename = "PMM_Module.L5X"
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,9 @@ class SIOModuleGenerator:
|
|||||||
|
|
||||||
def __init__(self, config: SIOModuleConfig):
|
def __init__(self, config: SIOModuleConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_path = os.path.join("boilerplate", "SIO_Module.L5X")
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, "SIO_Module.L5X")
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,9 @@ class TL70BeaconGenerator:
|
|||||||
|
|
||||||
def __init__(self, config: TL70BeaconConfig):
|
def __init__(self, config: TL70BeaconConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_path = os.path.join("boilerplate", "TL70_Module.L5X")
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, "TL70_Module.L5X")
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,9 @@ class TurckHubModuleGenerator:
|
|||||||
|
|
||||||
# Determine the correct boilerplate file
|
# Determine the correct boilerplate file
|
||||||
self.boilerplate_filename = self._determine_boilerplate_filename()
|
self.boilerplate_filename = self._determine_boilerplate_filename()
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
|
|
||||||
# Set default port address if not specified
|
# Set default port address if not specified
|
||||||
if not self.config.port_address:
|
if not self.config.port_address:
|
||||||
@ -107,7 +109,9 @@ class TurckHubModuleGenerator:
|
|||||||
|
|
||||||
# Second, try module-specific boilerplate
|
# Second, try module-specific boilerplate
|
||||||
module_specific_filename = f"{self.config.name}_Module.L5X"
|
module_specific_filename = f"{self.config.name}_Module.L5X"
|
||||||
module_specific_path = os.path.join("boilerplate", module_specific_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
module_specific_path = os.path.join(boilerplate_dir, module_specific_filename)
|
||||||
|
|
||||||
if os.path.exists(module_specific_path):
|
if os.path.exists(module_specific_path):
|
||||||
print(f" {self.config.name} (FIOH {self.config.variant}): Using module-specific boilerplate {module_specific_filename}")
|
print(f" {self.config.name} (FIOH {self.config.variant}): Using module-specific boilerplate {module_specific_filename}")
|
||||||
|
|||||||
@ -45,7 +45,9 @@ class VFDModuleGenerator:
|
|||||||
raise ValueError(f"Unsupported HP value: {self.config.hp}. Supported values: 15, 20, 30")
|
raise ValueError(f"Unsupported HP value: {self.config.hp}. Supported values: 15, 20, 30")
|
||||||
|
|
||||||
self.boilerplate_filename = self.HP_BOILERPLATE_MAP[self.config.hp]
|
self.boilerplate_filename = self.HP_BOILERPLATE_MAP[self.config.hp]
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,9 @@ class ZMXModuleGenerator:
|
|||||||
def __init__(self, config: ZMXModuleConfig):
|
def __init__(self, config: ZMXModuleConfig):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.boilerplate_filename = "ZMX_Module.L5X"
|
self.boilerplate_filename = "ZMX_Module.L5X"
|
||||||
self.boilerplate_path = os.path.join("boilerplate", self.boilerplate_filename)
|
# Use project-specific boilerplate directory if set, otherwise default
|
||||||
|
boilerplate_dir = os.environ.get('MCM_BOILERPLATE_DIR', 'boilerplate')
|
||||||
|
self.boilerplate_path = os.path.join(boilerplate_dir, self.boilerplate_filename)
|
||||||
self.tree = None
|
self.tree = None
|
||||||
self.root = None
|
self.root = None
|
||||||
|
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
import sys
|
|
||||||
import pathlib
|
|
||||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
|
|
||||||
|
|
||||||
import os
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from enhanced_mcm_generator import EnhancedMCMGenerator
|
|
||||||
|
|
||||||
|
|
||||||
def _canonicalize_xml(path: str) -> bytes:
|
|
||||||
"""Return a canonicalised representation of an L5X / XML file.
|
|
||||||
|
|
||||||
The function removes volatile attributes (e.g. ExportDate) and sorts
|
|
||||||
attributes of every element so the resulting byte string is stable
|
|
||||||
across Python runs and operating systems.
|
|
||||||
"""
|
|
||||||
tree = ET.parse(path)
|
|
||||||
root = tree.getroot()
|
|
||||||
|
|
||||||
# Remove volatile attributes that change on every export (present on many elements)
|
|
||||||
for elem in root.iter():
|
|
||||||
elem.attrib.pop("ExportDate", None)
|
|
||||||
|
|
||||||
# Recursively sort attributes to obtain a deterministic ordering
|
|
||||||
def _sort_attrs(elem: ET.Element):
|
|
||||||
if elem.attrib:
|
|
||||||
# Convert to list with sorted items to keep ElementTree stable
|
|
||||||
sorted_items = sorted(elem.attrib.items())
|
|
||||||
elem.attrib.clear()
|
|
||||||
elem.attrib.update(sorted_items)
|
|
||||||
for child in elem:
|
|
||||||
_sort_attrs(child)
|
|
||||||
|
|
||||||
_sort_attrs(root)
|
|
||||||
|
|
||||||
# Normalise text nodes: strip leading/trailing whitespace so that
|
|
||||||
# cosmetic indentation inside <Comment> elements does not cause false
|
|
||||||
# differences.
|
|
||||||
for elem in root.iter():
|
|
||||||
if elem.text is not None:
|
|
||||||
elem.text = elem.text.strip()
|
|
||||||
|
|
||||||
# ElementTree does not guarantee attribute ordering when converting to
|
|
||||||
# string, but because we have manually re-inserted sorted attributes we
|
|
||||||
# get a deterministic output here.
|
|
||||||
return ET.tostring(root, encoding="utf-8")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.regression
|
|
||||||
def test_generated_project_matches_golden(tmp_path):
|
|
||||||
"""Generate the project and compare it against the golden reference.
|
|
||||||
|
|
||||||
If this test fails, a refactor has changed the *semantic* XML output.
|
|
||||||
Check the diff to decide whether the change is intended or not.
|
|
||||||
"""
|
|
||||||
project_name = "MCM04_Chute_Load"
|
|
||||||
|
|
||||||
# 1. Run the generator to build a fresh project under the temporary dir
|
|
||||||
generator = EnhancedMCMGenerator(project_name, excel_file="Data.xlsx")
|
|
||||||
assert generator.load_and_process_data(), "Failed to load/process Excel data"
|
|
||||||
|
|
||||||
output_path = generator.generate_complete_project()
|
|
||||||
|
|
||||||
# 2. Compare with the golden file
|
|
||||||
golden_path = os.path.join(
|
|
||||||
"generated_projects", "MCM04_Chute_Load_To_Compare_Against.L5X"
|
|
||||||
)
|
|
||||||
assert os.path.exists(golden_path), "Golden file is missing"
|
|
||||||
|
|
||||||
assert _canonicalize_xml(output_path) == _canonicalize_xml(
|
|
||||||
golden_path
|
|
||||||
), "Generated project differs from golden reference"
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import xml.etree.ElementTree as ET
|
|
||||||
import difflib
|
|
||||||
import sys
|
|
||||||
import pathlib
|
|
||||||
|
|
||||||
def canon(path):
|
|
||||||
tree = ET.parse(path)
|
|
||||||
root = tree.getroot()
|
|
||||||
for elem in root.iter():
|
|
||||||
elem.attrib.pop('ExportDate', None)
|
|
||||||
# sort attributes
|
|
||||||
if elem.attrib:
|
|
||||||
items = sorted(elem.attrib.items())
|
|
||||||
elem.attrib.clear()
|
|
||||||
elem.attrib.update(items)
|
|
||||||
return ET.tostring(root, encoding='unicode').splitlines()
|
|
||||||
|
|
||||||
a = canon(sys.argv[1])
|
|
||||||
b = canon(sys.argv[2])
|
|
||||||
for l in difflib.unified_diff(a,b, lineterm=''):
|
|
||||||
print(l)
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo ====================================
|
echo ====================================
|
||||||
echo PLC Compilation: mtn6
|
echo PLC Compilation: MTN6_MCM02
|
||||||
echo Project Type: UNKNOWN
|
echo Project Type: UNKNOWN
|
||||||
echo ====================================
|
echo ====================================
|
||||||
echo.
|
echo.
|
||||||
@ -9,8 +9,8 @@ cd /d "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler"
|
|||||||
echo Working directory: %CD%
|
echo Working directory: %CD%
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
if not exist "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.L5X" (
|
if not exist "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.L5X" (
|
||||||
echo ERROR: L5X file not found: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.L5X
|
echo ERROR: L5X file not found: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.L5X
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
@ -61,28 +61,28 @@ if errorlevel 1 (
|
|||||||
echo ✓ Logix Designer SDK found
|
echo ✓ Logix Designer SDK found
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
echo Input L5X file: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.L5X
|
echo Input L5X file: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.L5X
|
||||||
for %%F in ("C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.L5X") do echo File size: %%~zF bytes
|
for %%F in ("C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.L5X") do echo File size: %%~zF bytes
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
echo Starting compilation...
|
echo Starting compilation...
|
||||||
echo Command: py -3.12 l5x_to_acd.py "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.L5X"
|
echo Command: py -3.12 l5x_to_acd.py "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.L5X"
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
py -3.12 l5x_to_acd.py "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.L5X"
|
py -3.12 l5x_to_acd.py "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.L5X"
|
||||||
|
|
||||||
if exist "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.ACD" (
|
if exist "C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.ACD" (
|
||||||
echo.
|
echo.
|
||||||
echo ====================================
|
echo ====================================
|
||||||
echo SUCCESS: Compilation completed!
|
echo SUCCESS: Compilation completed!
|
||||||
echo Output: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.ACD
|
echo Output: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.ACD
|
||||||
for %%F in ("C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.ACD") do echo ACD size: %%~zF bytes
|
for %%F in ("C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.ACD") do echo ACD size: %%~zF bytes
|
||||||
echo ====================================
|
echo ====================================
|
||||||
) else (
|
) else (
|
||||||
echo.
|
echo.
|
||||||
echo ====================================
|
echo ====================================
|
||||||
echo ERROR: Compilation failed!
|
echo ERROR: Compilation failed!
|
||||||
echo Expected output: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\mtn6.ACD
|
echo Expected output: C:\Users\ilia.gurielidze\Projects\PLC Generation\L5X2ACD Compiler\MTN6_MCM02.ACD
|
||||||
echo ====================================
|
echo ====================================
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2,6 +2,7 @@
|
|||||||
"files": {
|
"files": {
|
||||||
"excel_file": "DESC_IP_MERGED.xlsx",
|
"excel_file": "DESC_IP_MERGED.xlsx",
|
||||||
"output_dir": ".",
|
"output_dir": ".",
|
||||||
|
"zones_file": "zones.json",
|
||||||
"safety_l5x": "SafetyProgram_Generated.L5X",
|
"safety_l5x": "SafetyProgram_Generated.L5X",
|
||||||
"main_l5x": "MainProgram_Generated.L5X",
|
"main_l5x": "MainProgram_Generated.L5X",
|
||||||
"mapping_txt": "SafetyTagMapping.txt"
|
"mapping_txt": "SafetyTagMapping.txt"
|
||||||
@ -10,7 +11,7 @@
|
|||||||
"global": {},
|
"global": {},
|
||||||
"per_routine": {}
|
"per_routine": {}
|
||||||
},
|
},
|
||||||
"routines": [
|
"routine_plan": [
|
||||||
{
|
{
|
||||||
"name": "main_routine",
|
"name": "main_routine",
|
||||||
"plugin": "main_routine",
|
"plugin": "main_routine",
|
||||||
@ -19,14 +20,6 @@
|
|||||||
"order": 10,
|
"order": 10,
|
||||||
"params": {}
|
"params": {}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "safety_tag_map",
|
|
||||||
"plugin": "safety_tag_map",
|
|
||||||
"enabled": true,
|
|
||||||
"program": "MainProgram",
|
|
||||||
"order": 130,
|
|
||||||
"params": {}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "mcm",
|
"name": "mcm",
|
||||||
"plugin": "mcm",
|
"plugin": "mcm",
|
||||||
@ -43,18 +36,10 @@
|
|||||||
"order": 25,
|
"order": 25,
|
||||||
"params": {}
|
"params": {}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "estop_check",
|
|
||||||
"plugin": "estop_check",
|
|
||||||
"enabled": true,
|
|
||||||
"program": "MainProgram",
|
|
||||||
"order": 120,
|
|
||||||
"params": {}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "dpm",
|
"name": "dpm",
|
||||||
"plugin": "dpm",
|
"plugin": "dpm",
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"program": "MainProgram",
|
"program": "MainProgram",
|
||||||
"order": 40,
|
"order": 40,
|
||||||
"params": {}
|
"params": {}
|
||||||
@ -142,7 +127,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jpe",
|
"name": "jpe",
|
||||||
"plugin": "jpe",
|
"plugin": "jpe",
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"program": "MainProgram",
|
"program": "MainProgram",
|
||||||
"order": 150,
|
"order": 150,
|
||||||
"params": {}
|
"params": {}
|
||||||
@ -210,6 +195,22 @@
|
|||||||
"program": "SafetyProgram",
|
"program": "SafetyProgram",
|
||||||
"order": 20,
|
"order": 20,
|
||||||
"params": {}
|
"params": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "estop_check",
|
||||||
|
"plugin": "estop_check",
|
||||||
|
"enabled": true,
|
||||||
|
"program": "MainProgram",
|
||||||
|
"order": 120,
|
||||||
|
"params": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "safety_tag_map",
|
||||||
|
"plugin": "safety_tag_map",
|
||||||
|
"enabled": true,
|
||||||
|
"program": "MainProgram",
|
||||||
|
"order": 130,
|
||||||
|
"params": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"xml": {
|
"xml": {
|
||||||
@ -219,6 +220,47 @@
|
|||||||
"target_class": "Standard",
|
"target_class": "Standard",
|
||||||
"export_options": "References NoRawData L5KData DecoratedData Context Dependencies ForceProtectedEncoding AllProjDocTrans"
|
"export_options": "References NoRawData L5KData DecoratedData Context Dependencies ForceProtectedEncoding AllProjDocTrans"
|
||||||
},
|
},
|
||||||
|
"routines": {
|
||||||
|
"safety_tag_prefix": "SFT_",
|
||||||
|
"mcm_input_address": "Local:5:I.Data.0",
|
||||||
|
"mcm_epb_status_inputs": ["Local:7:I.Pt02.Status", "Local:7:I.Pt03.Status"],
|
||||||
|
"mcm_epb_dcs_inputs": ["Local:7:I.Pt02.Data", "Local:7:I.Pt03.Data"],
|
||||||
|
"mcm_epb_dcs_tag_name": "MCM_EPB_DCS_CTRL",
|
||||||
|
"mcm_safety_tag": "MCM_S_PB",
|
||||||
|
"speed_ctrl_setpoint_tag": "Speed_350_FPM",
|
||||||
|
"speed_ctrl_setpoint_value": 350,
|
||||||
|
"no_horn_tag_name": "NO_Horn",
|
||||||
|
"mcm_base_tag": "MCM",
|
||||||
|
"mcm_ctrl_tag": "MCM.CTRL",
|
||||||
|
"rack_fault_tag": "Rack.AOI.Slot2_EN4TR_Faulted",
|
||||||
|
"mcm_epb_status_tag": "MCM_EPB_STATUS",
|
||||||
|
"top_level_estop_ok_tag": "EStop_MCM_OK",
|
||||||
|
"station_ctrl_tag": "Station.CTRL",
|
||||||
|
"apf_input_default": "In_0",
|
||||||
|
"mcm_aoi_input_args": [
|
||||||
|
"Local:5:I.Data.2",
|
||||||
|
"Local:5:I.Data.5",
|
||||||
|
"Local:5:I.Data.4",
|
||||||
|
"Local:5:I.Data.0",
|
||||||
|
"Local:5:I.Data.3",
|
||||||
|
"Local:7:I.Pt02.Data",
|
||||||
|
"Local:7:I.Pt03.Data",
|
||||||
|
"Local:5:I.Data.1",
|
||||||
|
"Local:7:I.Pt00.Data",
|
||||||
|
"Local:5:I.Data.7",
|
||||||
|
"Local:5:I.Data.8",
|
||||||
|
"Local:5:I.Data.6",
|
||||||
|
"Local:5:I.Data.9"
|
||||||
|
],
|
||||||
|
"mcm_aoi_output_args": [
|
||||||
|
"Local:6:O.Data.2",
|
||||||
|
"Local:6:O.Data.5",
|
||||||
|
"Local:6:O.Data.4",
|
||||||
|
"Local:6:O.Data.0",
|
||||||
|
"Local:6:O.Data.1",
|
||||||
|
"Local:6:O.Data.3"
|
||||||
|
]
|
||||||
|
},
|
||||||
"extraction": {
|
"extraction": {
|
||||||
"rst_desc_contains": [ "START" ],
|
"rst_desc_contains": [ "START" ],
|
||||||
"rst_desc_excludes": [ "LIGHT" ],
|
"rst_desc_excludes": [ "LIGHT" ],
|
||||||
701
MTN6_zones.json
Normal file
701
MTN6_zones.json
Normal file
@ -0,0 +1,701 @@
|
|||||||
|
{
|
||||||
|
"MCM01": [
|
||||||
|
{
|
||||||
|
"name": "MCM01",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-01",
|
||||||
|
"start": "UL1_1",
|
||||||
|
"stop": "UL1_13",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-06",
|
||||||
|
"start": "UL4_1",
|
||||||
|
"stop": "UL4_13",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-11",
|
||||||
|
"start": "UL7_1",
|
||||||
|
"stop": "UL7_13",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-17",
|
||||||
|
"start": "UL11_1",
|
||||||
|
"stop": "UL11_13",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-02",
|
||||||
|
"start": "UL2_1",
|
||||||
|
"stop": "UL2_10",
|
||||||
|
"interlock": "01-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-03",
|
||||||
|
"start": "UL3_1",
|
||||||
|
"stop": "UL3_9",
|
||||||
|
"interlock": "01-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-04",
|
||||||
|
"start": "PS1_1",
|
||||||
|
"stop": "PS1_4",
|
||||||
|
"interlock": "01-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-07",
|
||||||
|
"start": "UL5_1",
|
||||||
|
"stop": "UL5_10",
|
||||||
|
"interlock": "01-06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-08",
|
||||||
|
"start": "UL6_1",
|
||||||
|
"stop": "UL6_9",
|
||||||
|
"interlock": "01-06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-09",
|
||||||
|
"start": "PS2_1",
|
||||||
|
"stop": "PS2_4",
|
||||||
|
"interlock": "01-06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-12",
|
||||||
|
"start": "UL8_1",
|
||||||
|
"stop": "UL8_9",
|
||||||
|
"interlock": "01-11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-13",
|
||||||
|
"start": "UL9_1",
|
||||||
|
"stop": "UL9_11",
|
||||||
|
"interlock": "01-11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-14",
|
||||||
|
"start": "PS3_1",
|
||||||
|
"stop": "PS3_3",
|
||||||
|
"interlock": "01-11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-16",
|
||||||
|
"start": "UL10_1",
|
||||||
|
"stop": "UL10_10",
|
||||||
|
"interlock": "01-17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-18",
|
||||||
|
"start": "UL12_1",
|
||||||
|
"stop": "UL12_10",
|
||||||
|
"interlock": "01-17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-19",
|
||||||
|
"start": "PS4_1",
|
||||||
|
"stop": "PS4_5",
|
||||||
|
"interlock": "01-17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-05",
|
||||||
|
"start": "PS1_5",
|
||||||
|
"stop": "PS1_5",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-10",
|
||||||
|
"start": "PS2_5",
|
||||||
|
"stop": "PS2_6",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-15",
|
||||||
|
"start": "PS3_8",
|
||||||
|
"stop": "PS3_12",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-20",
|
||||||
|
"start": "PS4_11",
|
||||||
|
"stop": "PS4_14",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "01-21",
|
||||||
|
"start": "PS3_4",
|
||||||
|
"stop": "PS3_7",
|
||||||
|
"interlock": "MCM01"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MCM02": [
|
||||||
|
{
|
||||||
|
"name": "MCM02",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-01",
|
||||||
|
"start": "UL13_1",
|
||||||
|
"stop": "UL13_12",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-02",
|
||||||
|
"start": "UL14_1",
|
||||||
|
"stop": "UL14_10",
|
||||||
|
"interlock": "02-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-03",
|
||||||
|
"start": "UL15_1",
|
||||||
|
"stop": "UL15_10",
|
||||||
|
"interlock": "02-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-04",
|
||||||
|
"start": "PS5_1",
|
||||||
|
"stop": "PS5_5",
|
||||||
|
"interlock": "02-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-05",
|
||||||
|
"start": "PS5_12",
|
||||||
|
"stop": "PS5_13",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-06",
|
||||||
|
"start": "UL16_1",
|
||||||
|
"stop": "UL16_9",
|
||||||
|
"interlock": "02-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-07",
|
||||||
|
"start": "UL17_1",
|
||||||
|
"stop": "UL17_10",
|
||||||
|
"interlock": "02-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-08",
|
||||||
|
"start": "UL18_1",
|
||||||
|
"stop": "UL18_16",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-09",
|
||||||
|
"start": "PS6_1",
|
||||||
|
"stop": "PS6_5",
|
||||||
|
"interlock": "02-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-10",
|
||||||
|
"start": "PS6_12",
|
||||||
|
"stop": "PS6_13",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-11",
|
||||||
|
"start": "UL19_1",
|
||||||
|
"stop": "UL19_9",
|
||||||
|
"interlock": "02-13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-12",
|
||||||
|
"start": "UL20_1",
|
||||||
|
"stop": "UL20_11",
|
||||||
|
"interlock": "02-13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-13",
|
||||||
|
"start": "UL21_1",
|
||||||
|
"stop": "UL21_17",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-14",
|
||||||
|
"start": "PS7_1",
|
||||||
|
"stop": "PS7_5",
|
||||||
|
"interlock": "02-13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-15",
|
||||||
|
"start": "PS7_8",
|
||||||
|
"stop": "PS7_9",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "02-16",
|
||||||
|
"start": "PS7_11",
|
||||||
|
"stop": "PS7_14",
|
||||||
|
"interlock": "MCM02"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MCM03": [
|
||||||
|
{
|
||||||
|
"name": "MCM03",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-01",
|
||||||
|
"start": "UL1_14",
|
||||||
|
"stop": "UL1_20",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-02",
|
||||||
|
"start": "UL4_14",
|
||||||
|
"stop": "UL4_20",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-03",
|
||||||
|
"start": "UL7_14",
|
||||||
|
"stop": "UL7_20",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-04",
|
||||||
|
"start": "UL11_14",
|
||||||
|
"stop": "UL11_20",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-05",
|
||||||
|
"start": "UL13_13",
|
||||||
|
"stop": "UL13_19",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-06",
|
||||||
|
"start": "UL18_17",
|
||||||
|
"stop": "UL18_23",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-07",
|
||||||
|
"start": "UL21_18",
|
||||||
|
"stop": "UL21_24",
|
||||||
|
"interlock": "03-08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-08",
|
||||||
|
"start": "NCP1_1",
|
||||||
|
"stop": "NCP1_5",
|
||||||
|
"interlock": "MCM03"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "03-09",
|
||||||
|
"start": "NCP1_6",
|
||||||
|
"stop": "NCP1_8",
|
||||||
|
"interlock": "MCM03"
|
||||||
|
}
|
||||||
|
],"MCM04": [
|
||||||
|
{
|
||||||
|
"name": "MCM04",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-01",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"ranges": [
|
||||||
|
{"start": "ULC7_1", "stop": "ULC7_3"},
|
||||||
|
{"start": "ULC8_1", "stop": "ULC8_3"},
|
||||||
|
{"start": "PS10_1", "stop": "PS10_3"}
|
||||||
|
],
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-02",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"ranges": [
|
||||||
|
{"start": "PS10_1", "stop": "PS10_3"},
|
||||||
|
{"start": "PS11_1", "stop": "PS11_2"}
|
||||||
|
],
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-03",
|
||||||
|
"start": "PS10_5",
|
||||||
|
"stop": "PS10_5",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-04",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"ranges": [
|
||||||
|
{"start": "ULC5_1", "stop": "ULC5_3"},
|
||||||
|
{"start": "ULC6_1", "stop": "ULC6_3"},
|
||||||
|
{"start": "PS11_1", "stop": "PS11_4"}
|
||||||
|
],
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-05",
|
||||||
|
"start": "PS11_6",
|
||||||
|
"stop": "PS11_7",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-06",
|
||||||
|
"start": "PS11_8",
|
||||||
|
"stop": "PS11_9",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-07",
|
||||||
|
"start": "PS11_11",
|
||||||
|
"stop": "PS11_11",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-08",
|
||||||
|
"start": "PRS3_5",
|
||||||
|
"stop": "PRS3_6",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "04-09",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"ranges": [
|
||||||
|
{"start": "PRS4_1", "stop": "PRS4_2"},
|
||||||
|
{"start": "PS11_3", "stop": "PS11_3"}
|
||||||
|
],
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL1014",
|
||||||
|
"start": "FL1014_2_VFD1",
|
||||||
|
"stop": "FL1014_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL1018",
|
||||||
|
"start": "FL1018_2_VFD1",
|
||||||
|
"stop": "FL1018_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL1022",
|
||||||
|
"start": "FL1022_2_VFD1",
|
||||||
|
"stop": "FL1022_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL1026",
|
||||||
|
"start": "FL1026_2_VFD1",
|
||||||
|
"stop": "FL1026_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL1034",
|
||||||
|
"start": "FL1034_2_VFD1",
|
||||||
|
"stop": "FL1034_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL1038",
|
||||||
|
"start": "FL1038_2_VFD1",
|
||||||
|
"stop": "FL1038_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL3012",
|
||||||
|
"start": "FL3012_2_VFD1",
|
||||||
|
"stop": "FL3012_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL3016",
|
||||||
|
"start": "FL3016_2_VFD1",
|
||||||
|
"stop": "FL3016_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL3020",
|
||||||
|
"start": "FL3020_2_VFD1",
|
||||||
|
"stop": "FL3020_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FL3024",
|
||||||
|
"start": "FL3024_2_VFD1",
|
||||||
|
"stop": "FL3024_4_EX1",
|
||||||
|
"interlock": "MCM04"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MCM05": [
|
||||||
|
{
|
||||||
|
"name": "MCM05",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "05-01",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"ranges": [
|
||||||
|
{"start": "ULC1_1", "stop": "ULC1_3"},
|
||||||
|
{"start": "ULC2_1", "stop": "ULC2_3"},
|
||||||
|
{"start": "UL3_1", "stop": "UL3_3"},
|
||||||
|
{"start": "UL4_1", "stop": "UL4_3"},
|
||||||
|
{"start": "PS8_1", "stop": "PS8_2"},
|
||||||
|
{"start": "PRS2_1", "stop": "PRS2_1"}
|
||||||
|
],
|
||||||
|
"interlock": "MCM05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "05-02",
|
||||||
|
"start": "PS8_4",
|
||||||
|
"stop": "PS8_5",
|
||||||
|
"interlock": "MCM05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "05-03",
|
||||||
|
"start": "PS9_2",
|
||||||
|
"stop": "PS9_3",
|
||||||
|
"interlock": "MCM05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "05-04",
|
||||||
|
"start": "PS8_11",
|
||||||
|
"stop": "PS8_11",
|
||||||
|
"interlock": "MCM05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "05-05",
|
||||||
|
"start": "PRS1_6",
|
||||||
|
"stop": "PRS1_7",
|
||||||
|
"interlock": "MCM05"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MCM06": [
|
||||||
|
{
|
||||||
|
"name": "MCM06",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "06-01",
|
||||||
|
"start": "NCP1_9",
|
||||||
|
"stop": "NCP1_20",
|
||||||
|
"interlock": "MCM06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "06-02",
|
||||||
|
"start": "NCS1_1",
|
||||||
|
"stop": "NCS1_9",
|
||||||
|
"interlock": "MCM06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "06-03",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"ranges": [
|
||||||
|
{"start": "NCP1_21", "stop": "NCP1_21"},
|
||||||
|
{"start": "NCS2_1", "stop": "NCS2_7"}
|
||||||
|
],
|
||||||
|
"interlock": "MCM06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "06-04",
|
||||||
|
"start": "S02_1",
|
||||||
|
"stop": "S02_1",
|
||||||
|
"interlock": "MCM06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "06-05",
|
||||||
|
"start": "S02_2",
|
||||||
|
"stop": "S02_2",
|
||||||
|
"interlock": "MCM06"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"MCM07": [
|
||||||
|
{
|
||||||
|
"name": "MCM07",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-01",
|
||||||
|
"start": "BYAC_2",
|
||||||
|
"stop": "BYAC_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-02",
|
||||||
|
"start": "BYAD_2",
|
||||||
|
"stop": "BYAD_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-03",
|
||||||
|
"start": "BYAB_2",
|
||||||
|
"stop": "BYAB_7",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-04",
|
||||||
|
"start": "BYAC_8",
|
||||||
|
"stop": "BYAC_11",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-05",
|
||||||
|
"start": "BYAD_8",
|
||||||
|
"stop": "BYAD_9",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-06",
|
||||||
|
"start": "BYAB_13",
|
||||||
|
"stop": "BYAB_15",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-07",
|
||||||
|
"start": "BYBA_2",
|
||||||
|
"stop": "BYBA_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-08",
|
||||||
|
"start": "BYBC_2",
|
||||||
|
"stop": "BYBC_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-09",
|
||||||
|
"start": "BYBD_2",
|
||||||
|
"stop": "BYBD_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-10",
|
||||||
|
"start": "BYBA_10",
|
||||||
|
"stop": "BYBA_15",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-11",
|
||||||
|
"start": "BYBC_9",
|
||||||
|
"stop": "BYBC_10",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-12",
|
||||||
|
"start": "BYBD_13",
|
||||||
|
"stop": "BYBD_14",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-13",
|
||||||
|
"start": "BYCA_2",
|
||||||
|
"stop": "BYCA_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-14",
|
||||||
|
"start": "BYCB_2",
|
||||||
|
"stop": "BYCB_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-15",
|
||||||
|
"start": "BYCD_2",
|
||||||
|
"stop": "BYCD_5",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-16",
|
||||||
|
"start": "BYCA_6",
|
||||||
|
"stop": "BYCA_9",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-17",
|
||||||
|
"start": "BYCB_9",
|
||||||
|
"stop": "BYCB_10",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-18",
|
||||||
|
"start": "BYCD_11",
|
||||||
|
"stop": "BYCD_12",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-19",
|
||||||
|
"start": "BYDC_2",
|
||||||
|
"stop": "BYDC_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-20",
|
||||||
|
"start": "BYDA_2",
|
||||||
|
"stop": "BYDA_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-21",
|
||||||
|
"start": "BYDB_2",
|
||||||
|
"stop": "BYDB_3",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-22",
|
||||||
|
"start": "BYDC_9",
|
||||||
|
"stop": "BYDC_13",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-23",
|
||||||
|
"start": "BYDA_4",
|
||||||
|
"stop": "BYDA_9",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-24",
|
||||||
|
"start": "BYDB_15",
|
||||||
|
"stop": "BYDB_16",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-25",
|
||||||
|
"start": "BYDB_4",
|
||||||
|
"stop": "BYDB_6",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "07-26",
|
||||||
|
"start": "BYDC_4",
|
||||||
|
"stop": "BYDC_8",
|
||||||
|
"interlock": "MCM07"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DEFAULT": [
|
||||||
|
{
|
||||||
|
"name": "MCM",
|
||||||
|
"start": "",
|
||||||
|
"stop": "",
|
||||||
|
"interlock": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@ -1812,13 +1812,10 @@ UL11_13_FIO1:I.Pt00.Data,UL11_13_PE1 DEBRIS SENSOR,MCM01
|
|||||||
UL11_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
UL11_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
||||||
UL11_13_FIO1:I.Pt02.Data,UL11_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
UL11_13_FIO1:I.Pt02.Data,UL11_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
||||||
UL11_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
UL11_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
||||||
UL11_13_FIO1:O.Pt05.Data,SPARE,MCM01
|
|
||||||
UL11_13_FIO1:O.Pt07.Data,SPARE,MCM01
|
|
||||||
UL11_13_FIO1:I.Pt08.Data,UL11_13_PE3 ENTRANCE PE,MCM01
|
UL11_13_FIO1:I.Pt08.Data,UL11_13_PE3 ENTRANCE PE,MCM01
|
||||||
UL11_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
UL11_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
||||||
UL11_13_FIO1:I.Pt10.Data,UL11_13_PE4 EXIT PE,MCM01
|
UL11_13_FIO1:I.Pt10.Data,UL11_13_PE4 EXIT PE,MCM01
|
||||||
UL11_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
UL11_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
||||||
UL11_13_FIO1:O.Pt13.Data,SPARE,MCM01
|
|
||||||
UL11_13_FIO1:I.Pt14.Data,UL11_13_PE5 DEBRIS SENSOR,MCM01
|
UL11_13_FIO1:I.Pt14.Data,UL11_13_PE5 DEBRIS SENSOR,MCM01
|
||||||
UL11_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
UL11_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
||||||
UL11_3_FIO1:O.Pt00.Data,UL11_3_BCN1_R RED BEACON LIGHT,MCM01
|
UL11_3_FIO1:O.Pt00.Data,UL11_3_BCN1_R RED BEACON LIGHT,MCM01
|
||||||
@ -1853,13 +1850,10 @@ UL1_13_FIO1:I.Pt00.Data,UL1_13_PE1 DEBRIS SENSOR,MCM01
|
|||||||
UL1_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
UL1_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
||||||
UL1_13_FIO1:I.Pt02.Data,UL1_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
UL1_13_FIO1:I.Pt02.Data,UL1_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
||||||
UL1_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
UL1_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
||||||
UL1_13_FIO1:O.Pt05.Data,SPARE,MCM01
|
|
||||||
UL1_13_FIO1:O.Pt07.Data,SPARE,MCM01
|
|
||||||
UL1_13_FIO1:I.Pt08.Data,UL1_13_PE3 ENTRANCE PE,MCM01
|
UL1_13_FIO1:I.Pt08.Data,UL1_13_PE3 ENTRANCE PE,MCM01
|
||||||
UL1_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
UL1_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
||||||
UL1_13_FIO1:I.Pt10.Data,UL1_13_PE4 EXIT PE,MCM01
|
UL1_13_FIO1:I.Pt10.Data,UL1_13_PE4 EXIT PE,MCM01
|
||||||
UL1_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
UL1_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
||||||
UL1_13_FIO1:O.Pt13.Data,SPARE,MCM01
|
|
||||||
UL1_13_FIO1:I.Pt14.Data,UL1_13_PE5 DEBRIS SENSOR,MCM01
|
UL1_13_FIO1:I.Pt14.Data,UL1_13_PE5 DEBRIS SENSOR,MCM01
|
||||||
UL1_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
UL1_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
||||||
UL1_3_FIO1:O.Pt00.Data,UL1_3_BCN1_R RED BEACON LIGHT,MCM01
|
UL1_3_FIO1:O.Pt00.Data,UL1_3_BCN1_R RED BEACON LIGHT,MCM01
|
||||||
@ -1908,13 +1902,10 @@ UL4_13_FIO1:I.Pt00.Data,UL4_13_PE1 DEBRIS SENSOR,MCM01
|
|||||||
UL4_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
UL4_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
||||||
UL4_13_FIO1:I.Pt02.Data,UL4_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
UL4_13_FIO1:I.Pt02.Data,UL4_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
||||||
UL4_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
UL4_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
||||||
UL4_13_FIO1:O.Pt05.Data,SPARE,MCM01
|
|
||||||
UL4_13_FIO1:O.Pt07.Data,SPARE,MCM01
|
|
||||||
UL4_13_FIO1:I.Pt08.Data,UL4_13_PE3 ENTRANCE PE,MCM01
|
UL4_13_FIO1:I.Pt08.Data,UL4_13_PE3 ENTRANCE PE,MCM01
|
||||||
UL4_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
UL4_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
||||||
UL4_13_FIO1:I.Pt10.Data,UL4_13_PE4 EXIT PE,MCM01
|
UL4_13_FIO1:I.Pt10.Data,UL4_13_PE4 EXIT PE,MCM01
|
||||||
UL4_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
UL4_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
||||||
UL4_13_FIO1:O.Pt13.Data,SPARE,MCM01
|
|
||||||
UL4_13_FIO1:I.Pt14.Data,UL4_13_PE5 DEBRIS SENSOR,MCM01
|
UL4_13_FIO1:I.Pt14.Data,UL4_13_PE5 DEBRIS SENSOR,MCM01
|
||||||
UL4_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
UL4_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
||||||
UL4_3_FIO1:O.Pt00.Data,UL4_3_BCN1_R RED BEACON LIGHT,MCM01
|
UL4_3_FIO1:O.Pt00.Data,UL4_3_BCN1_R RED BEACON LIGHT,MCM01
|
||||||
@ -1963,13 +1954,10 @@ UL7_13_FIO1:I.Pt00.Data,UL7_13_PE1 DEBRIS SENSOR,MCM01
|
|||||||
UL7_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
UL7_13_FIO1:I.Pt01.Data,SPARE,MCM01
|
||||||
UL7_13_FIO1:I.Pt02.Data,UL7_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
UL7_13_FIO1:I.Pt02.Data,UL7_13_PE2 BELT ENGAGEMENT SENSOR,MCM01
|
||||||
UL7_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
UL7_13_FIO1:I.Pt03.Data,SPARE,MCM01
|
||||||
UL7_13_FIO1:O.Pt05.Data,SPARE,MCM01
|
|
||||||
UL7_13_FIO1:O.Pt07.Data,SPARE,MCM01
|
|
||||||
UL7_13_FIO1:I.Pt08.Data,UL7_13_PE3 ENTRANCE PE,MCM01
|
UL7_13_FIO1:I.Pt08.Data,UL7_13_PE3 ENTRANCE PE,MCM01
|
||||||
UL7_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
UL7_13_FIO1:I.Pt09.Data,SPARE,MCM01
|
||||||
UL7_13_FIO1:I.Pt10.Data,UL7_13_PE4 EXIT PE,MCM01
|
UL7_13_FIO1:I.Pt10.Data,UL7_13_PE4 EXIT PE,MCM01
|
||||||
UL7_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
UL7_13_FIO1:I.Pt11.Data,SPARE,MCM01
|
||||||
UL7_13_FIO1:O.Pt13.Data,SPARE,MCM01
|
|
||||||
UL7_13_FIO1:I.Pt14.Data,UL7_13_PE5 DEBRIS SENSOR,MCM01
|
UL7_13_FIO1:I.Pt14.Data,UL7_13_PE5 DEBRIS SENSOR,MCM01
|
||||||
UL7_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
UL7_13_FIO1:O.Pt15.Data,SPARE,MCM01
|
||||||
UL7_3_FIO1:O.Pt00.Data,UL7_3_BCN1_R RED BEACON LIGHT,MCM01
|
UL7_3_FIO1:O.Pt00.Data,UL7_3_BCN1_R RED BEACON LIGHT,MCM01
|
||||||
|
|||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
PLC Data Generator/MTN6_MCM02_DESC_IP_MERGED.xlsx
Normal file
BIN
PLC Data Generator/MTN6_MCM02_DESC_IP_MERGED.xlsx
Normal file
Binary file not shown.
1791
PLC Data Generator/MTN6_MCM02_OUTPUT.csv
Normal file
1791
PLC Data Generator/MTN6_MCM02_OUTPUT.csv
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,189 +0,0 @@
|
|||||||
import pandas as pd
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
def extract_fio_fioh_modules(input_file='MCM04_DESC_IP_MERGED.xlsx', output_file='FIO_FIOH_OUTPUT.csv'):
|
|
||||||
"""
|
|
||||||
Extract FIO and FIOH modules from DESC/IP sheet and create output with format:
|
|
||||||
TAGNAME, ADDR, TERM, TERMDESC, DESCA, DESCB
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Read the DESC/IP merged data
|
|
||||||
print(f"Reading input file: {input_file}")
|
|
||||||
xl = pd.ExcelFile(input_file)
|
|
||||||
# Try to auto-detect the DESC/IP sheet (kept for backward compatibility)
|
|
||||||
df = pd.read_excel(xl, sheet_name='DESC_IP')
|
|
||||||
print(f"Total rows in DESC_IP sheet: {len(df)}")
|
|
||||||
|
|
||||||
# --- Load NETWORK sheet for DPM mapping ----------------------------
|
|
||||||
network_sheet = None
|
|
||||||
for sheet in xl.sheet_names:
|
|
||||||
if 'NETWORK' in sheet.upper():
|
|
||||||
network_sheet = sheet
|
|
||||||
break
|
|
||||||
if network_sheet:
|
|
||||||
network_df = pd.read_excel(xl, sheet_name=network_sheet)
|
|
||||||
print(f"Loaded NETWORK sheet: {network_sheet} ({len(network_df)} rows)")
|
|
||||||
# Build mapping from Name -> DPM (blank-safe)
|
|
||||||
network_df['Name'] = network_df['Name'].astype(str).str.strip()
|
|
||||||
network_df['DPM'] = network_df['DPM'].fillna('').astype(str).str.strip()
|
|
||||||
name_to_dpm = dict(zip(network_df['Name'], network_df['DPM']))
|
|
||||||
else:
|
|
||||||
print("WARNING: NETWORK sheet not found in workbook – DPM column will be blank for masters")
|
|
||||||
name_to_dpm = {}
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(f"ERROR: File {input_file} not found!")
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
print(f"ERROR: Failed to read {input_file}: {str(e)}")
|
|
||||||
return
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------
|
|
||||||
# Build a mapping of FIOH tag -> its MASTER FIO tag by scanning DESC_IP
|
|
||||||
# Rows where DESCA contains 'FIOH' typically reference the hub on a master
|
|
||||||
# channel. We use these to derive the master relationship.
|
|
||||||
fioh_master_map = {}
|
|
||||||
fioh_ref_rows = df[df['DESCA'].astype(str).str.contains('FIOH', case=False, na=False)]
|
|
||||||
for _, r in fioh_ref_rows.iterrows():
|
|
||||||
fioh_tag = str(r['DESCA']).strip()
|
|
||||||
master_tag = str(r['TAGNAME']).strip()
|
|
||||||
# Keep the first master encountered to avoid overriding inconsistencies
|
|
||||||
fioh_master_map.setdefault(fioh_tag, master_tag)
|
|
||||||
|
|
||||||
# Filter for FIO and FIOH modules (TAGNAME containing "FIO")
|
|
||||||
fio_fioh_filter = df['TAGNAME'].str.contains('FIO', case=False, na=False)
|
|
||||||
fio_fioh_data = df[fio_fioh_filter].copy()
|
|
||||||
|
|
||||||
print(f"Found {len(fio_fioh_data)} FIO/FIOH entries")
|
|
||||||
|
|
||||||
if len(fio_fioh_data) == 0:
|
|
||||||
print("No FIO/FIOH modules found in the data!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get unique module names
|
|
||||||
unique_modules = fio_fioh_data['TAGNAME'].unique()
|
|
||||||
print(f"Found {len(unique_modules)} unique FIO/FIOH modules")
|
|
||||||
|
|
||||||
# Define channel mappings based on device type
|
|
||||||
def get_channels_for_device(device_type):
|
|
||||||
"""Return list of channels for a given device type"""
|
|
||||||
if device_type == 'M12DR': # FIO devices
|
|
||||||
return [f'IO{i}' for i in range(16)] # IO0 to IO15
|
|
||||||
elif device_type == 'Hub': # FIOH devices
|
|
||||||
return [f'IO{i}' for i in range(16)] # IO0 to IO15
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
# Prepare output data
|
|
||||||
output_rows = []
|
|
||||||
|
|
||||||
for module_name in unique_modules:
|
|
||||||
# Get module data
|
|
||||||
module_data = fio_fioh_data[fio_fioh_data['TAGNAME'] == module_name]
|
|
||||||
|
|
||||||
if len(module_data) == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Get device type from first row
|
|
||||||
device_type = module_data.iloc[0]['DEVICE_TYPE']
|
|
||||||
channels = get_channels_for_device(device_type)
|
|
||||||
|
|
||||||
print(f"Processing {module_name} ({device_type}) - {len(channels)} channels")
|
|
||||||
|
|
||||||
# Create a mapping of existing data by TERM
|
|
||||||
existing_data = {}
|
|
||||||
for _, row in module_data.iterrows():
|
|
||||||
term = str(row['TERM']).strip()
|
|
||||||
existing_data[term] = {
|
|
||||||
'DESCA': row['DESCA'] if pd.notna(row['DESCA']) else '',
|
|
||||||
'DESCB': row['DESCB'] if pd.notna(row['DESCB']) else ''
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate output rows for all channels
|
|
||||||
for channel in channels:
|
|
||||||
# Create ADDR by combining module name with channel
|
|
||||||
addr = f"{module_name}_{channel}"
|
|
||||||
|
|
||||||
# Get DESCA and DESCB from existing data if available
|
|
||||||
if channel in existing_data:
|
|
||||||
desca = existing_data[channel]['DESCA']
|
|
||||||
descb = existing_data[channel]['DESCB']
|
|
||||||
else:
|
|
||||||
# Default to SPARE if no existing data
|
|
||||||
desca = 'SPARE'
|
|
||||||
descb = ''
|
|
||||||
|
|
||||||
# Determine DPM value based on device type
|
|
||||||
if device_type == 'M12DR': # Master FIO
|
|
||||||
dpm_value = name_to_dpm.get(module_name, '')
|
|
||||||
elif device_type == 'Hub': # FIOH – use its master
|
|
||||||
dpm_value = fioh_master_map.get(module_name, '')
|
|
||||||
else:
|
|
||||||
dpm_value = ''
|
|
||||||
|
|
||||||
output_rows.append({
|
|
||||||
'TAGNAME': module_name,
|
|
||||||
'ADDR': addr,
|
|
||||||
'TERM': channel,
|
|
||||||
'TERMDESC': '', # Empty as shown in example
|
|
||||||
'DESCA': desca,
|
|
||||||
'DESCB': descb,
|
|
||||||
'DPM': dpm_value
|
|
||||||
})
|
|
||||||
|
|
||||||
# Create output DataFrame
|
|
||||||
output_df = pd.DataFrame(output_rows)
|
|
||||||
|
|
||||||
# Extract numeric part from TERM for natural sorting
|
|
||||||
def extract_io_number(term):
|
|
||||||
"""Extract the numeric part from IO term for proper sorting"""
|
|
||||||
match = re.match(r'IO(\d+)', term)
|
|
||||||
if match:
|
|
||||||
return int(match.group(1))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Add a temporary column for sorting
|
|
||||||
output_df['TERM_NUM'] = output_df['TERM'].apply(extract_io_number)
|
|
||||||
|
|
||||||
# Sort by TAGNAME and then by the numeric value of TERM
|
|
||||||
output_df = output_df.sort_values(['TAGNAME', 'TERM_NUM'])
|
|
||||||
|
|
||||||
# Drop the temporary column
|
|
||||||
output_df = output_df.drop(columns=['TERM_NUM'])
|
|
||||||
|
|
||||||
print(f"\nGenerated {len(output_df)} output rows")
|
|
||||||
print(f"Saving to: {output_file}")
|
|
||||||
|
|
||||||
# Replace any NaN values with empty strings for clean output
|
|
||||||
output_df = output_df.fillna('')
|
|
||||||
|
|
||||||
# Ensure DPM column is last (you can change order if desired)
|
|
||||||
cols = ['TAGNAME', 'ADDR', 'TERM', 'TERMDESC', 'DESCA', 'DESCB', 'DPM']
|
|
||||||
output_df = output_df[cols]
|
|
||||||
|
|
||||||
# Save to CSV
|
|
||||||
output_df.to_csv(output_file, index=False)
|
|
||||||
|
|
||||||
print(f"\nSample output:")
|
|
||||||
print(output_df.head(15))
|
|
||||||
|
|
||||||
print(f"\nOutput saved successfully to {output_file}")
|
|
||||||
return output_df
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Check if custom input file is provided
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
input_file = sys.argv[1]
|
|
||||||
else:
|
|
||||||
input_file = 'MCM04_DESC_IP_MERGED.xlsx'
|
|
||||||
|
|
||||||
# Check if custom output file is provided
|
|
||||||
if len(sys.argv) > 2:
|
|
||||||
output_file = sys.argv[2]
|
|
||||||
else:
|
|
||||||
output_file = 'FIO_FIOH_OUTPUT.csv'
|
|
||||||
|
|
||||||
extract_fio_fioh_modules(input_file, output_file)
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
import pandas as pd
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Read the Excel file
|
|
||||||
input_file = 'mcm04 very last.xlsx'
|
|
||||||
df = pd.read_excel(input_file)
|
|
||||||
|
|
||||||
# Prepare output rows
|
|
||||||
output_rows = []
|
|
||||||
|
|
||||||
io_columns = [f'IO{i}' for i in range(16)]
|
|
||||||
|
|
||||||
# First pass: collect all prefixes with JR1_PB (JAM RESET PUSHBUTTON)
|
|
||||||
jam_reset_prefixes = set()
|
|
||||||
for _, row in df.iterrows():
|
|
||||||
for io_col in io_columns:
|
|
||||||
val = row.get(io_col, '')
|
|
||||||
if pd.isna(val) or val == '':
|
|
||||||
continue
|
|
||||||
if 'JR1_PB' in str(val):
|
|
||||||
m = re.match(r'(S\d+)_', str(val))
|
|
||||||
if m:
|
|
||||||
jam_reset_prefixes.add(m.group(1))
|
|
||||||
|
|
||||||
# Second pass: build output with DESB logic
|
|
||||||
def get_prefix(tag):
|
|
||||||
m = re.match(r'(S\d+)_', str(tag))
|
|
||||||
return m.group(1) if m else None
|
|
||||||
|
|
||||||
def get_desb(desca):
|
|
||||||
if desca == 'SPARE' or pd.isna(desca) or desca == '':
|
|
||||||
return ''
|
|
||||||
tag = str(desca)
|
|
||||||
prefix = get_prefix(tag)
|
|
||||||
if 'BCN1_A' in tag:
|
|
||||||
return 'AMBER BEACON LIGHT'
|
|
||||||
if 'BCN1_B' in tag:
|
|
||||||
return 'BLUE BEACON LIGHT'
|
|
||||||
if 'BCN1' in tag:
|
|
||||||
if prefix in jam_reset_prefixes:
|
|
||||||
return '3 STACK IOLINK BEACON'
|
|
||||||
else:
|
|
||||||
return '2 STACK IOLINK BEACON'
|
|
||||||
if 'SOL' in tag:
|
|
||||||
return 'PKG RELEASE SOLENOID'
|
|
||||||
if 'PR' in tag:
|
|
||||||
return 'PKG RELEASE PUSHBUTTON'
|
|
||||||
if 'PE1' in tag:
|
|
||||||
return 'FULL PHOTOEYE 50%'
|
|
||||||
if 'PE2' in tag:
|
|
||||||
return 'FULL PHOTOEYE 100%'
|
|
||||||
if 'GS1_PB_LT' in tag or 'GS1_PB' in tag:
|
|
||||||
return 'CHUTE ENABLE PUSHBUTTON LIGHT'
|
|
||||||
if 'JR1_PB_LT' in tag:
|
|
||||||
return 'SORTER JAM RESET PUSHBUTTON LIGHT'
|
|
||||||
if 'JR1_PB' in tag:
|
|
||||||
return 'SORTER JAM RESET PUSHBUTTON'
|
|
||||||
if 'FIOH' in tag:
|
|
||||||
return 'HUB ARMOR BLOCK'
|
|
||||||
return ''
|
|
||||||
|
|
||||||
for _, row in df.iterrows():
|
|
||||||
tagname = row['P_TAG1']
|
|
||||||
for io_col in io_columns:
|
|
||||||
term = io_col
|
|
||||||
desca = row.get(io_col, '')
|
|
||||||
if pd.isna(desca) or desca == '':
|
|
||||||
desca = 'SPARE'
|
|
||||||
desb = get_desb(desca)
|
|
||||||
output_rows.append({'TAGNAME': tagname, 'TERM': term, 'DESCA': desca, 'DESB': desb})
|
|
||||||
|
|
||||||
# Output to CSV
|
|
||||||
output_df = pd.DataFrame(output_rows)
|
|
||||||
output_df.to_csv('MCM04_IO_EXPANDED.csv', index=False)
|
|
||||||
print('Output written to MCM04_IO_EXPANDED.csv')
|
|
||||||
@ -1,149 +1,98 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from io_paths import load_io_path_mappings
|
from io_paths import load_io_path_mappings
|
||||||
from process import process_data
|
from process import process_data
|
||||||
from post_process import post_process_io_data
|
from post_process import post_process_io_data
|
||||||
|
|
||||||
def create_desc_ip_sheet():
|
def create_desc_ip_sheet():
|
||||||
# Get Excel file path from command line arguments
|
# Get Excel file path from command line arguments
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: python main.py <excel_file_path>")
|
print("Usage: python main.py <excel_file_path>")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
excel_file = sys.argv[1]
|
excel_file = sys.argv[1]
|
||||||
|
|
||||||
if not os.path.exists(excel_file):
|
if not os.path.exists(excel_file):
|
||||||
print(f"CRITICAL: Excel file not found: {excel_file}")
|
print(f"CRITICAL: Excel file not found: {excel_file}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Load IO path mappings
|
# Load IO path mappings
|
||||||
print("Loading IO path mappings...")
|
print("Loading IO path mappings...")
|
||||||
apf_df, m12dr_df, hub_df, sorter_hub_df, sio_df, ib16_df, ob16e_df, ib16s_df = load_io_path_mappings()
|
apf_df, m12dr_df, hub_df, sorter_hub_df, sio_df, ib16_df, ob16e_df, ib16s_df = load_io_path_mappings()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Read Excel file to check available sheets
|
# Read Excel file to check available sheets
|
||||||
xl = pd.ExcelFile(excel_file)
|
xl = pd.ExcelFile(excel_file)
|
||||||
print(f"Available sheets: {xl.sheet_names}")
|
print(f"Available sheets: {xl.sheet_names}")
|
||||||
|
|
||||||
# Try to find sheets with similar names
|
# Try to find sheets with specific names
|
||||||
desc_sheet = None
|
desc_sheet = None
|
||||||
network_sheet = None
|
network_sheet = None
|
||||||
|
|
||||||
for sheet in xl.sheet_names:
|
for sheet in xl.sheet_names:
|
||||||
if 'DESC' in sheet.upper():
|
if 'DESC_PLC' in sheet.upper():
|
||||||
desc_sheet = sheet
|
desc_sheet = sheet
|
||||||
if 'NETWORK' in sheet.upper():
|
if 'NETWORK_PLC' in sheet.upper():
|
||||||
network_sheet = sheet
|
network_sheet = sheet
|
||||||
|
|
||||||
print(f"Found DESC sheet: {desc_sheet}")
|
print(f"Found DESC sheet: {desc_sheet}")
|
||||||
print(f"Found NETWORK sheet: {network_sheet}")
|
print(f"Found NETWORK sheet: {network_sheet}")
|
||||||
|
|
||||||
if not desc_sheet or not network_sheet:
|
if not desc_sheet or not network_sheet:
|
||||||
print("CRITICAL: Required sheets 'DESC...' and 'NETWORK...' not found in the Excel file.")
|
print("CRITICAL: Required sheets 'DESC_PLC' and 'NETWORK_PLC' not found in the Excel file.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Read the sheets
|
# Read the sheets
|
||||||
desc_df = pd.read_excel(xl, sheet_name=desc_sheet)
|
desc_df = pd.read_excel(xl, sheet_name=desc_sheet)
|
||||||
network_df = pd.read_excel(xl, sheet_name=network_sheet)
|
network_df = pd.read_excel(xl, sheet_name=network_sheet)
|
||||||
|
|
||||||
print(f"\nDESC columns: {list(desc_df.columns)}")
|
print(f"\nDESC columns: {list(desc_df.columns)}")
|
||||||
print(f"NETWORK columns: {list(network_df.columns)}")
|
print(f"NETWORK columns: {list(network_df.columns)}")
|
||||||
|
|
||||||
# Sort network data by PartNumber, DPM, and then Name
|
# Sort network data by PartNumber, DPM, and then Name
|
||||||
network_df['PartNumber'] = network_df['PartNumber'].fillna('') # Handle NaN in PartNumber
|
network_df['PartNumber'] = network_df['PartNumber'].fillna('') # Handle NaN in PartNumber
|
||||||
network_df['DPM'] = network_df['DPM'].fillna('') # Handle NaN in DPM
|
network_df['DPM'] = network_df['DPM'].fillna('') # Handle NaN in DPM
|
||||||
network_df = network_df.sort_values(by=['PartNumber', 'DPM', 'Name'])
|
network_df = network_df.sort_values(by=['PartNumber', 'DPM', 'Name'])
|
||||||
|
|
||||||
# Process the data based on user requirements
|
# Process the data based on user requirements
|
||||||
process_data(
|
process_data(
|
||||||
desc_df,
|
desc_df,
|
||||||
network_df,
|
network_df,
|
||||||
excel_file,
|
excel_file,
|
||||||
apf_df,
|
apf_df,
|
||||||
m12dr_df,
|
m12dr_df,
|
||||||
hub_df,
|
hub_df,
|
||||||
sorter_hub_df,
|
sorter_hub_df,
|
||||||
sio_df,
|
sio_df,
|
||||||
ib16_df,
|
ib16_df,
|
||||||
ob16e_df,
|
ob16e_df,
|
||||||
ib16s_df
|
ib16s_df
|
||||||
)
|
)
|
||||||
|
|
||||||
# Determine subsystem (e.g. MCM04) from the Excel file path so that
|
# Determine subsystem (e.g. MCM04) from the Excel file path so that
|
||||||
# we reference the exact file produced in process_data
|
# we reference the exact file produced in process_data
|
||||||
subsystem_match = re.search(r"(MCM\d+)", excel_file, re.IGNORECASE)
|
subsystem_match = re.search(r"(MCM\d+)", excel_file, re.IGNORECASE)
|
||||||
subsystem = subsystem_match.group(1).upper() if subsystem_match else "MCM"
|
subsystem = subsystem_match.group(1).upper() if subsystem_match else "MCM"
|
||||||
|
|
||||||
# Now run post-processing on the freshly generated workbook
|
# Now run post-processing on the freshly generated workbook
|
||||||
new_file = f"{subsystem}_DESC_IP_MERGED.xlsx"
|
new_file = f"{subsystem}_DESC_IP_MERGED.xlsx"
|
||||||
output_file = f"{subsystem}_OUTPUT.csv"
|
output_file = f"{subsystem}_OUTPUT.csv"
|
||||||
post_process_io_data(new_file, output_file)
|
post_process_io_data(new_file, output_file)
|
||||||
|
|
||||||
# Copy the output file to the standard name expected by streamlined generator
|
# Copy the output file to the standard name expected by streamlined generator
|
||||||
import shutil
|
import shutil
|
||||||
if os.path.exists(new_file):
|
if os.path.exists(new_file):
|
||||||
shutil.copy2(new_file, "DESC_IP_MERGED.xlsx")
|
shutil.copy2(new_file, "DESC_IP_MERGED.xlsx")
|
||||||
print(f"Created standard output file: DESC_IP_MERGED.xlsx")
|
print(f"Created standard output file: DESC_IP_MERGED.xlsx")
|
||||||
|
|
||||||
# Add minimal safety sheets for Routines Generator compatibility
|
# Do not append legacy safety/ZONES sheets. Step 2 extracts from DESC_IP and uses zones.json.
|
||||||
print("Adding minimal safety sheets for Routines Generator...")
|
|
||||||
with pd.ExcelWriter("DESC_IP_MERGED.xlsx", mode='a', if_sheet_exists='replace') as writer:
|
except Exception as e:
|
||||||
# Create minimal empty safety sheets that LimitedDataLoader expects
|
print(f"Error occurred during processing: {e}")
|
||||||
# Note: These are minimal empty sheets - the actual safety devices will be extracted from DESC_IP
|
sys.exit(1)
|
||||||
empty_rst = pd.DataFrame(columns=['TAGNAME', 'DESCA', 'IO_PATH', 'TERM'])
|
|
||||||
empty_sto = pd.DataFrame(columns=['TAGNAME', 'DESCA', 'IO_PATH', 'TERM'])
|
if __name__ == "__main__":
|
||||||
empty_epc = pd.DataFrame(columns=['TAGNAME', 'DESCA', 'IO_PATH', 'TERM'])
|
|
||||||
|
|
||||||
# Load zones configuration from zones_config.py
|
|
||||||
print("Loading zones configuration...")
|
|
||||||
try:
|
|
||||||
# Import zones configuration
|
|
||||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../')
|
|
||||||
from zones_config import ZONES_CONFIGS, DEFAULT_ZONES
|
|
||||||
|
|
||||||
# Determine which zones configuration to use based on subsystem
|
|
||||||
if subsystem in ZONES_CONFIGS:
|
|
||||||
zones_config = ZONES_CONFIGS[subsystem]
|
|
||||||
print(f"Using {subsystem} zones configuration")
|
|
||||||
else:
|
|
||||||
zones_config = DEFAULT_ZONES
|
|
||||||
print(f"Using default zones configuration (subsystem {subsystem} not found)")
|
|
||||||
|
|
||||||
# Convert zones configuration to DataFrame format
|
|
||||||
zone_data = []
|
|
||||||
for zone_config in zones_config:
|
|
||||||
zone_data.append({
|
|
||||||
'name': zone_config.get('name', ''),
|
|
||||||
'start': zone_config.get('start', ''),
|
|
||||||
'stop': zone_config.get('stop', ''),
|
|
||||||
'interlock': zone_config.get('interlock', '')
|
|
||||||
})
|
|
||||||
|
|
||||||
zones_df = pd.DataFrame(zone_data)
|
|
||||||
print(f"Loaded {len(zone_data)} zones: {[z['name'] for z in zone_data]}")
|
|
||||||
|
|
||||||
except ImportError as e:
|
|
||||||
print(f"Warning: Could not load zones_config.py ({e}), falling back to minimal MCM zone")
|
|
||||||
# Fallback to minimal MCM zone if zones_config.py is not available
|
|
||||||
zones_df = pd.DataFrame([{
|
|
||||||
'name': 'MCM',
|
|
||||||
'start': 'MCM_S_PB',
|
|
||||||
'stop': 'MCM_EPB_STATUS',
|
|
||||||
'interlock': ''
|
|
||||||
}])
|
|
||||||
|
|
||||||
empty_rst.to_excel(writer, sheet_name='RST', index=False)
|
|
||||||
empty_sto.to_excel(writer, sheet_name='STO', index=False)
|
|
||||||
empty_epc.to_excel(writer, sheet_name='EPC', index=False)
|
|
||||||
zones_df.to_excel(writer, sheet_name='ZONES', index=False)
|
|
||||||
print("Added empty RST, STO, EPC, and configured ZONES sheets")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error occurred during processing: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
create_desc_ip_sheet()
|
create_desc_ip_sheet()
|
||||||
@ -44,7 +44,41 @@ def post_process_io_data(input_file, output_file):
|
|||||||
print(f"Removed {len(rows_to_remove_spare_after_fioh)} SPARE rows following FIOH entries")
|
print(f"Removed {len(rows_to_remove_spare_after_fioh)} SPARE rows following FIOH entries")
|
||||||
df = df.drop(rows_to_remove_spare_after_fioh).reset_index(drop=True)
|
df = df.drop(rows_to_remove_spare_after_fioh).reset_index(drop=True)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Remove SPARE rows for M12DR devices containing VS
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
m12dr_vs_spare_mask = (
|
||||||
|
df['TAGNAME'].astype(str).str.contains(r'M12DR', case=False, na=False) &
|
||||||
|
df['TAGNAME'].astype(str).str.contains(r'VS', case=False, na=False) &
|
||||||
|
((df['DESCA'].astype(str).str.strip().str.upper() == 'SPARE') |
|
||||||
|
(df['DESC'].astype(str).str.strip().str.upper() == 'SPARE'))
|
||||||
|
)
|
||||||
|
removed_m12dr_vs_spare_count = m12dr_vs_spare_mask.sum()
|
||||||
|
if removed_m12dr_vs_spare_count:
|
||||||
|
print(f"Removed {removed_m12dr_vs_spare_count} SPARE rows for M12DR devices containing VS")
|
||||||
|
df = df[~m12dr_vs_spare_mask].reset_index(drop=True)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Remove SPARE rows that directly follow IOLink entries with SOL in DESCA
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
rows_to_remove_spare_after_iolink_sol = []
|
||||||
|
for idx in range(len(df) - 1):
|
||||||
|
try:
|
||||||
|
current_signal = str(df.at[idx, 'SIGNAL']).strip()
|
||||||
|
current_desca = str(df.at[idx, 'DESCA']).strip().upper()
|
||||||
|
next_desc = str(df.at[idx + 1, 'DESC']).strip().upper()
|
||||||
|
|
||||||
|
if (current_signal == 'IOLink' and
|
||||||
|
'SOL' in current_desca and
|
||||||
|
next_desc == 'SPARE'):
|
||||||
|
rows_to_remove_spare_after_iolink_sol.append(idx + 1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Debug: Error at idx {idx}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if rows_to_remove_spare_after_iolink_sol:
|
||||||
|
print(f"Removed {len(rows_to_remove_spare_after_iolink_sol)} SPARE rows following IOLink SOL entries")
|
||||||
|
df = df.drop(rows_to_remove_spare_after_iolink_sol).reset_index(drop=True)
|
||||||
|
|
||||||
# Remove ALL IOLink rows and generate beacon light entries first
|
# Remove ALL IOLink rows and generate beacon light entries first
|
||||||
iolink_mask = df['SIGNAL'] == 'IOLink'
|
iolink_mask = df['SIGNAL'] == 'IOLink'
|
||||||
|
|||||||
@ -1,270 +1,270 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from classifiers import classify_signal, get_device_type
|
from classifiers import classify_signal, get_device_type
|
||||||
from utils import normalize_vfd_name, normalize_io_path, assign_partnumber, combine_desc
|
from utils import normalize_vfd_name, normalize_io_path, assign_partnumber, combine_desc
|
||||||
from io_paths import get_io_path
|
from io_paths import get_io_path
|
||||||
|
|
||||||
def process_data(desc_df, network_df, original_file, apf_df, m12dr_df, hub_df, sorter_hub_df, sio_df, ib16_df, ob16e_df, ib16s_df):
|
def process_data(desc_df, network_df, original_file, apf_df, m12dr_df, hub_df, sorter_hub_df, sio_df, ib16_df, ob16e_df, ib16s_df):
|
||||||
validation_errors = []
|
validation_errors = []
|
||||||
|
|
||||||
# Ignore rows from DESC sheet with an empty or NaN TAGNAME
|
# Ignore rows from DESC sheet with an empty or NaN TAGNAME
|
||||||
original_desc_rows = len(desc_df)
|
original_desc_rows = len(desc_df)
|
||||||
desc_df = desc_df.dropna(subset=['TAGNAME'])
|
desc_df = desc_df.dropna(subset=['TAGNAME'])
|
||||||
desc_df = desc_df[desc_df['TAGNAME'].astype(str).str.strip() != '']
|
desc_df = desc_df[desc_df['TAGNAME'].astype(str).str.strip() != '']
|
||||||
rows_dropped = original_desc_rows - len(desc_df)
|
rows_dropped = original_desc_rows - len(desc_df)
|
||||||
if rows_dropped > 0:
|
if rows_dropped > 0:
|
||||||
print(f"\nNOTE: Ignored {rows_dropped} rows from DESC sheet with an empty TAGNAME.")
|
print(f"\nNOTE: Ignored {rows_dropped} rows from DESC sheet with an empty TAGNAME.")
|
||||||
|
|
||||||
desc_df['NORMALIZED_TAGNAME'] = desc_df['TAGNAME'].apply(normalize_vfd_name)
|
desc_df['NORMALIZED_TAGNAME'] = desc_df['TAGNAME'].apply(normalize_vfd_name)
|
||||||
network_df['NORMALIZED_NAME'] = network_df['Name'].apply(normalize_vfd_name)
|
network_df['NORMALIZED_NAME'] = network_df['Name'].apply(normalize_vfd_name)
|
||||||
|
|
||||||
if network_df['NORMALIZED_NAME'].duplicated().any():
|
if network_df['NORMALIZED_NAME'].duplicated().any():
|
||||||
print("\nWARNING: The following names in the 'NETWORK' sheet become duplicates after normalization (e.g., 'DEV01' and 'DEV1'). Using the first occurrence for IP and PartNumber mapping.")
|
print("\nWARNING: The following names in the 'NETWORK_PLC' sheet become duplicates after normalization (e.g., 'DEV01' and 'DEV1'). Using the first occurrence for IP and PartNumber mapping.")
|
||||||
duplicated_names = network_df[network_df['NORMALIZED_NAME'].duplicated(keep=False)].sort_values('NORMALIZED_NAME')
|
duplicated_names = network_df[network_df['NORMALIZED_NAME'].duplicated(keep=False)].sort_values('NORMALIZED_NAME')
|
||||||
print(duplicated_names[['Name', 'NORMALIZED_NAME']].to_string(index=False))
|
print(duplicated_names[['Name', 'NORMALIZED_NAME']].to_string(index=False))
|
||||||
|
|
||||||
unique_network_df = network_df.drop_duplicates(subset=['NORMALIZED_NAME'], keep='first')
|
unique_network_df = network_df.drop_duplicates(subset=['NORMALIZED_NAME'], keep='first')
|
||||||
network_mapping = dict(zip(unique_network_df['NORMALIZED_NAME'], unique_network_df['IP']))
|
network_mapping = dict(zip(unique_network_df['NORMALIZED_NAME'], unique_network_df['IP']))
|
||||||
part_number_mapping = dict(zip(unique_network_df['NORMALIZED_NAME'], unique_network_df['PartNumber']))
|
part_number_mapping = dict(zip(unique_network_df['NORMALIZED_NAME'], unique_network_df['PartNumber']))
|
||||||
|
|
||||||
# Create a mapping from DPM -> DPM_IP for unique DPM entries
|
# Create a mapping from DPM -> DPM_IP for unique DPM entries
|
||||||
dpm_mapping = dict(zip(network_df['DPM'], network_df['DPM_IP']))
|
dpm_mapping = dict(zip(network_df['DPM'], network_df['DPM_IP']))
|
||||||
|
|
||||||
# Start with DESC data and add IP and PartNumber columns
|
# Start with DESC data and add IP and PartNumber columns
|
||||||
result_df = desc_df.copy()
|
result_df = desc_df.copy()
|
||||||
|
|
||||||
# Fill empty DESCA with "SPARE"
|
# Fill empty DESCA with "SPARE"
|
||||||
result_df['DESCA'] = result_df['DESCA'].fillna('SPARE')
|
result_df['DESCA'] = result_df['DESCA'].fillna('SPARE')
|
||||||
result_df.loc[result_df['DESCA'] == '', 'DESCA'] = 'SPARE'
|
result_df.loc[result_df['DESCA'] == '', 'DESCA'] = 'SPARE'
|
||||||
|
|
||||||
result_df['IP'] = result_df['NORMALIZED_TAGNAME'].map(network_mapping)
|
result_df['IP'] = result_df['NORMALIZED_TAGNAME'].map(network_mapping)
|
||||||
result_df['PARTNUMBER'] = result_df['NORMALIZED_TAGNAME'].map(part_number_mapping)
|
result_df['PARTNUMBER'] = result_df['NORMALIZED_TAGNAME'].map(part_number_mapping)
|
||||||
|
|
||||||
# Add DPM column mapping for FIOM-DPM relationships
|
# Add DPM column mapping for FIOM-DPM relationships
|
||||||
dpm_to_devices_mapping = dict(zip(network_df['NORMALIZED_NAME'], network_df['DPM']))
|
dpm_to_devices_mapping = dict(zip(network_df['NORMALIZED_NAME'], network_df['DPM']))
|
||||||
result_df['DPM'] = result_df['NORMALIZED_TAGNAME'].map(dpm_to_devices_mapping)
|
result_df['DPM'] = result_df['NORMALIZED_TAGNAME'].map(dpm_to_devices_mapping)
|
||||||
|
|
||||||
result_df['PARTNUMBER'] = result_df.apply(assign_partnumber, axis=1)
|
result_df['PARTNUMBER'] = result_df.apply(assign_partnumber, axis=1)
|
||||||
|
|
||||||
result_df['DESC'] = result_df.apply(combine_desc, axis=1)
|
result_df['DESC'] = result_df.apply(combine_desc, axis=1)
|
||||||
|
|
||||||
# Add signal classification and IO path columns
|
# Add signal classification and IO path columns
|
||||||
print("\nClassifying signals and adding IO paths...")
|
print("\nClassifying signals and adding IO paths...")
|
||||||
|
|
||||||
# Add signal classification column
|
# Add signal classification column
|
||||||
result_df['SIGNAL'] = result_df.apply(lambda row: classify_signal(row['DESCA'], row['TAGNAME'], row['DESCB']), axis=1)
|
result_df['SIGNAL'] = result_df.apply(lambda row: classify_signal(row['DESCA'], row['TAGNAME'], row['DESCB']), axis=1)
|
||||||
|
|
||||||
# Add device type column
|
# Add device type column
|
||||||
result_df['DEVICE_TYPE'] = result_df['TAGNAME'].apply(get_device_type)
|
result_df['DEVICE_TYPE'] = result_df['TAGNAME'].apply(get_device_type)
|
||||||
|
|
||||||
# Add IO path column
|
# Add IO path column
|
||||||
result_df['IO_PATH'] = result_df.apply(
|
result_df['IO_PATH'] = result_df.apply(
|
||||||
lambda row: get_io_path(
|
lambda row: get_io_path(
|
||||||
row['TAGNAME'],
|
row['TAGNAME'],
|
||||||
row['TERM'],
|
row['TERM'],
|
||||||
row['SIGNAL'],
|
row['SIGNAL'],
|
||||||
row['DEVICE_TYPE'],
|
row['DEVICE_TYPE'],
|
||||||
apf_df,
|
apf_df,
|
||||||
m12dr_df,
|
m12dr_df,
|
||||||
hub_df,
|
hub_df,
|
||||||
sorter_hub_df,
|
sorter_hub_df,
|
||||||
sio_df,
|
sio_df,
|
||||||
ib16_df,
|
ib16_df,
|
||||||
ob16e_df,
|
ob16e_df,
|
||||||
ib16s_df
|
ib16s_df
|
||||||
), axis=1
|
), axis=1
|
||||||
)
|
)
|
||||||
|
|
||||||
# Print statistics about signal classification
|
# Print statistics about signal classification
|
||||||
signal_counts = result_df['SIGNAL'].value_counts()
|
signal_counts = result_df['SIGNAL'].value_counts()
|
||||||
print(f"\nSignal classification results:")
|
print(f"\nSignal classification results:")
|
||||||
for signal_type, count in signal_counts.items():
|
for signal_type, count in signal_counts.items():
|
||||||
print(f" {signal_type}: {count}")
|
print(f" {signal_type}: {count}")
|
||||||
|
|
||||||
# Show TAGNAMEs classified as UNKNOWN
|
# Show TAGNAMEs classified as UNKNOWN
|
||||||
unknown_entries = result_df[result_df['SIGNAL'] == 'UNKNOWN']
|
unknown_entries = result_df[result_df['SIGNAL'] == 'UNKNOWN']
|
||||||
if len(unknown_entries) > 0:
|
if len(unknown_entries) > 0:
|
||||||
print(f"\nFound {len(unknown_entries)} entries with UNKNOWN signal classification.")
|
print(f"\nFound {len(unknown_entries)} entries with UNKNOWN signal classification.")
|
||||||
for _, row in unknown_entries.iterrows():
|
for _, row in unknown_entries.iterrows():
|
||||||
desca = row['DESCA'] if pd.notna(row['DESCA']) and row['DESCA'] != '' else 'N/A'
|
desca = row['DESCA'] if pd.notna(row['DESCA']) and row['DESCA'] != '' else 'N/A'
|
||||||
validation_errors.append(f"Signal UNKNOWN for TAGNAME: {row['TAGNAME']} (DESCA: '{desca}')")
|
validation_errors.append(f"Signal UNKNOWN for TAGNAME: {row['TAGNAME']} (DESCA: '{desca}')")
|
||||||
|
|
||||||
device_counts = result_df['DEVICE_TYPE'].value_counts()
|
device_counts = result_df['DEVICE_TYPE'].value_counts()
|
||||||
print(f"\nDevice type distribution:")
|
print(f"\nDevice type distribution:")
|
||||||
for device_type, count in device_counts.items():
|
for device_type, count in device_counts.items():
|
||||||
print(f" {device_type}: {count}")
|
print(f" {device_type}: {count}")
|
||||||
|
|
||||||
# Count successful IO path mappings
|
# Count successful IO path mappings
|
||||||
successful_mappings = result_df['IO_PATH'].notna().sum()
|
successful_mappings = result_df['IO_PATH'].notna().sum()
|
||||||
total_rows = len(result_df)
|
total_rows = len(result_df)
|
||||||
print(f"\nIO Path mapping results:")
|
print(f"\nIO Path mapping results:")
|
||||||
print(f" Successful mappings: {successful_mappings}/{total_rows} ({successful_mappings/total_rows*100:.1f}%)")
|
print(f" Successful mappings: {successful_mappings}/{total_rows} ({successful_mappings/total_rows*100:.1f}%)")
|
||||||
|
|
||||||
# Log missing mappings for debugging
|
# Log missing mappings for debugging
|
||||||
missing_mappings = result_df[result_df['IO_PATH'].isna() & result_df['TERM'].notna()]
|
missing_mappings = result_df[result_df['IO_PATH'].isna() & result_df['TERM'].notna()]
|
||||||
if len(missing_mappings) > 0:
|
if len(missing_mappings) > 0:
|
||||||
print(f"\nFound {len(missing_mappings)} entries with missing IO path mappings.")
|
print(f"\nFound {len(missing_mappings)} entries with missing IO path mappings.")
|
||||||
for _, row in missing_mappings.iterrows():
|
for _, row in missing_mappings.iterrows():
|
||||||
tag, term, sig, dev = row['TAGNAME'], row['TERM'], row['SIGNAL'], row['DEVICE_TYPE']
|
tag, term, sig, dev = row['TAGNAME'], row['TERM'], row['SIGNAL'], row['DEVICE_TYPE']
|
||||||
desca = row['DESCA'] if pd.notna(row['DESCA']) and row['DESCA'] != '' else 'N/A'
|
desca = row['DESCA'] if pd.notna(row['DESCA']) and row['DESCA'] != '' else 'N/A'
|
||||||
error_msg = f"On device '{tag}' ({dev}), cannot connect signal '{sig}' to channel number '{term}'. (DESCA: '{desca}')"
|
error_msg = f"On device '{tag}' ({dev}), cannot connect signal '{sig}' to channel number '{term}'. (DESCA: '{desca}')"
|
||||||
validation_errors.append(error_msg)
|
validation_errors.append(error_msg)
|
||||||
|
|
||||||
# Convert failed mappings to SPARE
|
# Convert failed mappings to SPARE
|
||||||
idx = result_df[(result_df['TAGNAME'] == tag) & (result_df['TERM'] == term)].index
|
idx = result_df[(result_df['TAGNAME'] == tag) & (result_df['TERM'] == term)].index
|
||||||
if len(idx) > 0:
|
if len(idx) > 0:
|
||||||
result_df.loc[idx, 'DESCA'] = 'SPARE'
|
result_df.loc[idx, 'DESCA'] = 'SPARE'
|
||||||
result_df.loc[idx, 'SIGNAL'] = 'SPARE'
|
result_df.loc[idx, 'SIGNAL'] = 'SPARE'
|
||||||
# Try to get SPARE path
|
# Try to get SPARE path
|
||||||
spare_path = get_io_path(
|
spare_path = get_io_path(
|
||||||
tag,
|
tag,
|
||||||
term,
|
term,
|
||||||
'SPARE',
|
'SPARE',
|
||||||
dev,
|
dev,
|
||||||
apf_df,
|
apf_df,
|
||||||
m12dr_df,
|
m12dr_df,
|
||||||
hub_df,
|
hub_df,
|
||||||
sorter_hub_df,
|
sorter_hub_df,
|
||||||
sio_df,
|
sio_df,
|
||||||
ib16_df,
|
ib16_df,
|
||||||
ob16e_df,
|
ob16e_df,
|
||||||
ib16s_df
|
ib16s_df
|
||||||
)
|
)
|
||||||
result_df.loc[idx, 'IO_PATH'] = spare_path
|
result_df.loc[idx, 'IO_PATH'] = spare_path
|
||||||
|
|
||||||
# Log names in DESC but not in NETWORK (using normalized names for comparison)
|
# Log names in DESC but not in NETWORK (using normalized names for comparison)
|
||||||
desc_names_norm = set(desc_df['NORMALIZED_TAGNAME'].unique())
|
desc_names_norm = set(desc_df['NORMALIZED_TAGNAME'].unique())
|
||||||
network_names_norm = set(unique_network_df['NORMALIZED_NAME'].unique())
|
network_names_norm = set(unique_network_df['NORMALIZED_NAME'].unique())
|
||||||
dpm_names = set(network_df['DPM'].unique())
|
dpm_names = set(network_df['DPM'].unique())
|
||||||
|
|
||||||
missing_in_network_norm = desc_names_norm - network_names_norm
|
missing_in_network_norm = desc_names_norm - network_names_norm
|
||||||
if missing_in_network_norm:
|
if missing_in_network_norm:
|
||||||
# Get the original tagnames that are missing to report them
|
# Get the original tagnames that are missing to report them
|
||||||
missing_desc_rows = desc_df[desc_df['NORMALIZED_TAGNAME'].isin(missing_in_network_norm)]
|
missing_desc_rows = desc_df[desc_df['NORMALIZED_TAGNAME'].isin(missing_in_network_norm)]
|
||||||
original_missing_tagnames = set(missing_desc_rows['TAGNAME'].unique())
|
original_missing_tagnames = set(missing_desc_rows['TAGNAME'].unique())
|
||||||
|
|
||||||
# Filter out FIOH-containing names for logging
|
# Filter out FIOH-containing names for logging
|
||||||
missing_in_network_filtered = {name for name in original_missing_tagnames if 'FIOH' not in str(name).upper()}
|
missing_in_network_filtered = {name for name in original_missing_tagnames if 'FIOH' not in str(name).upper()}
|
||||||
if missing_in_network_filtered:
|
if missing_in_network_filtered:
|
||||||
print(f"\nFound {len(missing_in_network_filtered)} TAGNAMEs present in DESC but not in NETWORK.")
|
print(f"\nFound {len(missing_in_network_filtered)} TAGNAMEs present in DESC but not in NETWORK_PLC.")
|
||||||
for name in sorted(list(missing_in_network_filtered)):
|
for name in sorted(list(missing_in_network_filtered)):
|
||||||
validation_errors.append(f"TAGNAME '{name}' from DESC sheet not found in NETWORK sheet.")
|
validation_errors.append(f"TAGNAME '{name}' from DESC sheet not found in NETWORK_PLC sheet.")
|
||||||
|
|
||||||
# Add names from NETWORK that are missing in DESC
|
# Add names from NETWORK_PLC that are missing in DESC
|
||||||
missing_in_desc_norm = network_names_norm - desc_names_norm
|
missing_in_desc_norm = network_names_norm - desc_names_norm
|
||||||
if missing_in_desc_norm:
|
if missing_in_desc_norm:
|
||||||
# Get the original rows from network_df to add, ensuring uniqueness
|
# Get the original rows from network_df to add, ensuring uniqueness
|
||||||
rows_to_add = unique_network_df[unique_network_df['NORMALIZED_NAME'].isin(missing_in_desc_norm)]
|
rows_to_add = unique_network_df[unique_network_df['NORMALIZED_NAME'].isin(missing_in_desc_norm)]
|
||||||
|
|
||||||
new_rows = []
|
new_rows = []
|
||||||
for _, row in rows_to_add.iterrows():
|
for _, row in rows_to_add.iterrows():
|
||||||
new_rows.append({
|
new_rows.append({
|
||||||
'TAGNAME': row['Name'], # Use original name
|
'TAGNAME': row['Name'], # Use original name
|
||||||
'TERM': '', 'DESCA': '', 'DESCB': '',
|
'TERM': '', 'DESCA': '', 'DESCB': '',
|
||||||
'IP': row['IP'], 'PARTNUMBER': row['PartNumber'], 'DESC': '',
|
'IP': row['IP'], 'PARTNUMBER': row['PartNumber'], 'DESC': '',
|
||||||
'DPM': row.get('DPM', '') # Include DPM relationship
|
'DPM': row.get('DPM', '') # Include DPM relationship
|
||||||
})
|
})
|
||||||
|
|
||||||
if new_rows:
|
if new_rows:
|
||||||
new_rows_df = pd.DataFrame(new_rows)
|
new_rows_df = pd.DataFrame(new_rows)
|
||||||
result_df = pd.concat([result_df, new_rows_df], ignore_index=True)
|
result_df = pd.concat([result_df, new_rows_df], ignore_index=True)
|
||||||
|
|
||||||
# Get all existing tagnames (including those just added)
|
# Get all existing tagnames (including those just added)
|
||||||
all_existing_names = set(result_df['TAGNAME'].unique())
|
all_existing_names = set(result_df['TAGNAME'].unique())
|
||||||
|
|
||||||
# Add unique DPM names that are not already in the result
|
# Add unique DPM names that are not already in the result
|
||||||
missing_dpm_names = dpm_names - all_existing_names
|
missing_dpm_names = dpm_names - all_existing_names
|
||||||
|
|
||||||
if missing_dpm_names:
|
if missing_dpm_names:
|
||||||
print(f"\nAdding unique DPM names not present in DESC or NETWORK.Name: {sorted(missing_dpm_names)}")
|
print(f"\nAdding unique DPM names not present in DESC or NETWORK_PLC.Name: {sorted(missing_dpm_names)}")
|
||||||
# Create rows for missing DPM names
|
# Create rows for missing DPM names
|
||||||
dpm_rows = []
|
dpm_rows = []
|
||||||
for dpm_name in missing_dpm_names:
|
for dpm_name in missing_dpm_names:
|
||||||
dpm_ip = dpm_mapping[dpm_name]
|
dpm_ip = dpm_mapping[dpm_name]
|
||||||
dpm_rows.append({
|
dpm_rows.append({
|
||||||
'TAGNAME': dpm_name,
|
'TAGNAME': dpm_name,
|
||||||
'TERM': '',
|
'TERM': '',
|
||||||
'DESCA': '',
|
'DESCA': '',
|
||||||
'DESCB': '',
|
'DESCB': '',
|
||||||
'IP': dpm_ip,
|
'IP': dpm_ip,
|
||||||
'PARTNUMBER': '', # DPM entries don't have part numbers
|
'PARTNUMBER': '', # DPM entries don't have part numbers
|
||||||
'DESC': '',
|
'DESC': '',
|
||||||
'DPM': dpm_name # DPM devices reference themselves
|
'DPM': dpm_name # DPM devices reference themselves
|
||||||
})
|
})
|
||||||
|
|
||||||
# Append DPM rows
|
# Append DPM rows
|
||||||
dpm_rows_df = pd.DataFrame(dpm_rows)
|
dpm_rows_df = pd.DataFrame(dpm_rows)
|
||||||
result_df = pd.concat([result_df, dpm_rows_df], ignore_index=True)
|
result_df = pd.concat([result_df, dpm_rows_df], ignore_index=True)
|
||||||
|
|
||||||
# Apply part number assignment to newly added DPM entries
|
# Apply part number assignment to newly added DPM entries
|
||||||
mask = result_df['TAGNAME'].isin(missing_dpm_names)
|
mask = result_df['TAGNAME'].isin(missing_dpm_names)
|
||||||
result_df.loc[mask, 'PARTNUMBER'] = result_df.loc[mask].apply(assign_partnumber, axis=1)
|
result_df.loc[mask, 'PARTNUMBER'] = result_df.loc[mask].apply(assign_partnumber, axis=1)
|
||||||
|
|
||||||
# Check for validation errors and terminate if any are found
|
# Check for validation errors and terminate if any are found
|
||||||
if validation_errors:
|
if validation_errors:
|
||||||
print("\n" + "="*80)
|
print("\n" + "="*80)
|
||||||
print("WARNING: The following issues were found but processing will continue:")
|
print("WARNING: The following issues were found but processing will continue:")
|
||||||
print("="*80)
|
print("="*80)
|
||||||
for error in validation_errors:
|
for error in validation_errors:
|
||||||
print(f"- {error}")
|
print(f"- {error}")
|
||||||
print("="*80)
|
print("="*80)
|
||||||
print("\nContinuing with processing...")
|
print("\nContinuing with processing...")
|
||||||
|
|
||||||
# Normalize TAGNAME, DESC, and IO_PATH in the final result before saving (only for VFDs)
|
# Normalize TAGNAME, DESC, and IO_PATH in the final result before saving (only for VFDs)
|
||||||
print("\nNormalizing TAGNAME, DESC, and IO_PATH columns for VFDs only in the final output...")
|
print("\nNormalizing TAGNAME, DESC, and IO_PATH columns for VFDs only in the final output...")
|
||||||
result_df['TAGNAME'] = result_df['TAGNAME'].apply(normalize_vfd_name)
|
result_df['TAGNAME'] = result_df['TAGNAME'].apply(normalize_vfd_name)
|
||||||
result_df['DESC'] = result_df['DESC'].apply(normalize_vfd_name)
|
result_df['DESC'] = result_df['DESC'].apply(normalize_vfd_name)
|
||||||
result_df['IO_PATH'] = result_df['IO_PATH'].apply(normalize_io_path)
|
result_df['IO_PATH'] = result_df['IO_PATH'].apply(normalize_io_path)
|
||||||
|
|
||||||
# Drop the temporary normalized column before sorting
|
# Drop the temporary normalized column before sorting
|
||||||
if 'NORMALIZED_TAGNAME' in result_df.columns:
|
if 'NORMALIZED_TAGNAME' in result_df.columns:
|
||||||
result_df = result_df.drop(columns=['NORMALIZED_TAGNAME'])
|
result_df = result_df.drop(columns=['NORMALIZED_TAGNAME'])
|
||||||
|
|
||||||
# Convert TAGNAME to string to prevent sorting errors with mixed types
|
# Convert TAGNAME to string to prevent sorting errors with mixed types
|
||||||
result_df['TAGNAME'] = result_df['TAGNAME'].astype(str)
|
result_df['TAGNAME'] = result_df['TAGNAME'].astype(str)
|
||||||
|
|
||||||
# Sort by PARTNUMBER, TAGNAME, and then TERM for better organization
|
# Sort by PARTNUMBER, TAGNAME, and then TERM for better organization
|
||||||
# Fill NaN values to ensure consistent sorting
|
# Fill NaN values to ensure consistent sorting
|
||||||
result_df['PARTNUMBER'] = result_df['PARTNUMBER'].fillna('')
|
result_df['PARTNUMBER'] = result_df['PARTNUMBER'].fillna('')
|
||||||
result_df['TERM'] = result_df['TERM'].fillna('')
|
result_df['TERM'] = result_df['TERM'].fillna('')
|
||||||
result_df = result_df.sort_values(by=['PARTNUMBER', 'TAGNAME', 'TERM']).reset_index(drop=True)
|
result_df = result_df.sort_values(by=['PARTNUMBER', 'TAGNAME', 'TERM']).reset_index(drop=True)
|
||||||
|
|
||||||
# Remove exact duplicate rows before exporting to Excel/CSV
|
# Remove exact duplicate rows before exporting to Excel/CSV
|
||||||
before_dedup = len(result_df)
|
before_dedup = len(result_df)
|
||||||
result_df = result_df.drop_duplicates()
|
result_df = result_df.drop_duplicates()
|
||||||
after_dedup = len(result_df)
|
after_dedup = len(result_df)
|
||||||
if before_dedup != after_dedup:
|
if before_dedup != after_dedup:
|
||||||
print(f"\nRemoved {before_dedup - after_dedup} duplicate rows (exact matches) before saving DESC_IP sheet.")
|
print(f"\nRemoved {before_dedup - after_dedup} duplicate rows (exact matches) before saving DESC_IP sheet.")
|
||||||
|
|
||||||
print(f"\nFinal result has {len(result_df)} rows")
|
print(f"\nFinal result has {len(result_df)} rows")
|
||||||
print("Sample of merged data:")
|
print("Sample of merged data:")
|
||||||
print(result_df.head(10))
|
print(result_df.head(10))
|
||||||
|
|
||||||
# Create new Excel file name derived from the subsystem (e.g. MCM04)
|
# Create new Excel file name derived from the subsystem (e.g. MCM04)
|
||||||
subsystem_match = re.search(r"(MCM\d+)", str(original_file), re.IGNORECASE)
|
subsystem_match = re.search(r"(MCM\d+)", str(original_file), re.IGNORECASE)
|
||||||
subsystem = subsystem_match.group(1).upper() if subsystem_match else "MCM"
|
subsystem = subsystem_match.group(1).upper() if subsystem_match else "MCM"
|
||||||
|
|
||||||
new_file = f"{subsystem}_DESC_IP_MERGED.xlsx"
|
new_file = f"{subsystem}_DESC_IP_MERGED.xlsx"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Copy all original sheets and add the new one
|
# Copy all original sheets and add the new one
|
||||||
with pd.ExcelWriter(new_file, engine='openpyxl') as writer:
|
with pd.ExcelWriter(new_file, engine='openpyxl') as writer:
|
||||||
# Copy original sheets
|
# Copy original sheets
|
||||||
xl_original = pd.ExcelFile(original_file)
|
xl_original = pd.ExcelFile(original_file)
|
||||||
for sheet_name in xl_original.sheet_names:
|
for sheet_name in xl_original.sheet_names:
|
||||||
df = pd.read_excel(xl_original, sheet_name=sheet_name)
|
df = pd.read_excel(xl_original, sheet_name=sheet_name)
|
||||||
df.to_excel(writer, sheet_name=sheet_name, index=False)
|
df.to_excel(writer, sheet_name=sheet_name, index=False)
|
||||||
|
|
||||||
# Add the new merged sheet
|
# Add the new merged sheet
|
||||||
result_df.to_excel(writer, sheet_name='DESC_IP', index=False)
|
result_df.to_excel(writer, sheet_name='DESC_IP', index=False)
|
||||||
|
|
||||||
print(f"\nNew Excel file created: {new_file}")
|
print(f"\nNew Excel file created: {new_file}")
|
||||||
print("The file contains all original sheets plus the new 'DESC_IP' sheet with merged data.")
|
print("The file contains all original sheets plus the new 'DESC_IP' sheet with merged data.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If that fails, just save the merged data alone
|
# If that fails, just save the merged data alone
|
||||||
result_df.to_excel(new_file, sheet_name='DESC_IP', index=False)
|
result_df.to_excel(new_file, sheet_name='DESC_IP', index=False)
|
||||||
print(f"\nNew Excel file created with merged data only: {new_file}")
|
print(f"\nNew Excel file created with merged data only: {new_file}")
|
||||||
Binary file not shown.
@ -1 +1 @@
|
|||||||
MCM_S_PB=SFT_MCM_S_PB, PS1_1_S1_PB=SFT_PS1_1_S1_PB, PS1_1_S2_PB=SFT_PS1_1_S2_PB, PS1_5_S1_PB=SFT_PS1_5_S1_PB, PS1_5_S2_PB=SFT_PS1_5_S2_PB, PS2_1_S1_PB=SFT_PS2_1_S1_PB, PS2_1_S2_PB=SFT_PS2_1_S2_PB, PS2_6_S1_PB=SFT_PS2_6_S1_PB, PS2_6_S2_PB=SFT_PS2_6_S2_PB, PS3_12_S1_PB=SFT_PS3_12_S1_PB, PS3_12_S2_PB=SFT_PS3_12_S2_PB, PS3_1_S1_PB=SFT_PS3_1_S1_PB, PS3_1_S2_PB=SFT_PS3_1_S2_PB, PS4_14_S1_PB=SFT_PS4_14_S1_PB, PS4_14_S2_PB=SFT_PS4_14_S2_PB, PS4_1_S1_PB=SFT_PS4_1_S1_PB, PS4_1_S2_PB=SFT_PS4_1_S2_PB, UL10_1_SS1_SPB=SFT_UL10_1_SS1_SPB, UL10_3_S1_PB=SFT_UL10_3_S1_PB, UL10_3_S2_PB=SFT_UL10_3_S2_PB, UL11_3_S1_PB=SFT_UL11_3_S1_PB, UL11_3_S2_PB=SFT_UL11_3_S2_PB, UL11_4_S1_PB=SFT_UL11_4_S1_PB, UL11_4_S2_PB=SFT_UL11_4_S2_PB, UL11_9_S1_PB=SFT_UL11_9_S1_PB, UL11_9_S2_PB=SFT_UL11_9_S2_PB, UL12_3_S1_PB=SFT_UL12_3_S1_PB, UL12_3_S2_PB=SFT_UL12_3_S2_PB, UL12_4_S1_PB=SFT_UL12_4_S1_PB, UL12_4_S2_PB=SFT_UL12_4_S2_PB, UL1_3_S1_PB=SFT_UL1_3_S1_PB, UL1_3_S2_PB=SFT_UL1_3_S2_PB, UL1_4_S1_PB=SFT_UL1_4_S1_PB, UL1_4_S2_PB=SFT_UL1_4_S2_PB, UL1_9_S1_PB=SFT_UL1_9_S1_PB, UL1_9_S2_PB=SFT_UL1_9_S2_PB, UL2_3_S1_PB=SFT_UL2_3_S1_PB, UL2_3_S2_PB=SFT_UL2_3_S2_PB, UL2_4_S1_PB=SFT_UL2_4_S1_PB, UL2_4_S2_PB=SFT_UL2_4_S2_PB, UL3_1_SS1_SPB=SFT_UL3_1_SS1_SPB, UL3_2_S1_PB=SFT_UL3_2_S1_PB, UL3_2_S2_PB=SFT_UL3_2_S2_PB, UL4_3_S1_PB=SFT_UL4_3_S1_PB, UL4_3_S2_PB=SFT_UL4_3_S2_PB, UL4_4_S1_PB=SFT_UL4_4_S1_PB, UL4_4_S2_PB=SFT_UL4_4_S2_PB, UL4_9_S1_PB=SFT_UL4_9_S1_PB, UL4_9_S2_PB=SFT_UL4_9_S2_PB, UL5_3_S1_PB=SFT_UL5_3_S1_PB, UL5_3_S2_PB=SFT_UL5_3_S2_PB, UL5_4_S1_PB=SFT_UL5_4_S1_PB, UL5_4_S2_PB=SFT_UL5_4_S2_PB, UL6_1_SS1_SPB=SFT_UL6_1_SS1_SPB, UL6_2_S1_PB=SFT_UL6_2_S1_PB, UL6_2_S2_PB=SFT_UL6_2_S2_PB, UL7_3_S1_PB=SFT_UL7_3_S1_PB, UL7_3_S2_PB=SFT_UL7_3_S2_PB, UL7_4_S1_PB=SFT_UL7_4_S1_PB, UL7_4_S2_PB=SFT_UL7_4_S2_PB, UL7_9_S1_PB=SFT_UL7_9_S1_PB, UL7_9_S2_PB=SFT_UL7_9_S2_PB, UL8_1_SS1_SPB=SFT_UL8_1_SS1_SPB, UL8_3_S1_PB=SFT_UL8_3_S1_PB, UL8_3_S2_PB=SFT_UL8_3_S2_PB, UL9_3_S1_PB=SFT_UL9_3_S1_PB, UL9_3_S2_PB=SFT_UL9_3_S2_PB, UL9_4_S1_PB=SFT_UL9_4_S1_PB, UL9_4_S2_PB=SFT_UL9_4_S2_PB
|
MCM_S_PB=SFT_MCM_S_PB, PS5_1_S1_PB=SFT_PS5_1_S1_PB, PS5_1_S2_PB=SFT_PS5_1_S2_PB, PS6_1_S1_PB=SFT_PS6_1_S1_PB, PS6_1_S2_PB=SFT_PS6_1_S2_PB, PS7_1_S1_PB=SFT_PS7_1_S1_PB, PS7_1_S2_PB=SFT_PS7_1_S2_PB, PS7_9_S1_PB=SFT_PS7_9_S1_PB, PS7_9_S2_PB=SFT_PS7_9_S2_PB, UL13_1_SS1_SPB=SFT_UL13_1_SS1_SPB, UL13_4_S1_PB=SFT_UL13_4_S1_PB, UL13_4_S2_PB=SFT_UL13_4_S2_PB, UL13_8_S1_PB=SFT_UL13_8_S1_PB, UL13_8_S2_PB=SFT_UL13_8_S2_PB, UL14_3_S1_PB=SFT_UL14_3_S1_PB, UL14_3_S2_PB=SFT_UL14_3_S2_PB, UL14_4_S1_PB=SFT_UL14_4_S1_PB, UL14_4_S2_PB=SFT_UL14_4_S2_PB, UL15_3_S1_PB=SFT_UL15_3_S1_PB, UL15_3_S2_PB=SFT_UL15_3_S2_PB, UL15_4_S1_PB=SFT_UL15_4_S1_PB, UL15_4_S2_PB=SFT_UL15_4_S2_PB, UL16_1_SS1_SPB=SFT_UL16_1_SS1_SPB, UL16_2_S1_PB=SFT_UL16_2_S1_PB, UL16_2_S2_PB=SFT_UL16_2_S2_PB, UL17_3_S1_PB=SFT_UL17_3_S1_PB, UL17_3_S2_PB=SFT_UL17_3_S2_PB, UL17_4_S1_PB=SFT_UL17_4_S1_PB, UL17_4_S2_PB=SFT_UL17_4_S2_PB, UL18_10_S1_PB=SFT_UL18_10_S1_PB, UL18_10_S2_PB=SFT_UL18_10_S2_PB, UL18_3_S1_PB=SFT_UL18_3_S1_PB, UL18_3_S2_PB=SFT_UL18_3_S2_PB, UL18_4_S1_PB=SFT_UL18_4_S1_PB, UL18_4_S2_PB=SFT_UL18_4_S2_PB, UL19_1_SS1_SPB=SFT_UL19_1_SS1_SPB, UL19_2_S1_PB=SFT_UL19_2_S1_PB, UL19_2_S2_PB=SFT_UL19_2_S2_PB, UL20_3_S1_PB=SFT_UL20_3_S1_PB, UL20_3_S2_PB=SFT_UL20_3_S2_PB, UL20_4_S1_PB=SFT_UL20_4_S1_PB, UL20_4_S2_PB=SFT_UL20_4_S2_PB, UL21_11_S1_PB=SFT_UL21_11_S1_PB, UL21_11_S2_PB=SFT_UL21_11_S2_PB, UL21_13_S1_PB=SFT_UL21_13_S1_PB, UL21_13_S2_PB=SFT_UL21_13_S2_PB, UL21_3_S1_PB=SFT_UL21_3_S1_PB, UL21_3_S2_PB=SFT_UL21_3_S2_PB, UL21_4_S1_PB=SFT_UL21_4_S1_PB, UL21_4_S2_PB=SFT_UL21_4_S2_PB
|
||||||
@ -1,148 +0,0 @@
|
|||||||
<Tag Name="PS1_3_VFD1" Class="Standard" TagType="Base" DataType="UDT_AOI_APF" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[17,0.00000000e+000,0,[0,5000,0],0.00000000e+000,[0,2000,0],0,[0,0,0],0,[9,' STARTING$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[8,' VFD FLT$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[9,' SAFE OFF$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[8,' Running$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[10,' LOST COMM$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,[4,' JAM$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[5,' FULL$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[9,' DISC OFF$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,45000,0],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,0,0.00000000e+000,0,0],[[0],[0,0,0],0,0,0,0,0,0,0,0,0,0,0,0,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,0],[0,0,0,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_APF">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_APF">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disconnect_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Running" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Run_Enabled" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="Run" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Maintenance_Running" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Speed_Not_Set_Up" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Fault_Reset_Required" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disconnected" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Saving_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_Conveyor" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Speed_To_REV_Ratio" DataType="REAL" Radix="Float" Value="0.0"/>
|
|
||||||
<DataValueMember Name="Maintenance_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped_By_Station_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Encoder_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped_Require_Start" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped_Auto_Restart" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Zero_Speed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="No_Interlock" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="MCM_Not_Started" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Length" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Horn_O" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_VFD">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_VFD">
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disconnected" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Reset_Required" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Safe_Off_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Maintenance_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="Maintenance" DataType="UDT_MAINTENANCE_VFD">
|
|
||||||
<DataValueMember Name="Maintenance_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jog_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Direction_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Direction" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Speed_FPM" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Buttons_State" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Speed_At_30rev" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Amperage" DataType="INT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Voltage" DataType="INT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Velocity" DataType="INT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="FPM" DataType="INT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Cycle_Time_Factor" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Fault_Code" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Fault_Code" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Last_VFD_Fault_Code" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Type" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Speed_At_60Hz" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Frequency" DataType="INT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_VFD">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_VFD_CMD">
|
|
||||||
<DataValueMember Name="Start" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_Require_Start" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_Auto_Restart" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disable" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Run_Up_Enabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Saving_Mode_ON" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Interlock" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped_By_Stop_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="Acknowledge_Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Encoder_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Speed_FPM" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_VFD_STS">
|
|
||||||
<DataValueMember Name="Running" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Interlock" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Current_Speed" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Requested_Speed" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="In_Power_Saving_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Maintenance_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="MCM_Started" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Enabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped_By_Station_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Encoder_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="MCM_JR_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="MCM_Start_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="MCM_EStop_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
<Tag Name="PDP1_CB_MONITOR" Class="Standard" TagType="Base" DataType="UDT_AOI_CB_MONITOR" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1],[[0,0,0,0]],[[0],[0,0,0,0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_CB_MONITOR">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_CB_MONITOR">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="IO_Block_Comm_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB1_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB2_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB3_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB4_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB5_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB6_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB7_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB8_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB9_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB10_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB11_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB12_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB13_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB14_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB15_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB16_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB17_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB18_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB19_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB20_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB21_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB22_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB23_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB24_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB25_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB26_I" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_CB_MONITOR">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_CB_MONITOR">
|
|
||||||
<DataValueMember Name="Power_Branch_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB1_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB2_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB3_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB4_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB5_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB6_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB7_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB8_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB9_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB10_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB11_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB12_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB13_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB14_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB15_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB16_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB17_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB18_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB19_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB20_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB21_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB22_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB23_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB24_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB25_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB26_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_CB_MONITOR">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_CB_MONITOR_CMD">
|
|
||||||
<DataValueMember Name="Power_Branch_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_CB_MONITOR_STS">
|
|
||||||
<DataValueMember Name="Power_Branch_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB1_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB2_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB3_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB4_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB5_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB6_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB7_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB8_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB9_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB10_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB11_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB12_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB13_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB14_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB15_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB16_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB17_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB18_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB19_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB20_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB21_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB22_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB23_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB24_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB25_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="CB26_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
<Tag Name="S011003_D2C_CHUTE" Class="Standard" TagType="Base" DataType="UDT_AOI_D2C_CHUTE" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[4194305,0,0,0,0,[1,0,0,[0,0,0],[0,0,0]],[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0
|
|
||||||
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[0,0,0,0,0,0,0,0]],0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],1,[1,0,0,0,[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
||||||
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,3000,0],[0,5000,0],[0,0]],[0,0,0],[0,0,0]],[0,[0],0],[[0],[0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_D2C_CHUTE">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_D2C_CHUTE">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="GS_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="GS_PB_Light_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="G_Beacon_Segment_Color_O" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="G_Beacon_Segment_Animation_O" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="B_Beacon_Segment_Color_O" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="B_Beacon_Segment_Animation_O" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Busy" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Done" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Bin_Error" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Cyclic_Error" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Comms_Error" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Cart_Present" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Short_Shuttle_Present" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Tall_Shuttle_Present" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Ready" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_D2C_CHUTE">
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="Alarms" DataType="UDT_ALARMS_D2C_CHUTE">
|
|
||||||
<DataValueMember Name="GS_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="No_Container" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disabled" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="GS_PB" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_D2C_CHUTE">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_D2C_CHUTE_CMD">
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_D2C_CHUTE_STS">
|
|
||||||
<DataValueMember Name="Enabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Ready" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
<Tag Name="PS1_4_DPM1" Class="Standard" TagType="Base" DataType="UDT_AOI_DPM" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,[0,2000,804],[0,10000,0],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0],[[0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0]],[[0],0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_DPM">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_DPM">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EN4TR_Communication_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_IO_BLOCK">
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_IO_BLOCK_STS">
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Acknowledge_Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Unacknowledge_All_Logs" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Unacknowldge_All" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Aknowledgement_ID" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_IO_BLOCK_CMD">
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Log_Quantity" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_IO_BLOCK">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_IO_BLOCK">
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="IP_Address" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
<Tag Name="UL11_1_EX1" Class="Standard" TagType="Base" DataType="UDT_AOI_EXTENDO" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,0,[10,' LOST COMM$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[6,' FULL$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],[0]],[[0],0,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_EXTENDO">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_EXTENDO">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="DPM_Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Extendo_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Extendo_Faulted_Reset_Required" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Estopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Saving_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="No_Interlock" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="MCM_Not_Started" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_EXTENDO">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_EXTENDO_CMD">
|
|
||||||
<DataValueMember Name="Interlock" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_EXTENDO_STS">
|
|
||||||
<DataValueMember Name="Interlock" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Allow_Loading" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Running" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_EXTENDO">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_EXTENDO">
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Extendo_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Loading_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Unloading_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Aopen_Active" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Saving_Mode" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Fully_Retracted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Fully_Extended" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="PEC_Override_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Extendo_Faults_Reset_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
<Tag Name="UL1_13_FIO1" Class="Standard" TagType="Base" DataType="UDT_AOI_IO_BLOCK" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[19,' Lost Communication$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_IO_BLOCK">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_IO_BLOCK">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_IO_BLOCK">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_IO_BLOCK">
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="IP_Address" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_IO_BLOCK">
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_IO_BLOCK_STS">
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Acknowledge_Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Unacknowledge_All_Logs" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Unacknowldge_All" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Aknowledgement_ID" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_IO_BLOCK_CMD">
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Log_Quantity" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
<Tag Name="FL1018_3CH_PE1" Class="Standard" TagType="Base" DataType="UDT_AOI_FPE" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,[0,0,0],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[5,' FULL$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],0,[14,'FL1018_3CH_PE1$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],[0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_FPE">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_FPE">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Parent_Comm_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Clear_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Blocked" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_PE">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_PE">
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="TimeOut" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="14"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_PE">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_PE_CMD">
|
|
||||||
<DataValueMember Name="Dynamic_Jam" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_PE_STS">
|
|
||||||
<DataValueMember Name="Blocked" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Chute_Roller_PE" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
<Tag Name="FL1014_2_PE1" Class="Standard" TagType="Base" DataType="UDT_AOI_JPE" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,[0,0,0],[1,0,0,[0,0,0],[0,0,0]],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[4,' Jam$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,0,0],[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],0,[12,'FL1014_2_PE1$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],[0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_JPE">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_JPE">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Parent_Comm_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Clear_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Blocked" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Run_Up_PE" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Run_Up" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_PE">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_PE">
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="TimeOut" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="12"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA['FL1014_2_PE1']]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_PE">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_PE_CMD">
|
|
||||||
<DataValueMember Name="Dynamic_Jam" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_PE_STS">
|
|
||||||
<DataValueMember Name="Blocked" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Chute_Roller_PE" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
<Tag Name="MCM" Class="Standard" TagType="Base" DataType="UDT_AOI_MCM" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,0,[1,0,0,[0,0,0],[0,0,0]],[1,0,0,[0,0,0],[0,0,0]],[1,0,0,[0,0,0],[0,0,0]],[1,0,0,[0,0,0],[0,0,0]],[1,0,0,[0,0,0],[0,0,0]]],[[0,0,0],0,0,0,0,0,0,0,0],[[[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,0,0],[[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0,0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_MCM">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_MCM">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault_Reset_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Reset_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Restart_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_PB_I_CH1" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_PB_I_CH2" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Fire_Relay_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="On_UPS_Battery_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_Low_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_Fault_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="NAT_Switch_Fault_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault_Reset_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Reset_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Actuated_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Restart_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Was_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Was_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Fault_Was_Present" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Encoder_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Was_Actuated" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStopped_Locally" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="System_Started" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Fire_Relay_De_Energized" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="On_UPS_Battery" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_Low" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="NAT_Switch_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_MCM">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_MCM">
|
|
||||||
<DataValueMember Name="EStop_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_On" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_Low" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Fire_Relay_De_Energized" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Encoder_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault_Reset_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Restart_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Reset_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="UPS_Battery_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="On_UPS_Battery" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="NAT_Switch_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStopped_Locally" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault_Reset_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Reset_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Restart_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault_Reset_PB_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_PB_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Reset_PB_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Actuated_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Restart_PB_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Local_Estop_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Setup_Motor_Speeds" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Fault_Reset_PB_STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_PB_STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Reset_PB_STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB_STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Restart_PB_STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_MCM">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_MCM_CMD">
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Log_Quantity" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Acknowledge_Log" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Unacknowledge_All_Logs" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Was_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Was_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Fault_Was_Present" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Encoder_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Actuated" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Was_Actuated" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_MCM_STS">
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Log_Quantity" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<StructureMember Name="Acknowledge_Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="Unacknowledge_All_Logs" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Motor_Was_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="VFD_Fault_Reset_Requested" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Power_Branch_Fault_Reset_Requested" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Low_Air_Pressure_Fault_Reset_Requested" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="System_Started" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Reset_Requested" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Communication_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Setup_Motor_Speeds" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="System_Start_Requested" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
<Tag Name="S011004_PB_Chute" Class="Standard" TagType="Base" DataType="UDT_AOI_PB_CHUTE" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,0,0,0,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],[0,0,0],[0,0,0],[1,0,0,[0,0,0],[0,0,0]]],[[0],0,0,0,0,0],[[0],[0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_PB_CHUTE">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_PB_CHUTE">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="IO_Block_Communication_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="PR_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Disabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Solenoid_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="G_Beacon_Segment_Color" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="G_Beacon_Segment_Animation" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="B_Beacon_Segment_Color" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="B_Beacon_Segment_Animation" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_PB_CHUTE">
|
|
||||||
<StructureMember Name="Alarms" DataType="UDT_ALARMS_PB_CHUTE">
|
|
||||||
<DataValueMember Name="Disabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="PR_PB_Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Enable_Chute_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="TimeOut" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="PR_PB_STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Chute_Type" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_PB_CHUTE">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_PB_CHUTE_CMD">
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_PB_CHUTE_STS">
|
|
||||||
<DataValueMember Name="Enabled" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Half_Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Full_PE_Blocked" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Ready" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<Tag Name="PDP1_PMM1" Class="Standard" TagType="Base" DataType="UDT_AOI_PMM" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,[0,0.00000000e+000,0.00000000e+000,0.00000000e+000,[0]]],[0,0.00000000e+000,0.00000000e+000,0.00000000e+000
|
|
||||||
,[0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_PMM">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_PMM">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Parent_Comm_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="PMM_Fault_I" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_PMM">
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="PMM_Total_Power" DataType="REAL" Radix="Float" Value="0.0"/>
|
|
||||||
<DataValueMember Name="PMM_KWH_Consumed" DataType="REAL" Radix="Float" Value="0.0"/>
|
|
||||||
<DataValueMember Name="PMM_Max_Total_Power" DataType="REAL" Radix="Float" Value="0.0"/>
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_PMM">
|
|
||||||
<DataValueMember Name="PMM_Communication_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="PMM_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
<Tag Name="Rack" Class="Standard" TagType="Base" DataType="UDT_AOI_RACK" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,0,0,0,0,0],[[0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_RACK">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_RACK">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot2_EN4TR_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot5_IB16_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot6_OB16E_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot7_IB16S_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_RACK">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_RACK">
|
|
||||||
<DataValueMember Name="Slot5_IB16_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot6_OB16E_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot7_IB16S_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Slot2_EN4TR_Faulted" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
<Tag Name="S011003_JR" Class="Standard" TagType="Base" DataType="UDT_AOI_STATION_JR_CHUTE" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,0,0,[1,0,0,[0,0,0],[0,0,0]]],[[0],0,0],[[0],[0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_STATION_JR_CHUTE">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_STATION_JR_CHUTE">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="JR_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="JR_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="A_Beacon_Segment_Color_O" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="A_Beacon_Segment_Animation_O" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_STATION_JR_CHUTE">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_STATION_JR_CHUTE">
|
|
||||||
<DataValueMember Name="Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="JR_PB" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_STATION_JR_CHUTE">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_STATION_JR_CHUTE_CMD">
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Sorter_JPE_Blocked" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_STATION_JR_CHUTE_STS">
|
|
||||||
<DataValueMember Name="Jam_Reset_Requested" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
<Tag Name="FL1014_2_JR2_PB" Class="Standard" TagType="Base" DataType="UDT_AOI_STATION_JR_PB" Constant="false" ExternalAccess="Read/Write" OpcUaAccess="None">
|
|
||||||
<Data Format="L5K">
|
|
||||||
<![CDATA[[[1,0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0],0,0,[15,'FL1014_2_JR2_PB$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[[0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
]],[0,[0,'$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00$00'
|
|
||||||
],0]]]]]>
|
|
||||||
</Data>
|
|
||||||
<Data Format="Decorated">
|
|
||||||
<Structure DataType="UDT_AOI_STATION_JR_PB">
|
|
||||||
<StructureMember Name="AOI" DataType="AOI_STATION_JR_PB">
|
|
||||||
<DataValueMember Name="EnableIn" DataType="BOOL" Value="1"/>
|
|
||||||
<DataValueMember Name="EnableOut" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="JR_PB_I" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="JR_PB_LT_O" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="HMI" DataType="UDT_HMI_STATION">
|
|
||||||
<StructureMember Name="Alarm" DataType="UDT_ALARMS_STATION">
|
|
||||||
<DataValueMember Name="EStopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stopped" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Pressed" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="STATE" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="JR_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Start_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop_PB" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Name" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="15"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="CTRL" DataType="UDT_CTRL_STATION">
|
|
||||||
<StructureMember Name="CMD" DataType="UDT_CTRL_STATION_CMD">
|
|
||||||
<DataValueMember Name="Jammed" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Actuated" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
<StructureMember Name="STS" DataType="UDT_CTRL_STATION_STS">
|
|
||||||
<DataValueMember Name="Start" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Stop" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="EStop_Was_Actuated" DataType="BOOL" Value="0"/>
|
|
||||||
<StructureMember Name="Log" DataType="STRING">
|
|
||||||
<DataValueMember Name="LEN" DataType="DINT" Radix="Decimal" Value="0"/>
|
|
||||||
<DataValueMember Name="DATA" DataType="STRING" Radix="ASCII">
|
|
||||||
<![CDATA[]]>
|
|
||||||
</DataValueMember>
|
|
||||||
</StructureMember>
|
|
||||||
<DataValueMember Name="JR_PB_LT" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Fault" DataType="BOOL" Value="0"/>
|
|
||||||
<DataValueMember Name="Jam_Reset" DataType="BOOL" Value="0"/>
|
|
||||||
</StructureMember>
|
|
||||||
</StructureMember>
|
|
||||||
</Structure>
|
|
||||||
</Data>
|
|
||||||
</Tag>
|
|
||||||
@ -18,6 +18,7 @@ import io
|
|||||||
import contextlib
|
import contextlib
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import re
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
def get_project_paths():
|
def get_project_paths():
|
||||||
"""Get standardized paths for all project components."""
|
"""Get standardized paths for all project components."""
|
||||||
@ -31,7 +32,100 @@ def get_project_paths():
|
|||||||
'io_tree_generator': project_root / "IO Tree Configuration Generator"
|
'io_tree_generator': project_root / "IO Tree Configuration Generator"
|
||||||
}
|
}
|
||||||
|
|
||||||
def run_plc_data_generator(raw_excel_file: Path, paths: dict, verbose: bool = False) -> bool:
|
def resolve_project_config_files(project_name: str, project_root: Path) -> tuple[Path, Path]:
|
||||||
|
"""Resolve configuration file paths based on project name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_name: Project name (e.g., 'MTN6_MCM02', 'SAT9_MCM01', 'CNO8_MCM01')
|
||||||
|
project_root: Root directory of the project
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (generator_config_path, zones_config_path)
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: If required config files don't exist
|
||||||
|
"""
|
||||||
|
# Extract project prefix (MTN6, SAT9, CNO8, etc.)
|
||||||
|
import re
|
||||||
|
project_match = re.match(r'^([A-Z0-9]+)_', project_name.upper())
|
||||||
|
if not project_match:
|
||||||
|
raise ValueError(f"Invalid project name format: {project_name}. Expected format: PREFIX_MCMXX")
|
||||||
|
|
||||||
|
project_prefix = project_match.group(1)
|
||||||
|
|
||||||
|
# Build config file paths
|
||||||
|
generator_config = project_root / f"{project_prefix}_generator_config.json"
|
||||||
|
zones_config = project_root / f"{project_prefix}_zones.json"
|
||||||
|
|
||||||
|
# Validate files exist
|
||||||
|
if not generator_config.exists():
|
||||||
|
raise FileNotFoundError(f"Generator config not found: {generator_config}")
|
||||||
|
if not zones_config.exists():
|
||||||
|
raise FileNotFoundError(f"Zones config not found: {zones_config}")
|
||||||
|
|
||||||
|
return generator_config, zones_config
|
||||||
|
|
||||||
|
def get_available_projects(project_root: Path) -> list[str]:
|
||||||
|
"""Get list of available project prefixes based on config files.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_root: Root directory of the project
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of available project prefixes (e.g., ['MTN6', 'SAT9', 'CNO8'])
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
projects = set()
|
||||||
|
|
||||||
|
# Look for *_generator_config.json files
|
||||||
|
for config_file in project_root.glob("*_generator_config.json"):
|
||||||
|
match = re.match(r'^([A-Z0-9]+)_generator_config\.json$', config_file.name)
|
||||||
|
if match:
|
||||||
|
prefix = match.group(1)
|
||||||
|
# Check if corresponding zones file exists
|
||||||
|
zones_file = project_root / f"{prefix}_zones.json"
|
||||||
|
if zones_file.exists():
|
||||||
|
projects.add(prefix)
|
||||||
|
|
||||||
|
return sorted(list(projects))
|
||||||
|
|
||||||
|
def resolve_boilerplate_directory(project_prefix: str, io_tree_dir: Path) -> str:
|
||||||
|
"""Resolve the boilerplate directory based on project prefix.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_prefix: Project prefix (e.g., 'MTN6', 'SAT9', 'CNO8')
|
||||||
|
io_tree_dir: IO Tree Configuration Generator directory
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Boilerplate directory name (e.g., 'MTN6_boilerplate')
|
||||||
|
"""
|
||||||
|
boilerplate_dir = f"{project_prefix}_boilerplate"
|
||||||
|
boilerplate_path = io_tree_dir / boilerplate_dir
|
||||||
|
|
||||||
|
if boilerplate_path.exists():
|
||||||
|
return boilerplate_dir
|
||||||
|
else:
|
||||||
|
# Fall back to default boilerplate directory
|
||||||
|
print(f"Warning: Project-specific boilerplate directory not found: {boilerplate_dir}")
|
||||||
|
print(" Using default 'boilerplate' directory")
|
||||||
|
return "boilerplate"
|
||||||
|
|
||||||
|
def _append_log(log_path: Path | None, header: str, body: str) -> None:
|
||||||
|
if not log_path:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(log_path, 'a', encoding='utf-8') as f:
|
||||||
|
f.write(f"\n=== {header} ===\n")
|
||||||
|
if body:
|
||||||
|
f.write(body)
|
||||||
|
if not body.endswith("\n"):
|
||||||
|
f.write("\n")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def run_plc_data_generator(raw_excel_file: Path, paths: dict, verbose: bool = False, log_file: Path | None = None) -> bool:
|
||||||
"""Run the PLC Data Generator to create DESC_IP_MERGED.xlsx."""
|
"""Run the PLC Data Generator to create DESC_IP_MERGED.xlsx."""
|
||||||
data_gen_dir = paths['data_generator']
|
data_gen_dir = paths['data_generator']
|
||||||
data_gen_script = data_gen_dir / "main.py"
|
data_gen_script = data_gen_dir / "main.py"
|
||||||
@ -64,9 +158,10 @@ def run_plc_data_generator(raw_excel_file: Path, paths: dict, verbose: bool = Fa
|
|||||||
# Consider it successful if the essential files were created, even with permission errors
|
# Consider it successful if the essential files were created, even with permission errors
|
||||||
if result.returncode == 0 or (any(success_indicators) and "[Errno 1] Operation not permitted" in result.stdout):
|
if result.returncode == 0 or (any(success_indicators) and "[Errno 1] Operation not permitted" in result.stdout):
|
||||||
if verbose and result.returncode != 0:
|
if verbose and result.returncode != 0:
|
||||||
print("Warning: Permission error at end of processing, core processing completed")
|
_append_log(log_file, "Step 1 Warning", "Permission error at end of processing, core processing completed")
|
||||||
if verbose:
|
if verbose:
|
||||||
print(result.stdout)
|
_append_log(log_file, "Step 1: PLC Data Generator stdout", result.stdout)
|
||||||
|
_append_log(log_file, "Step 1: PLC Data Generator stderr", result.stderr)
|
||||||
|
|
||||||
# Copy DESC_IP_MERGED.xlsx from data generator output (it already has safety sheets)
|
# Copy DESC_IP_MERGED.xlsx from data generator output (it already has safety sheets)
|
||||||
dest = paths['routines_generator'] / "DESC_IP_MERGED.xlsx"
|
dest = paths['routines_generator'] / "DESC_IP_MERGED.xlsx"
|
||||||
@ -79,28 +174,33 @@ def run_plc_data_generator(raw_excel_file: Path, paths: dict, verbose: bool = Fa
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if verbose:
|
if verbose:
|
||||||
print("Error: Data processing failed")
|
_append_log(log_file, "Step 1 Error", "Data processing failed")
|
||||||
print("STDOUT:", result.stdout)
|
_append_log(log_file, "Step 1: PLC Data Generator stdout", result.stdout)
|
||||||
print("STDERR:", result.stderr)
|
_append_log(log_file, "Step 1: PLC Data Generator stderr", result.stderr)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"Error: Exception in data processing: {e}")
|
_append_log(log_file, "Step 1 Exception", str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def run_routines_generator(paths: dict, project_name: str = None, ignore_estop1ok: bool = False, safety_only: bool = False, verbose: bool = False) -> bool:
|
def run_routines_generator(paths: dict, project_name: str = None, ignore_estop1ok: bool = False, safety_only: bool = False, verbose: bool = False, log_file: Path | None = None, config_path: Path = None) -> bool:
|
||||||
"""Run the Routines Generator.
|
"""Run the Routines Generator.
|
||||||
|
|
||||||
When safety_only is True, runs safety-only generation (inputs, outputs, resets,
|
When safety_only is True, runs safety-only generation (inputs, outputs, resets,
|
||||||
estops, zones, estop_check, safety tag map). Otherwise runs the standard
|
estops, zones, estop_check, safety tag map). Otherwise runs the standard
|
||||||
generator with DPM and other routines.
|
generator with DPM and other routines.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: Path to project-specific generator config file. If None, uses default.
|
||||||
"""
|
"""
|
||||||
routines_dir = paths['routines_generator']
|
routines_dir = paths['routines_generator']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Build command arguments to use unified, config-driven CLI
|
# Use provided config path or fall back to default
|
||||||
config_path = paths['project_root'] / 'generator_config.json'
|
if config_path is None:
|
||||||
|
config_path = paths['project_root'] / 'generator_config.json'
|
||||||
|
|
||||||
excel_path = routines_dir / 'DESC_IP_MERGED.xlsx'
|
excel_path = routines_dir / 'DESC_IP_MERGED.xlsx'
|
||||||
subcmd = 'safety' if safety_only else 'all'
|
subcmd = 'safety' if safety_only else 'all'
|
||||||
# Build args with global flags BEFORE the subcommand
|
# Build args with global flags BEFORE the subcommand
|
||||||
@ -112,6 +212,8 @@ def run_routines_generator(paths: dict, project_name: str = None, ignore_estop1o
|
|||||||
]
|
]
|
||||||
if verbose:
|
if verbose:
|
||||||
cmd_args.extend(['--log-level', 'DEBUG'])
|
cmd_args.extend(['--log-level', 'DEBUG'])
|
||||||
|
if log_file is not None:
|
||||||
|
cmd_args.extend(['--log-file', str(log_file)])
|
||||||
cmd_args.append(subcmd)
|
cmd_args.append(subcmd)
|
||||||
# Note: routine inclusion/exclusion is driven by config; project_name and ignore-estop1ok are configured in JSON
|
# Note: routine inclusion/exclusion is driven by config; project_name and ignore-estop1ok are configured in JSON
|
||||||
|
|
||||||
@ -119,9 +221,9 @@ def run_routines_generator(paths: dict, project_name: str = None, ignore_estop1o
|
|||||||
result = subprocess.run(cmd_args, cwd=routines_dir, capture_output=True, text=True)
|
result = subprocess.run(cmd_args, cwd=routines_dir, capture_output=True, text=True)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print(result.stdout)
|
_append_log(log_file, "Step 2: Routines Generator stdout", result.stdout)
|
||||||
if result.stderr:
|
if result.stderr:
|
||||||
print("[generator stderr]", result.stderr)
|
_append_log(log_file, "Step 2: Routines Generator stderr", result.stderr)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
return True
|
return True
|
||||||
@ -130,10 +232,10 @@ def run_routines_generator(paths: dict, project_name: str = None, ignore_estop1o
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"Error: Exception in routine generation: {e}")
|
_append_log(log_file, "Step 2 Exception", str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def run_io_tree_generator(paths: dict, project_name: str, safety_only: bool = False, verbose: bool = False) -> bool:
|
def run_io_tree_generator(paths: dict, project_name: str, safety_only: bool = False, verbose: bool = False, log_file: Path | None = None, boilerplate_dir: str = None) -> bool:
|
||||||
"""Run the IO Tree Configuration Generator.
|
"""Run the IO Tree Configuration Generator.
|
||||||
|
|
||||||
If safety_only is True, skip this step to avoid generating non-safety routines.
|
If safety_only is True, skip this step to avoid generating non-safety routines.
|
||||||
@ -161,15 +263,17 @@ def run_io_tree_generator(paths: dict, project_name: str, safety_only: bool = Fa
|
|||||||
project_name
|
project_name
|
||||||
]
|
]
|
||||||
|
|
||||||
# Zones removed; no additional args
|
# Add boilerplate directory if specified
|
||||||
|
if boilerplate_dir:
|
||||||
|
cmd_args.append(boilerplate_dir)
|
||||||
|
|
||||||
# Run the IO Tree Configuration Generator
|
# Run the IO Tree Configuration Generator
|
||||||
result = subprocess.run(cmd_args, cwd=io_tree_dir, capture_output=True, text=True)
|
result = subprocess.run(cmd_args, cwd=io_tree_dir, capture_output=True, text=True)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print(result.stdout)
|
_append_log(log_file, "Step 3: IO Tree Generator stdout", result.stdout)
|
||||||
if result.stderr:
|
if result.stderr:
|
||||||
print("[io-tree stderr]", result.stderr)
|
_append_log(log_file, "Step 3: IO Tree Generator stderr", result.stderr)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
return True
|
return True
|
||||||
@ -178,10 +282,10 @@ def run_io_tree_generator(paths: dict, project_name: str, safety_only: bool = Fa
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"Error: Exception in IO tree generation: {e}")
|
_append_log(log_file, "Step 3 Exception", str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def run_l5x_to_acd_compiler(paths: dict, project_name: str, safety_only: bool = False, verbose: bool = False) -> bool:
|
def run_l5x_to_acd_compiler(paths: dict, project_name: str, safety_only: bool = False, verbose: bool = False, log_file: Path | None = None) -> bool:
|
||||||
"""Prepare for L5X2ACD Compilation using dynamic compilation manager.
|
"""Prepare for L5X2ACD Compilation using dynamic compilation manager.
|
||||||
|
|
||||||
If safety_only is True, skip this step since a full project L5X wasn't generated.
|
If safety_only is True, skip this step since a full project L5X wasn't generated.
|
||||||
@ -200,31 +304,23 @@ def run_l5x_to_acd_compiler(paths: dict, project_name: str, safety_only: bool =
|
|||||||
l5x_files = list(generated_projects_dir.glob(f"{project_name}*.L5X"))
|
l5x_files = list(generated_projects_dir.glob(f"{project_name}*.L5X"))
|
||||||
|
|
||||||
if not l5x_files:
|
if not l5x_files:
|
||||||
if verbose:
|
# Minimal output: rely on caller to report FAIL
|
||||||
available_files = list(generated_projects_dir.glob("*.L5X"))
|
|
||||||
if available_files:
|
|
||||||
print(f"Available L5X files: {[f.name for f in available_files]}")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(l5x_files) > 1 and verbose:
|
if len(l5x_files) > 1 and verbose:
|
||||||
print(f"Warning: Multiple L5X files found, using first: {l5x_files[0].name}")
|
print(f"Warning: Multiple L5X files found, using first: {l5x_files[0].name}")
|
||||||
|
|
||||||
complete_l5x = l5x_files[0]
|
complete_l5x = l5x_files[0]
|
||||||
if verbose:
|
|
||||||
print(f"Found generated L5X file: {complete_l5x.name}")
|
|
||||||
|
|
||||||
# Inject SafetyTagMap from SafetyTagMapping.txt before compilation (if available)
|
# Inject SafetyTagMap from SafetyTagMapping.txt before compilation (if available)
|
||||||
try:
|
try:
|
||||||
mapping_file = paths['routines_generator'] / 'SafetyTagMapping.txt'
|
mapping_file = paths['routines_generator'] / 'SafetyTagMapping.txt'
|
||||||
if mapping_file.exists():
|
if mapping_file.exists():
|
||||||
if verbose:
|
# Always inject silently (minimal logging)
|
||||||
print("Injecting SafetyTagMap from SafetyTagMapping.txt into L5X ...")
|
_inject_safety_tag_map_into_l5x(complete_l5x, mapping_file, False)
|
||||||
_inject_safety_tag_map_into_l5x(complete_l5x, mapping_file, verbose)
|
|
||||||
elif verbose:
|
|
||||||
print("SafetyTagMapping.txt not found; skipping SafetyTagMap injection")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"Warning: Failed to inject SafetyTagMap: {e}")
|
_append_log(log_file, "Step 4 Warning", f"Failed to inject SafetyTagMap: {e}")
|
||||||
|
|
||||||
# Use the dynamic compilation manager
|
# Use the dynamic compilation manager
|
||||||
l5x2acd_dir = paths['project_root'] / "L5X2ACD Compiler"
|
l5x2acd_dir = paths['project_root'] / "L5X2ACD Compiler"
|
||||||
@ -238,10 +334,9 @@ def run_l5x_to_acd_compiler(paths: dict, project_name: str, safety_only: bool =
|
|||||||
# Create compilation manager
|
# Create compilation manager
|
||||||
manager = CompilationManager(l5x2acd_dir)
|
manager = CompilationManager(l5x2acd_dir)
|
||||||
|
|
||||||
# Determine project-specific options
|
# Determine project-specific options (silent)
|
||||||
project_type = "UNKNOWN"
|
project_type = "UNKNOWN"
|
||||||
options = {}
|
options = {}
|
||||||
|
|
||||||
if project_name:
|
if project_name:
|
||||||
if "MCM01" in project_name.upper():
|
if "MCM01" in project_name.upper():
|
||||||
project_type = "MCM01"
|
project_type = "MCM01"
|
||||||
@ -250,42 +345,23 @@ def run_l5x_to_acd_compiler(paths: dict, project_name: str, safety_only: bool =
|
|||||||
project_type = "MCM04"
|
project_type = "MCM04"
|
||||||
options['enable_feeder_optimization'] = True
|
options['enable_feeder_optimization'] = True
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print(f"- Project type: {project_type}")
|
|
||||||
|
|
||||||
# Setup compilation with wipe and dynamic generation
|
# Setup compilation with wipe and dynamic generation
|
||||||
if verbose:
|
# Always run quietly and suppress tool output; caller prints step OK/FAIL
|
||||||
|
_buf = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(_buf), contextlib.redirect_stderr(_buf):
|
||||||
result = manager.setup_compilation(
|
result = manager.setup_compilation(
|
||||||
source_l5x=complete_l5x,
|
source_l5x=complete_l5x,
|
||||||
project_name=project_name or complete_l5x.stem,
|
project_name=project_name or complete_l5x.stem,
|
||||||
compilation_options=options,
|
compilation_options=options,
|
||||||
wipe_existing=True
|
wipe_existing=True
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
_buf = io.StringIO()
|
|
||||||
with contextlib.redirect_stdout(_buf), contextlib.redirect_stderr(_buf):
|
|
||||||
result = manager.setup_compilation(
|
|
||||||
source_l5x=complete_l5x,
|
|
||||||
project_name=project_name or complete_l5x.stem,
|
|
||||||
compilation_options=options,
|
|
||||||
wipe_existing=True
|
|
||||||
)
|
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print("OK: Compilation setup completed")
|
_append_log(log_file, "Step 4: L5X2ACD Compiler output", _buf.getvalue())
|
||||||
l5x2acd_windows_path = str(l5x2acd_dir).replace('/mnt/c/', 'C:\\').replace('/', '\\')
|
|
||||||
l5x_windows_path = str(result['l5x_file']).replace('/mnt/c/', 'C:\\').replace('/', '\\')
|
|
||||||
print("To compile on Windows:")
|
|
||||||
print(f"- cd \"{l5x2acd_windows_path}\"")
|
|
||||||
print(f"- python l5x_to_acd.py \"{l5x_windows_path}\"")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if verbose:
|
# Minimal output; let caller handle FAIL display
|
||||||
print(f"Error: Exception in compilation setup: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -369,35 +445,153 @@ def main() -> None:
|
|||||||
"""Main entry point for complete workflow."""
|
"""Main entry point for complete workflow."""
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Complete PLC generation workflow from raw Excel to ACD")
|
parser = argparse.ArgumentParser(description="Complete PLC generation workflow from raw Excel to ACD")
|
||||||
parser.add_argument('--excel-file', type=Path, required=True, help='Raw Excel file to process')
|
parser.add_argument('--excel-file', type=Path, help='Raw Excel file to process')
|
||||||
parser.add_argument('--project-name', help='Project name (for compatibility)')
|
|
||||||
|
# Project selection
|
||||||
|
parser.add_argument('--project', help='Project prefix (e.g., MTN6, SAT9, CNO8) - automatically selects config files')
|
||||||
|
parser.add_argument('--project-name', help='Project name (e.g., MTN6_MCM02) - used for output naming and compatibility')
|
||||||
|
|
||||||
parser.add_argument('--ignore-estop1ok', action='store_true', help='Ignore ESTOP1OK tags in safety routines generation')
|
parser.add_argument('--ignore-estop1ok', action='store_true', help='Ignore ESTOP1OK tags in safety routines generation')
|
||||||
parser.add_argument('--safety-only', action='store_true', help='Generate only safety routines and safety checks')
|
parser.add_argument('--safety-only', action='store_true', help='Generate only safety routines and safety checks')
|
||||||
parser.add_argument('--verbose', action='store_true', help='Print detailed logs for each step')
|
parser.add_argument('--verbose', action='store_true', help='Write detailed logs for each step to a file (no console spam)')
|
||||||
|
parser.add_argument('--list-projects', action='store_true', help='List available projects and exit')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Get project paths
|
# Get project paths
|
||||||
paths = get_project_paths()
|
paths = get_project_paths()
|
||||||
|
|
||||||
|
# Handle --list-projects (allow without --excel-file)
|
||||||
|
if args.list_projects:
|
||||||
|
available_projects = get_available_projects(paths['project_root'])
|
||||||
|
print("Available projects:")
|
||||||
|
for project in available_projects:
|
||||||
|
generator_config = paths['project_root'] / f"{project}_generator_config.json"
|
||||||
|
zones_config = paths['project_root'] / f"{project}_zones.json"
|
||||||
|
boilerplate_dir = paths['io_tree_generator'] / f"{project}_boilerplate"
|
||||||
|
boilerplate_status = "✓" if boilerplate_dir.exists() else "✗"
|
||||||
|
print(f" {project:<6} - Config: {generator_config.name}, Zones: {zones_config.name}, Boilerplate: {boilerplate_status}")
|
||||||
|
if not available_projects:
|
||||||
|
print(" No projects found. Expected files: PREFIX_generator_config.json and PREFIX_zones.json")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Resolve project configuration
|
||||||
|
generator_config_path = None
|
||||||
|
zones_config_path = None
|
||||||
|
project_name = args.project_name # Use provided project name if given
|
||||||
|
|
||||||
|
if args.project:
|
||||||
|
# Project selection mode - use project prefix to find config files
|
||||||
|
try:
|
||||||
|
# If no project name provided, derive it from Excel file name
|
||||||
|
if not project_name:
|
||||||
|
excel_name = args.excel_file.stem
|
||||||
|
if 'MCM' in excel_name.upper():
|
||||||
|
# Try to extract MCM info from filename
|
||||||
|
import re
|
||||||
|
mcm_match = re.search(r'(MCM\d+)', excel_name.upper())
|
||||||
|
if mcm_match:
|
||||||
|
project_name = f"{args.project.upper()}_{mcm_match.group(1)}"
|
||||||
|
else:
|
||||||
|
project_name = f"{args.project.upper()}_MCM01" # Default fallback
|
||||||
|
else:
|
||||||
|
project_name = f"{args.project.upper()}_MCM01" # Default fallback
|
||||||
|
|
||||||
|
# Use project prefix to find config files, but use provided or derived project name
|
||||||
|
generator_config_path, zones_config_path = resolve_project_config_files(f"{args.project.upper()}_MCM01", paths['project_root'])
|
||||||
|
print(f"Using project: {args.project.upper()}")
|
||||||
|
print(f" Generator config: {generator_config_path.name}")
|
||||||
|
print(f" Zones config: {zones_config_path.name}")
|
||||||
|
print(f" Project name: {project_name}")
|
||||||
|
except (ValueError, FileNotFoundError) as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
available_projects = get_available_projects(paths['project_root'])
|
||||||
|
if available_projects:
|
||||||
|
print(f"Available projects: {', '.join(available_projects)}")
|
||||||
|
else:
|
||||||
|
print("No projects found. Run with --list-projects to see details.")
|
||||||
|
sys.exit(1)
|
||||||
|
elif args.project_name:
|
||||||
|
# Backward compatibility mode using --project-name only
|
||||||
|
project_name = args.project_name
|
||||||
|
# Try to auto-detect config files based on project name
|
||||||
|
try:
|
||||||
|
generator_config_path, zones_config_path = resolve_project_config_files(project_name, paths['project_root'])
|
||||||
|
print(f"Auto-detected config files for {project_name}:")
|
||||||
|
print(f" Generator config: {generator_config_path.name}")
|
||||||
|
print(f" Zones config: {zones_config_path.name}")
|
||||||
|
except (ValueError, FileNotFoundError):
|
||||||
|
# Fall back to default config files
|
||||||
|
print(f"Using default config files (project-specific configs not found for {project_name})")
|
||||||
|
generator_config_path = None # Will use default in run_routines_generator
|
||||||
|
else:
|
||||||
|
# No project specified - require at least one
|
||||||
|
print("Error: Either --project or --project-name must be specified")
|
||||||
|
available_projects = get_available_projects(paths['project_root'])
|
||||||
|
if available_projects:
|
||||||
|
print(f"Available projects: {', '.join(available_projects)}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Validate excel-file is provided for actual processing (not just listing)
|
||||||
|
if not args.excel_file:
|
||||||
|
print("Error: --excel-file is required for processing")
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Setup enhanced logging
|
||||||
|
from src.logging_config import setup_logging, get_logger
|
||||||
|
ts = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
|
log_dir = paths['project_root'] / 'logs'
|
||||||
|
base = project_name or 'project'
|
||||||
|
log_file = log_dir / f"workflow_{base}_{ts}.log" if args.verbose else None
|
||||||
|
|
||||||
|
# Configure logging with location information
|
||||||
|
setup_logging(
|
||||||
|
level='DEBUG' if args.verbose else 'INFO',
|
||||||
|
console_format='human',
|
||||||
|
log_file=log_file,
|
||||||
|
show_module=True,
|
||||||
|
show_location=True,
|
||||||
|
use_colors=True
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
logger.info("PLC Generation Workflow started", excel_file=str(args.excel_file), project_name=args.project_name)
|
||||||
|
|
||||||
print("PLC Generation Workflow")
|
print("PLC Generation Workflow")
|
||||||
|
|
||||||
# Step 1: Process raw Excel data
|
# Step 1: Process raw Excel data
|
||||||
print("Step 1: Data processing ...", end=" ")
|
print("Step 1: Data processing ...", end=" ")
|
||||||
ok = run_plc_data_generator(args.excel_file, paths, verbose=args.verbose)
|
logger.info("Starting data processing step")
|
||||||
print("OK" if ok else "FAIL")
|
try:
|
||||||
if not ok:
|
ok = run_plc_data_generator(args.excel_file, paths, verbose=args.verbose, log_file=log_file)
|
||||||
if not args.verbose:
|
print("OK" if ok else "FAIL")
|
||||||
print("(details suppressed; re-run with --verbose)")
|
if not ok:
|
||||||
|
logger.error("Data processing failed")
|
||||||
|
if not args.verbose:
|
||||||
|
print("(details suppressed; re-run with --verbose)")
|
||||||
|
sys.exit(1)
|
||||||
|
logger.info("Data processing completed successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("Data processing step failed with exception", error=str(e))
|
||||||
|
print("FAIL")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Step 2: Generate L5X programs (Routines Generator)
|
# Step 2: Generate L5X programs (Routines Generator)
|
||||||
print("Step 2: Routine generation ...", end=" ")
|
print("Step 2: Routine generation ...", end=" ")
|
||||||
ok = run_routines_generator(paths, args.project_name, args.ignore_estop1ok, args.safety_only, verbose=args.verbose)
|
logger.info("Starting routine generation step")
|
||||||
print("OK" if ok else "FAIL")
|
try:
|
||||||
if not ok:
|
ok = run_routines_generator(paths, project_name, args.ignore_estop1ok, args.safety_only, verbose=args.verbose, log_file=log_file, config_path=generator_config_path)
|
||||||
if not args.verbose:
|
print("OK" if ok else "FAIL")
|
||||||
print("(details suppressed; re-run with --verbose)")
|
if not ok:
|
||||||
|
logger.error("Routine generation failed")
|
||||||
|
if not args.verbose:
|
||||||
|
print("(details suppressed; re-run with --verbose)")
|
||||||
|
sys.exit(1)
|
||||||
|
logger.info("Routine generation completed successfully")
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("Routine generation step failed with exception", error=str(e))
|
||||||
|
print("FAIL")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Step 3: Generate complete project L5X (IO Tree Generator)
|
# Step 3: Generate complete project L5X (IO Tree Generator)
|
||||||
@ -405,7 +599,11 @@ def main() -> None:
|
|||||||
print("Step 3: IO tree generation ... SKIPPED")
|
print("Step 3: IO tree generation ... SKIPPED")
|
||||||
else:
|
else:
|
||||||
print("Step 3: IO tree generation ...", end=" ")
|
print("Step 3: IO tree generation ...", end=" ")
|
||||||
ok = run_io_tree_generator(paths, args.project_name, args.safety_only, verbose=args.verbose)
|
# Determine boilerplate directory based on project
|
||||||
|
boilerplate_dir = None
|
||||||
|
if args.project:
|
||||||
|
boilerplate_dir = resolve_boilerplate_directory(args.project.upper(), paths['io_tree_generator'])
|
||||||
|
ok = run_io_tree_generator(paths, project_name, args.safety_only, verbose=args.verbose, log_file=log_file, boilerplate_dir=boilerplate_dir)
|
||||||
print("OK" if ok else "FAIL")
|
print("OK" if ok else "FAIL")
|
||||||
if not ok:
|
if not ok:
|
||||||
if not args.verbose:
|
if not args.verbose:
|
||||||
@ -417,7 +615,7 @@ def main() -> None:
|
|||||||
print("Step 4: Prepare compilation ... SKIPPED")
|
print("Step 4: Prepare compilation ... SKIPPED")
|
||||||
else:
|
else:
|
||||||
print("Step 4: Prepare compilation ...", end=" ")
|
print("Step 4: Prepare compilation ...", end=" ")
|
||||||
ok = run_l5x_to_acd_compiler(paths, args.project_name, args.safety_only, verbose=args.verbose)
|
ok = run_l5x_to_acd_compiler(paths, project_name, args.safety_only, verbose=args.verbose, log_file=log_file)
|
||||||
print("OK" if ok else "FAIL")
|
print("OK" if ok else "FAIL")
|
||||||
if not ok:
|
if not ok:
|
||||||
if not args.verbose:
|
if not args.verbose:
|
||||||
@ -425,8 +623,10 @@ def main() -> None:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("Workflow complete")
|
print("Workflow complete")
|
||||||
if args.verbose and not args.safety_only and args.project_name:
|
if args.verbose and log_file is not None:
|
||||||
print(f"L5X: IO Tree Configuration Generator/generated_projects/{args.project_name}.L5X")
|
print(f"Logs: {log_file}")
|
||||||
|
if not args.safety_only and project_name:
|
||||||
|
print(f"L5X: IO Tree Configuration Generator/generated_projects/{project_name}.L5X")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
This script is deprecated. Use the unified CLI:
|
|
||||||
python -m src.unified_cli --config ../generator_config.json --excel-file DESC_IP_MERGED.xlsx all
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
print("generate_all.py is deprecated. Please use the unified CLI (src.unified_cli).", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
@ -1,135 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
PLC Routines Generator - Main Entry Point
|
|
||||||
|
|
||||||
This script provides backward compatibility while using the new unified CLI internally.
|
|
||||||
For new usage, prefer the unified CLI: python -m src.unified_cli
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import argparse
|
|
||||||
from pathlib import Path
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
def run_l5x_to_acd_compilation(safety_l5x: Path, main_l5x: Path) -> bool:
|
|
||||||
"""Run the L5X to ACD compilation step."""
|
|
||||||
l5x2acd_dir = Path("../L5X2ACD Compiler")
|
|
||||||
l5x2acd_script = l5x2acd_dir / "l5x_to_acd.py"
|
|
||||||
|
|
||||||
if not l5x2acd_script.exists():
|
|
||||||
print(f"ERROR: L5X2ACD compiler not found at {l5x2acd_script}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not safety_l5x.exists():
|
|
||||||
print(f"ERROR: SafetyProgram L5X not found at {safety_l5x}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not main_l5x.exists():
|
|
||||||
print(f"ERROR: MainProgram L5X not found at {main_l5x}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"\n=== Compiling L5X files to ACD ===")
|
|
||||||
print(f"Safety L5X: {safety_l5x}")
|
|
||||||
print(f"Main L5X: {main_l5x}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Run the L5X2ACD compiler
|
|
||||||
result = subprocess.run([
|
|
||||||
sys.executable,
|
|
||||||
str(l5x2acd_script),
|
|
||||||
str(safety_l5x.resolve()),
|
|
||||||
str(main_l5x.resolve())
|
|
||||||
], cwd=l5x2acd_dir, capture_output=True, text=True)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("SUCCESS: L5X to ACD compilation completed successfully")
|
|
||||||
print(result.stdout)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("ERROR: L5X to ACD compilation failed")
|
|
||||||
print("STDOUT:", result.stdout)
|
|
||||||
print("STDERR:", result.stderr)
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"ERROR: Error running L5X2ACD compiler: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
"""Main entry point that maps old generate_all.py behavior to new unified CLI."""
|
|
||||||
|
|
||||||
# Parse arguments to maintain backward compatibility
|
|
||||||
parser = argparse.ArgumentParser(description="Generate PLC routine artifacts")
|
|
||||||
parser.add_argument('--config', type=Path, default=Path(__file__).parent.parent / 'generator_config.json', help='Configuration file')
|
|
||||||
parser.add_argument('--excel-file', type=Path, help='Excel file to process')
|
|
||||||
parser.add_argument('--output-dir', type=Path, help='Output directory')
|
|
||||||
parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'], default='INFO', help='Log level')
|
|
||||||
parser.add_argument('--log-file', type=Path, help='Log file path')
|
|
||||||
parser.add_argument('--project-name', help='Project name (for compatibility, not used)')
|
|
||||||
# Zones option removed
|
|
||||||
|
|
||||||
# New compilation option
|
|
||||||
parser.add_argument('--compile-acd', action='store_true', help='Compile L5X files to ACD after generation')
|
|
||||||
|
|
||||||
# Legacy argument for compatibility
|
|
||||||
parser.add_argument('--desc-ip-mode', action='store_true', help='(Deprecated) DESC_IP extraction is now the default')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.desc_ip_mode:
|
|
||||||
print("WARNING: --desc-ip-mode flag is deprecated. DESC_IP extraction is now the default mode.")
|
|
||||||
|
|
||||||
# Construct arguments for unified CLI
|
|
||||||
unified_args = ['--log-level', args.log_level]
|
|
||||||
|
|
||||||
if args.config:
|
|
||||||
unified_args.extend(['--config', str(args.config)])
|
|
||||||
|
|
||||||
if args.excel_file:
|
|
||||||
unified_args.extend(['--excel-file', str(args.excel_file)])
|
|
||||||
|
|
||||||
# Zones option removed
|
|
||||||
|
|
||||||
# Add the 'all' command
|
|
||||||
unified_args.append('all')
|
|
||||||
|
|
||||||
if args.output_dir:
|
|
||||||
unified_args.extend(['--output-dir', str(args.output_dir)])
|
|
||||||
|
|
||||||
# Import and call the unified CLI
|
|
||||||
from src.unified_cli import main as unified_main
|
|
||||||
|
|
||||||
print("=== PLC Routines Generator (Refactored) ===")
|
|
||||||
print("Using unified CLI internally...")
|
|
||||||
print()
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Generate L5X files
|
|
||||||
unified_main(unified_args)
|
|
||||||
|
|
||||||
# If compilation requested, run L5X2ACD compiler
|
|
||||||
if args.compile_acd:
|
|
||||||
# Determine output directory
|
|
||||||
output_dir = args.output_dir or Path('.')
|
|
||||||
|
|
||||||
safety_l5x = output_dir / "SafetyProgram_Generated.L5X"
|
|
||||||
main_l5x = output_dir / "MainProgram_Generated.L5X"
|
|
||||||
|
|
||||||
success = run_l5x_to_acd_compilation(safety_l5x, main_l5x)
|
|
||||||
if not success:
|
|
||||||
print("\nERROR: Compilation failed")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
print("\nSUCCESS: Complete PLC generation and compilation successful!")
|
|
||||||
else:
|
|
||||||
print("\nSUCCESS: L5X generation completed. Use --compile-acd to also generate ACD files.")
|
|
||||||
|
|
||||||
except SystemExit as e:
|
|
||||||
if e.code != 0:
|
|
||||||
print(f"\nERROR: Generation failed with exit code {e.code}")
|
|
||||||
sys.exit(e.code)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -30,6 +30,8 @@ class BaseGenerator(ABC):
|
|||||||
# Will be set during generation
|
# Will be set during generation
|
||||||
self.builder: Optional[L5XBuilder] = None
|
self.builder: Optional[L5XBuilder] = None
|
||||||
self.routine_manager: Optional[RoutineManager] = None
|
self.routine_manager: Optional[RoutineManager] = None
|
||||||
|
# Metrics for summary logging
|
||||||
|
self.metrics: Dict[str, Any] = {}
|
||||||
|
|
||||||
def generate(self) -> ET.Element:
|
def generate(self) -> ET.Element:
|
||||||
"""Template method for generating PLC programs.
|
"""Template method for generating PLC programs.
|
||||||
@ -102,7 +104,46 @@ class BaseGenerator(ABC):
|
|||||||
|
|
||||||
def finalize_generation(self) -> None:
|
def finalize_generation(self) -> None:
|
||||||
"""Finalize the generation process. Override if needed."""
|
"""Finalize the generation process. Override if needed."""
|
||||||
pass
|
# Default: log a concise program summary
|
||||||
|
try:
|
||||||
|
self._log_program_summary()
|
||||||
|
except Exception:
|
||||||
|
# Summary is best-effort; do not break generation
|
||||||
|
return
|
||||||
|
|
||||||
|
def _log_program_summary(self) -> None:
|
||||||
|
if not self.builder:
|
||||||
|
return
|
||||||
|
routines_el = self.builder.get_routines_section()
|
||||||
|
program_el = self.builder.get_program_element()
|
||||||
|
# Collect routines
|
||||||
|
routines = [r.get('Name', '') for r in routines_el.findall('Routine')]
|
||||||
|
routines = [r for r in routines if r]
|
||||||
|
routines.sort()
|
||||||
|
if routines:
|
||||||
|
self.logger.info(f"Routines created ({len(routines)}): {', '.join(routines)}", stage="summary")
|
||||||
|
# For each routine, log rung count and one example rung text (first non-empty)
|
||||||
|
for r in routines_el.findall('Routine'):
|
||||||
|
name = r.get('Name', '')
|
||||||
|
rll = r.find('RLLContent')
|
||||||
|
if rll is None:
|
||||||
|
continue
|
||||||
|
rungs = rll.findall('Rung')
|
||||||
|
rung_count = len(rungs)
|
||||||
|
example = ''
|
||||||
|
for rung in rungs:
|
||||||
|
txt = rung.find('Text')
|
||||||
|
if txt is not None and (txt.text or '').strip():
|
||||||
|
example = (txt.text or '').strip()
|
||||||
|
break
|
||||||
|
if example:
|
||||||
|
# Keep example on one line, clipped
|
||||||
|
snippet = example.replace('\n', ' ')
|
||||||
|
if len(snippet) > 160:
|
||||||
|
snippet = snippet[:157] + '...'
|
||||||
|
self.logger.info(f"- {name}: {rung_count} rungs | example: {snippet}", stage="summary")
|
||||||
|
else:
|
||||||
|
self.logger.info(f"- {name}: {rung_count} rungs", stage="summary")
|
||||||
|
|
||||||
def get_context_metadata(self) -> Dict[str, Any]:
|
def get_context_metadata(self) -> Dict[str, Any]:
|
||||||
"""Get metadata for routine context. Override to add custom metadata."""
|
"""Get metadata for routine context. Override to add custom metadata."""
|
||||||
@ -171,16 +212,8 @@ class SafetyProgramGenerator(BaseGenerator):
|
|||||||
# Add safety tag map if needed
|
# Add safety tag map if needed
|
||||||
self._add_safety_tag_map()
|
self._add_safety_tag_map()
|
||||||
|
|
||||||
# Add zones routine from configuration if available
|
# Zones routine is now handled through routine_plan configuration
|
||||||
try:
|
# No longer adding zones routine here to avoid duplication
|
||||||
zones_df = self.data_loader.zones
|
|
||||||
if zones_df is not None:
|
|
||||||
from .routines.zones import create_zones_routine
|
|
||||||
create_zones_routine(self.builder.get_routines_section(), zones_df, self.data_loader.epc)
|
|
||||||
self.logger.info("Added R030_ZONES routine from zones.json")
|
|
||||||
except Exception:
|
|
||||||
# Zones are optional; proceed without blocking generation
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Ensure a MainRoutine exists and references generated safety routines
|
# Ensure a MainRoutine exists and references generated safety routines
|
||||||
# The ProgramAttributes set MainRoutineName to config.routines.main_routine_name
|
# The ProgramAttributes set MainRoutineName to config.routines.main_routine_name
|
||||||
@ -218,7 +251,7 @@ class SafetyProgramGenerator(BaseGenerator):
|
|||||||
safety_tags = self.data_loader.safety_tags_from_pb
|
safety_tags = self.data_loader.safety_tags_from_pb
|
||||||
|
|
||||||
if safety_tags:
|
if safety_tags:
|
||||||
from .generators.safety_program import create_safety_tag_map
|
from .utils.safety_tag_map import create_safety_tag_map
|
||||||
create_safety_tag_map(program_element, safety_tags, set())
|
create_safety_tag_map(program_element, safety_tags, set())
|
||||||
self.logger.debug(f"Added safety tag map with {len(safety_tags)} tags")
|
self.logger.debug(f"Added safety tag map with {len(safety_tags)} tags")
|
||||||
|
|
||||||
|
|||||||
@ -1,132 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from generators import (
|
|
||||||
SafetyProgramGenerator,
|
|
||||||
MainProgramGenerator,
|
|
||||||
)
|
|
||||||
from writers import create_safety_tag_mapping
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd_safety(args: argparse.Namespace) -> None:
|
|
||||||
"""Generate SafetyProgram L5X using safety-focused generator."""
|
|
||||||
gen = SafetyProgramGenerator(args.excel)
|
|
||||||
gen.write(args.output)
|
|
||||||
print(f"Safety L5X written to {args.output}")
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd_main(args: argparse.Namespace) -> None:
|
|
||||||
"""Generate MainProgram L5X using safety-focused generator."""
|
|
||||||
gen = MainProgramGenerator(args.excel)
|
|
||||||
gen.write(args.output)
|
|
||||||
print(f"Main L5X written to {args.output}")
|
|
||||||
|
|
||||||
|
|
||||||
# CSV generation removed (deprecated)
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd_mapping(args: argparse.Namespace) -> None:
|
|
||||||
cnt = create_safety_tag_mapping(args.excel, args.output)
|
|
||||||
print(f"Safety tag mapping ({cnt} entries) written to {args.output}")
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd_safety_only(args: argparse.Namespace) -> None:
|
|
||||||
"""Generate only essential safety routines using DESC_IP data extraction (RST, STO, EPC)."""
|
|
||||||
|
|
||||||
# Check if ignore_estop1ok flag is set
|
|
||||||
ignore_estop1ok = getattr(args, 'ignore_estop1ok', False)
|
|
||||||
if ignore_estop1ok:
|
|
||||||
print("INFO: Ignoring ESTOP1OK tags in safety routines generation")
|
|
||||||
|
|
||||||
# Generate limited SafetyProgram with only 5 essential routines
|
|
||||||
safety_gen = SafetyProgramGenerator(args.excel, ignore_estop1ok=ignore_estop1ok)
|
|
||||||
safety_output = args.safety_output or 'SafetyProgram_Limited.L5X'
|
|
||||||
safety_gen.write(safety_output)
|
|
||||||
print(f"Limited Safety L5X written to {safety_output}")
|
|
||||||
|
|
||||||
# Generate limited MainProgram with only safety_tag_map and estop_check
|
|
||||||
main_gen = MainProgramGenerator(args.excel)
|
|
||||||
main_output = args.main_output or 'MainProgram_Limited.L5X'
|
|
||||||
main_gen.write(main_output)
|
|
||||||
print(f"Limited Main L5X written to {main_output}")
|
|
||||||
|
|
||||||
# CSV generation removed
|
|
||||||
|
|
||||||
# Create safety tag mapping for the limited mode
|
|
||||||
from data_loader import LimitedDataLoader
|
|
||||||
loader = LimitedDataLoader.from_excel(args.excel)
|
|
||||||
|
|
||||||
# Collect safety tags from RST sheet
|
|
||||||
safety_tags = set()
|
|
||||||
safety_tags.add("MCM_S_PB") # Static MCM tag
|
|
||||||
|
|
||||||
for _, row in loader.rst.iterrows():
|
|
||||||
if isinstance(row['DESCA'], str) and (any(k in row['DESCA'] for k in ('S1_PB', 'S2_PB')) or row['DESCA'].endswith('SPB')):
|
|
||||||
safety_tags.add(row['DESCA'])
|
|
||||||
|
|
||||||
mapping_output = args.mapping_output or 'SafetyTagMapping_Limited.txt'
|
|
||||||
from writers.mapping_writer import create_safety_tag_mapping
|
|
||||||
# For limited mode, we don't have beacon tags, so pass empty sets
|
|
||||||
create_safety_tag_mapping(safety_tags, set(), set(), mapping_output)
|
|
||||||
print(f"Limited safety tag mapping written to {mapping_output}")
|
|
||||||
|
|
||||||
print(f"\n[SUCCESS] Safety-only mode complete:")
|
|
||||||
print(f" - Safety routines: inputs, outputs, estops, resets")
|
|
||||||
print(f" - Main routines: safety_tag_map, estop_check")
|
|
||||||
print(f" - Data sources: DESC_IP extraction (RST, STO, EPC)")
|
|
||||||
|
|
||||||
|
|
||||||
_DEF_OUTPUTS = {
|
|
||||||
'safety': 'SafetyProgram_Generated.L5X',
|
|
||||||
'main': 'MainProgram_Generated.L5X',
|
|
||||||
'mapping': 'SafetyTagMapping.txt',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
|
||||||
p = argparse.ArgumentParser(prog='aoi-generator', description='Generate safety-focused L5X artifacts from Excel spec.')
|
|
||||||
sub = p.add_subparsers(dest='cmd', required=True)
|
|
||||||
|
|
||||||
# Safety
|
|
||||||
s = sub.add_parser('safety', help='Generate SafetyProgram L5X')
|
|
||||||
s.add_argument('excel', help='Merged descriptor Excel file')
|
|
||||||
s.add_argument('-o', '--output', default=_DEF_OUTPUTS['safety'])
|
|
||||||
s.set_defaults(func=_cmd_safety)
|
|
||||||
|
|
||||||
# Main
|
|
||||||
m = sub.add_parser('main', help='Generate MainProgram L5X')
|
|
||||||
m.add_argument('excel')
|
|
||||||
m.add_argument('-o', '--output', default=_DEF_OUTPUTS['main'])
|
|
||||||
m.set_defaults(func=_cmd_main)
|
|
||||||
|
|
||||||
# CSV subcommand removed
|
|
||||||
|
|
||||||
# Mapping
|
|
||||||
mp = sub.add_parser('mapping', help='Generate SafetyTag mapping file')
|
|
||||||
mp.add_argument('excel')
|
|
||||||
mp.add_argument('-o', '--output', default=_DEF_OUTPUTS['mapping'])
|
|
||||||
mp.set_defaults(func=_cmd_mapping)
|
|
||||||
|
|
||||||
# Safety-only mode
|
|
||||||
so = sub.add_parser('safety-only', help='Generate only essential safety routines (inputs, outputs, estops, resets, safety_tag_map, estop_check) using DESC_IP data extraction')
|
|
||||||
so.add_argument('excel', help='Merged descriptor Excel file')
|
|
||||||
so.add_argument('--safety-output', help='Safety L5X output file (default: SafetyProgram_Limited.L5X)')
|
|
||||||
so.add_argument('--main-output', help='Main L5X output file (default: MainProgram_Limited.L5X)')
|
|
||||||
# CSV options removed
|
|
||||||
so.add_argument('--mapping-output', help='Safety tag mapping output file (default: SafetyTagMapping_Limited.txt)')
|
|
||||||
so.add_argument('--ignore-estop1ok', action='store_true', help='Ignore ESTOP1OK tags in inputs and estops routines')
|
|
||||||
so.set_defaults(func=_cmd_safety_only)
|
|
||||||
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv: list[str] | None = None) -> None:
|
|
||||||
parser = build_parser()
|
|
||||||
args = parser.parse_args(argv)
|
|
||||||
args.func(args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@ -10,6 +10,7 @@ class FileConfig:
|
|||||||
"""File path configuration."""
|
"""File path configuration."""
|
||||||
excel_file: Path = Path('DESC_IP_MERGED.xlsx')
|
excel_file: Path = Path('DESC_IP_MERGED.xlsx')
|
||||||
output_dir: Path = Path('.')
|
output_dir: Path = Path('.')
|
||||||
|
zones_file: Path = Path('zones.json')
|
||||||
|
|
||||||
# Output file names
|
# Output file names
|
||||||
safety_l5x: str = 'SafetyProgram_Generated.L5X'
|
safety_l5x: str = 'SafetyProgram_Generated.L5X'
|
||||||
@ -126,6 +127,23 @@ class RoutineConfig:
|
|||||||
# Safety tag mapping prefix and MCM input address
|
# Safety tag mapping prefix and MCM input address
|
||||||
safety_tag_prefix: str = 'SFT_'
|
safety_tag_prefix: str = 'SFT_'
|
||||||
mcm_input_address: str = 'Local:5:I.Data.0'
|
mcm_input_address: str = 'Local:5:I.Data.0'
|
||||||
|
# Global control and status tags
|
||||||
|
mcm_base_tag: str = 'MCM'
|
||||||
|
mcm_ctrl_tag: str = 'MCM.CTRL'
|
||||||
|
rack_fault_tag: str = 'Rack.AOI.Slot2_EN4TR_Faulted'
|
||||||
|
mcm_epb_status_tag: str = 'MCM_EPB_STATUS'
|
||||||
|
top_level_estop_ok_tag: str = 'EStop_MCM_OK'
|
||||||
|
station_ctrl_tag: str = 'Station.CTRL'
|
||||||
|
# MCM EPB wiring and tag names
|
||||||
|
mcm_epb_status_inputs: List[str] = field(default_factory=lambda: [
|
||||||
|
'Local:7:I.Pt02.Status',
|
||||||
|
'Local:7:I.Pt03.Status',
|
||||||
|
])
|
||||||
|
mcm_epb_dcs_inputs: List[str] = field(default_factory=lambda: [
|
||||||
|
'Local:7:I.Pt02.Data',
|
||||||
|
'Local:7:I.Pt03.Data',
|
||||||
|
])
|
||||||
|
mcm_epb_dcs_tag_name: str = 'MCM_EPB_DCS_CTRL'
|
||||||
|
|
||||||
# Safety routine names
|
# Safety routine names
|
||||||
inputs_routine: str = 'R010_INPUTS'
|
inputs_routine: str = 'R010_INPUTS'
|
||||||
@ -138,6 +156,39 @@ class RoutineConfig:
|
|||||||
mcm_safety_tag: str = 'MCM_S_PB'
|
mcm_safety_tag: str = 'MCM_S_PB'
|
||||||
mcm_epb_tag: str = 'MCM_EPB_DCS_CTRL.O1'
|
mcm_epb_tag: str = 'MCM_EPB_DCS_CTRL.O1'
|
||||||
|
|
||||||
|
# Speed control and auxiliaries
|
||||||
|
speed_ctrl_setpoint_tag: str = 'Speed_350_FPM'
|
||||||
|
speed_ctrl_setpoint_value: int = 350
|
||||||
|
no_horn_tag_name: str = 'NO_Horn'
|
||||||
|
# AOI/APF defaults
|
||||||
|
apf_input_default: str = 'In_0'
|
||||||
|
|
||||||
|
# MCM AOI argument lists (after AOI, HMI, CTRL)
|
||||||
|
# Provide defaults matching current project wiring
|
||||||
|
mcm_aoi_input_args: List[str] = field(default_factory=lambda: [
|
||||||
|
'Local:5:I.Data.2',
|
||||||
|
'Local:5:I.Data.5',
|
||||||
|
'Local:5:I.Data.4',
|
||||||
|
'Local:5:I.Data.0',
|
||||||
|
'Local:5:I.Data.3',
|
||||||
|
'Local:7:I.Pt02.Data',
|
||||||
|
'Local:7:I.Pt03.Data',
|
||||||
|
'Local:5:I.Data.1',
|
||||||
|
'Local:7:I.Pt00.Data',
|
||||||
|
'Local:5:I.Data.7',
|
||||||
|
'Local:5:I.Data.8',
|
||||||
|
'Local:5:I.Data.6',
|
||||||
|
'Local:5:I.Data.9',
|
||||||
|
])
|
||||||
|
mcm_aoi_output_args: List[str] = field(default_factory=lambda: [
|
||||||
|
'Local:6:O.Data.2',
|
||||||
|
'Local:6:O.Data.5',
|
||||||
|
'Local:6:O.Data.4',
|
||||||
|
'Local:6:O.Data.0',
|
||||||
|
'Local:6:O.Data.1',
|
||||||
|
'Local:6:O.Data.3',
|
||||||
|
])
|
||||||
|
|
||||||
# Routine name map for all known plugins (can be overridden in config)
|
# Routine name map for all known plugins (can be overridden in config)
|
||||||
name_map: Dict[str, str] = field(default_factory=lambda: {
|
name_map: Dict[str, str] = field(default_factory=lambda: {
|
||||||
'main_routine': 'MainRoutine',
|
'main_routine': 'MainRoutine',
|
||||||
@ -227,6 +278,8 @@ class GeneratorConfig:
|
|||||||
files_data['excel_file'] = Path(files_data['excel_file'])
|
files_data['excel_file'] = Path(files_data['excel_file'])
|
||||||
if 'output_dir' in files_data:
|
if 'output_dir' in files_data:
|
||||||
files_data['output_dir'] = Path(files_data['output_dir'])
|
files_data['output_dir'] = Path(files_data['output_dir'])
|
||||||
|
if 'zones_file' in files_data and files_data['zones_file']:
|
||||||
|
files_data['zones_file'] = Path(files_data['zones_file'])
|
||||||
|
|
||||||
files = FileConfig(**files_data)
|
files = FileConfig(**files_data)
|
||||||
xml = XMLConfig(**config_data.get('xml', {}))
|
xml = XMLConfig(**config_data.get('xml', {}))
|
||||||
@ -237,16 +290,22 @@ class GeneratorConfig:
|
|||||||
routine_plan: List[RoutineEntry] = []
|
routine_plan: List[RoutineEntry] = []
|
||||||
routines: RoutineConfig
|
routines: RoutineConfig
|
||||||
if isinstance(routines_section, list):
|
if isinstance(routines_section, list):
|
||||||
# New plan format under key 'routines'
|
# Legacy: plan mistakenly stored under 'routines' key
|
||||||
for entry in routines_section:
|
for entry in routines_section:
|
||||||
try:
|
try:
|
||||||
routine_plan.append(RoutineEntry(**entry))
|
routine_plan.append(RoutineEntry(**entry))
|
||||||
except Exception:
|
except Exception:
|
||||||
# Skip invalid entries silently to keep robustness
|
|
||||||
continue
|
continue
|
||||||
routines = RoutineConfig()
|
routines = RoutineConfig()
|
||||||
else:
|
else:
|
||||||
routines = RoutineConfig(**routines_section)
|
routines = RoutineConfig(**routines_section)
|
||||||
|
# Prefer explicit routine_plan key when present
|
||||||
|
if not routine_plan and isinstance(config_data.get('routine_plan', None), list):
|
||||||
|
for entry in config_data['routine_plan']:
|
||||||
|
try:
|
||||||
|
routine_plan.append(RoutineEntry(**entry))
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
# Optional filters section
|
# Optional filters section
|
||||||
filters_section = config_data.get('filters', {})
|
filters_section = config_data.get('filters', {})
|
||||||
@ -271,6 +330,7 @@ class GeneratorConfig:
|
|||||||
'xml': self.xml.__dict__,
|
'xml': self.xml.__dict__,
|
||||||
'extraction': self.extraction.__dict__,
|
'extraction': self.extraction.__dict__,
|
||||||
'routines': self.routines.__dict__,
|
'routines': self.routines.__dict__,
|
||||||
|
'routine_plan': [e.__dict__ for e in self.routine_plan],
|
||||||
'tags': self.tags,
|
'tags': self.tags,
|
||||||
}
|
}
|
||||||
# Intentionally do not overwrite user's list-based routines if present; save only legacy schema by default.
|
# Intentionally do not overwrite user's list-based routines if present; save only legacy schema by default.
|
||||||
|
|||||||
@ -6,6 +6,7 @@ from typing import Optional, List, Dict, Any
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import re
|
import re
|
||||||
from .utils.common import natural_sort_key
|
from .utils.common import natural_sort_key
|
||||||
|
from .logging_config import get_logger
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DataLoader:
|
class DataLoader:
|
||||||
@ -14,8 +15,8 @@ class DataLoader:
|
|||||||
Removes the complexity of dual-mode operation since sheet-based
|
Removes the complexity of dual-mode operation since sheet-based
|
||||||
mode is deprecated and will never be used.
|
mode is deprecated and will never be used.
|
||||||
|
|
||||||
Zones configuration is provided via zones_dict parameter or
|
Zones configuration is provided via `files.zones_file` (zones.json) in
|
||||||
defaults to zones_config.py. No longer reads ZONES sheet from Excel.
|
generator_config.json. ZONES sheet in Excel is ignored and not used.
|
||||||
|
|
||||||
Supported Excel sheets: DESC_IP only
|
Supported Excel sheets: DESC_IP only
|
||||||
External files: IO-To-Path.xlsx (for IO path mappings)
|
External files: IO-To-Path.xlsx (for IO path mappings)
|
||||||
@ -35,6 +36,7 @@ class DataLoader:
|
|||||||
self.excel_path = Path(self.excel_path)
|
self.excel_path = Path(self.excel_path)
|
||||||
self._extracted_data = {}
|
self._extracted_data = {}
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
|
self._logger = get_logger(self.__class__.__name__)
|
||||||
|
|
||||||
# Zones deprecated: ensure zones_dict is an empty list
|
# Zones deprecated: ensure zones_dict is an empty list
|
||||||
self.zones_dict = []
|
self.zones_dict = []
|
||||||
@ -60,10 +62,10 @@ class DataLoader:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def network(self) -> pd.DataFrame:
|
def network(self) -> pd.DataFrame:
|
||||||
"""Get NETWORK sheet data."""
|
"""Get NETWORK_PLC sheet data."""
|
||||||
if 'network' not in self._cache:
|
if 'network' not in self._cache:
|
||||||
try:
|
try:
|
||||||
df = pd.read_excel(self.excel_path, sheet_name='NETWORK')
|
df = pd.read_excel(self.excel_path, sheet_name='NETWORK_PLC')
|
||||||
# Coerce commonly used text columns to string to allow safe .str usage
|
# Coerce commonly used text columns to string to allow safe .str usage
|
||||||
for col in ['Name', 'PartNumber', 'DPM']:
|
for col in ['Name', 'PartNumber', 'DPM']:
|
||||||
if col in df.columns:
|
if col in df.columns:
|
||||||
@ -117,28 +119,35 @@ class DataLoader:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def zones(self) -> pd.DataFrame:
|
def zones(self) -> pd.DataFrame:
|
||||||
"""Load zones from zones.json configuration.
|
"""Load zones from configurable JSON (files.zones_file in config).
|
||||||
|
|
||||||
The JSON file is expected at project root as `zones.json` with a
|
The JSON should contain a top-level object keyed by subsystem
|
||||||
top-level object keyed by subsystem (e.g., MCM01, MCM04) and a
|
(e.g., MCM01, MCM04) with a `DEFAULT` fallback. Each entry is a list
|
||||||
`DEFAULT` fallback. Each entry is a list of objects having
|
of objects with fields: name, start, stop, interlock.
|
||||||
fields: name, start, stop, interlock.
|
|
||||||
"""
|
"""
|
||||||
if 'zones' in self._cache:
|
if 'zones' in self._cache:
|
||||||
return self._cache['zones']
|
return self._cache['zones']
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
# Determine subsystem from Excel path
|
# Determine subsystem from Excel path
|
||||||
import re
|
import re
|
||||||
excel_path_str = str(self.excel_path)
|
excel_path_str = str(self.excel_path)
|
||||||
m = re.search(r"(MCM\d+)", excel_path_str, re.IGNORECASE)
|
m = re.search(r"(MCM\d+)", excel_path_str, re.IGNORECASE)
|
||||||
subsystem = (m.group(1).upper() if m else 'DEFAULT')
|
subsystem = (m.group(1).upper() if m else 'DEFAULT')
|
||||||
|
|
||||||
json_path = Path(__file__).parent.parent.parent / 'zones copy.json'
|
# Resolve zones file from config
|
||||||
|
try:
|
||||||
|
from .config import get_config
|
||||||
|
cfg = get_config()
|
||||||
|
zones_file = cfg.files.zones_file
|
||||||
|
except Exception:
|
||||||
|
zones_file = Path('zones.json')
|
||||||
|
# Make path relative to project root if needed
|
||||||
|
if not zones_file.is_absolute():
|
||||||
|
zones_file = Path(__file__).parent.parent.parent / zones_file
|
||||||
zones_df = pd.DataFrame(columns=['name', 'start', 'stop', 'interlock'])
|
zones_df = pd.DataFrame(columns=['name', 'start', 'stop', 'interlock'])
|
||||||
try:
|
try:
|
||||||
with open(json_path, 'r', encoding='utf-8') as f:
|
with open(zones_file, 'r', encoding='utf-8') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
|
||||||
# Pick group: prefer explicit subsystem; otherwise, choose the best
|
# Pick group: prefer explicit subsystem; otherwise, choose the best
|
||||||
@ -390,12 +399,12 @@ class DataLoader:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def apf(self) -> pd.DataFrame:
|
def apf(self) -> pd.DataFrame:
|
||||||
"""Get APF data from NETWORK sheet."""
|
"""Get APF data from NETWORK_PLC sheet."""
|
||||||
return self._extract_apf()
|
return self._extract_apf()
|
||||||
|
|
||||||
def _extract_apf(self) -> pd.DataFrame:
|
def _extract_apf(self) -> pd.DataFrame:
|
||||||
"""Extract APF (Variable Frequency Drive) data from NETWORK sheet."""
|
"""Extract APF (Variable Frequency Drive) data from NETWORK_PLC sheet."""
|
||||||
# Use centralized NETWORK accessor which normalizes dtypes
|
# Use centralized NETWORK_PLC accessor which normalizes dtypes
|
||||||
network_df = self.network
|
network_df = self.network
|
||||||
if network_df.empty:
|
if network_df.empty:
|
||||||
return pd.DataFrame()
|
return pd.DataFrame()
|
||||||
@ -426,12 +435,12 @@ class DataLoader:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def extendo(self) -> pd.DataFrame:
|
def extendo(self) -> pd.DataFrame:
|
||||||
"""Get EXTENDO data from NETWORK sheet."""
|
"""Get EXTENDO data from NETWORK_PLC sheet."""
|
||||||
return self._extract_extendo()
|
return self._extract_extendo()
|
||||||
|
|
||||||
def _extract_extendo(self) -> pd.DataFrame:
|
def _extract_extendo(self) -> pd.DataFrame:
|
||||||
"""Extract EXTENDO (Siemens ET 200SP) data from NETWORK sheet."""
|
"""Extract EXTENDO (Siemens ET 200SP) data from NETWORK_PLC sheet."""
|
||||||
# Use centralized NETWORK accessor which normalizes dtypes
|
# Use centralized NETWORK_PLC accessor which normalizes dtypes
|
||||||
network_df = self.network
|
network_df = self.network
|
||||||
if network_df.empty:
|
if network_df.empty:
|
||||||
return pd.DataFrame()
|
return pd.DataFrame()
|
||||||
@ -504,7 +513,7 @@ class DataLoader:
|
|||||||
return fpe_data
|
return fpe_data
|
||||||
|
|
||||||
def _extract_d2c_data(self) -> dict:
|
def _extract_d2c_data(self) -> dict:
|
||||||
"""Extract D2C data from DESC_IP and NETWORK sheets."""
|
"""Extract D2C data from DESC_IP and NETWORK_PLC sheets."""
|
||||||
import re
|
import re
|
||||||
# Get config tokens
|
# Get config tokens
|
||||||
cfg = None
|
cfg = None
|
||||||
@ -519,7 +528,7 @@ class DataLoader:
|
|||||||
bcn_token = (getattr(cfg.extraction, 'd2c_bcn_token', None) if cfg else None) or 'BCN'
|
bcn_token = (getattr(cfg.extraction, 'd2c_bcn_token', None) if cfg else None) or 'BCN'
|
||||||
zmx_suffix = (getattr(cfg.extraction, 'd2c_zmx_suffix', None) if cfg else None) or '_ZMX'
|
zmx_suffix = (getattr(cfg.extraction, 'd2c_zmx_suffix', None) if cfg else None) or '_ZMX'
|
||||||
|
|
||||||
# Get DESC_IP and NETWORK dataframes
|
# Get DESC_IP and NETWORK_PLC dataframes
|
||||||
desc_ip_df = self.desc_ip
|
desc_ip_df = self.desc_ip
|
||||||
network_df = self.network
|
network_df = self.network
|
||||||
if desc_ip_df.empty or network_df.empty:
|
if desc_ip_df.empty or network_df.empty:
|
||||||
@ -556,7 +565,7 @@ class DataLoader:
|
|||||||
stack_type = '3 STACK' if '3 STACK' in desca_upper or '3-STACK' in desca_upper else '2 STACK'
|
stack_type = '3 STACK' if '3 STACK' in desca_upper or '3-STACK' in desca_upper else '2 STACK'
|
||||||
bcn_name = bcn_row['DESCA'].split()[0] if pd.notna(bcn_row.get('DESCA')) else f"{s0}_{bcn_token}1"
|
bcn_name = bcn_row['DESCA'].split()[0] if pd.notna(bcn_row.get('DESCA')) else f"{s0}_{bcn_token}1"
|
||||||
s0_data[s0]['bcn'] = { 'tagname': bcn_name, 'stack_type': stack_type }
|
s0_data[s0]['bcn'] = { 'tagname': bcn_name, 'stack_type': stack_type }
|
||||||
# ZMX in NETWORK
|
# ZMX in NETWORK_PLC
|
||||||
zmx = network_df[network_df['Name'].astype(str).str.contains(f'{re.escape(s0)}{re.escape(zmx_suffix)}', na=False, regex=True)]
|
zmx = network_df[network_df['Name'].astype(str).str.contains(f'{re.escape(s0)}{re.escape(zmx_suffix)}', na=False, regex=True)]
|
||||||
if not zmx.empty:
|
if not zmx.empty:
|
||||||
zmx_row = zmx.iloc[0]
|
zmx_row = zmx.iloc[0]
|
||||||
@ -844,7 +853,7 @@ class DataLoader:
|
|||||||
|
|
||||||
def _extract_fpe_data(self) -> Dict[str, Dict[str, str]]:
|
def _extract_fpe_data(self) -> Dict[str, Dict[str, str]]:
|
||||||
"""Extract FPE (Full Photo Eye) data."""
|
"""Extract FPE (Full Photo Eye) data."""
|
||||||
print("\n [DataLoader] Extracting FPE data...")
|
self._logger.debug("Extracting FPE data", stage="data_extraction", type="FPE")
|
||||||
|
|
||||||
desc_ip = self.desc_ip
|
desc_ip = self.desc_ip
|
||||||
network = self.network
|
network = self.network
|
||||||
@ -862,10 +871,10 @@ class DataLoader:
|
|||||||
fpe_entries = desc_ip[fpe_mask]
|
fpe_entries = desc_ip[fpe_mask]
|
||||||
|
|
||||||
if fpe_entries.empty:
|
if fpe_entries.empty:
|
||||||
print(" No FPE entries found")
|
self._logger.info("Data: FPE count=0")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
print(f" Found {len(fpe_entries)} FPE entries")
|
self._logger.info(f"Data: FPE count={len(fpe_entries)}")
|
||||||
|
|
||||||
fpe_data = {}
|
fpe_data = {}
|
||||||
|
|
||||||
@ -897,7 +906,7 @@ class DataLoader:
|
|||||||
print(f" [WARNING] Could not extract base name from {fpe_name}")
|
print(f" [WARNING] Could not extract base name from {fpe_name}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f"\n Processing {fpe_name} (base: {base_name})")
|
self._logger.debug(f"FPE processing {fpe_name} base={base_name}")
|
||||||
|
|
||||||
# Find associated VFD from network sheet
|
# Find associated VFD from network sheet
|
||||||
vfd_name = f"{base_name}_VFD1"
|
vfd_name = f"{base_name}_VFD1"
|
||||||
@ -911,7 +920,7 @@ class DataLoader:
|
|||||||
if not vfd_row.empty:
|
if not vfd_row.empty:
|
||||||
conveyor_ctrl = f"{vfd_name}.CTRL"
|
conveyor_ctrl = f"{vfd_name}.CTRL"
|
||||||
else:
|
else:
|
||||||
print(f" [WARNING] No VFD found for {base_name}")
|
self._logger.debug(f"No VFD found for {base_name}")
|
||||||
vfd_name = f"{base_name}_VFD1" # Use default VFD name
|
vfd_name = f"{base_name}_VFD1" # Use default VFD name
|
||||||
conveyor_ctrl = f"{vfd_name}.CTRL"
|
conveyor_ctrl = f"{vfd_name}.CTRL"
|
||||||
|
|
||||||
@ -967,12 +976,12 @@ class DataLoader:
|
|||||||
'beacon_output': beacon_output
|
'beacon_output': beacon_output
|
||||||
}
|
}
|
||||||
|
|
||||||
print(f" Conveyor: {conveyor_ctrl}")
|
# Detailed per-device values logged at DEBUG to reduce noise
|
||||||
print(f" Parent Fault: {parent_comm_fault}")
|
self._logger.debug(
|
||||||
print(f" Input Path: {input_path}")
|
f"FPE {fpe_name}: conveyor={conveyor_ctrl} parent_fault={parent_comm_fault} input={input_path} beacon={beacon_output}"
|
||||||
print(f" Beacon Output: {beacon_output}")
|
)
|
||||||
|
|
||||||
print(f"\n Extracted {len(fpe_data)} FPE configurations")
|
self._logger.info(f"Data: FPE configs={len(fpe_data)}")
|
||||||
return fpe_data
|
return fpe_data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -986,12 +995,12 @@ class DataLoader:
|
|||||||
return pmm_data
|
return pmm_data
|
||||||
|
|
||||||
def _extract_pmm_data(self) -> Dict[str, Dict[str, Any]]:
|
def _extract_pmm_data(self) -> Dict[str, Dict[str, Any]]:
|
||||||
"""Extract PMM data from NETWORK and DESC sheets."""
|
"""Extract PMM data from NETWORK_PLC and DESC sheets."""
|
||||||
pmm_data = {}
|
pmm_data = {}
|
||||||
|
|
||||||
print("\n=== Extracting PMM Data ===")
|
self._logger.debug("Extracting PMM data", stage="data_extraction", type="PMM")
|
||||||
|
|
||||||
# Get PMM entries from NETWORK sheet (part number 1420-V2-ENT)
|
# Get PMM entries from NETWORK_PLC sheet (part number 1420-V2-ENT)
|
||||||
network = self.network
|
network = self.network
|
||||||
desc_ip = self.desc_ip
|
desc_ip = self.desc_ip
|
||||||
|
|
||||||
@ -1005,15 +1014,15 @@ class DataLoader:
|
|||||||
target = (getattr(cfg.extraction, 'pmm_partnumber_exact', None) if cfg else None) or ['1420-V2-ENT']
|
target = (getattr(cfg.extraction, 'pmm_partnumber_exact', None) if cfg else None) or ['1420-V2-ENT']
|
||||||
pmm_entries = network[network['PartNumber'].isin(target)]
|
pmm_entries = network[network['PartNumber'].isin(target)]
|
||||||
|
|
||||||
print(f"Found {len(pmm_entries)} PMM entries")
|
self._logger.info(f"Data: PMM entries={len(pmm_entries)}")
|
||||||
|
|
||||||
for _, pmm in pmm_entries.iterrows():
|
for _, pmm in pmm_entries.iterrows():
|
||||||
pmm_name = pmm['Name']
|
pmm_name = pmm['Name']
|
||||||
print(f"\n Processing PMM: {pmm_name}")
|
self._logger.debug(f"PMM processing {pmm_name}")
|
||||||
|
|
||||||
# Get DPM association
|
# Get DPM association
|
||||||
dpm_name = pmm.get('DPM', 'MCM')
|
dpm_name = pmm.get('DPM', 'MCM')
|
||||||
print(f" DPM: {dpm_name}")
|
self._logger.debug(f"PMM {pmm_name} DPM={dpm_name}")
|
||||||
|
|
||||||
# Find PWM (Phase/Power Monitor) fault input
|
# Find PWM (Phase/Power Monitor) fault input
|
||||||
# Extract base name (e.g., PDP11 from PDP11_PMM1)
|
# Extract base name (e.g., PDP11 from PDP11_PMM1)
|
||||||
@ -1030,7 +1039,7 @@ class DataLoader:
|
|||||||
io_path = pwm_row.get('IO_PATH', '')
|
io_path = pwm_row.get('IO_PATH', '')
|
||||||
if io_path:
|
if io_path:
|
||||||
pwm_fault_io = io_path
|
pwm_fault_io = io_path
|
||||||
print(f" PWM Fault IO: {pwm_fault_io}")
|
self._logger.debug(f"PMM {pmm_name} PWM fault IO {pwm_fault_io}")
|
||||||
else:
|
else:
|
||||||
# Build from TAGNAME and TERM
|
# Build from TAGNAME and TERM
|
||||||
tagname = pwm_row['TAGNAME']
|
tagname = pwm_row['TAGNAME']
|
||||||
@ -1039,7 +1048,7 @@ class DataLoader:
|
|||||||
# Convert term format (IO12 -> Pt12)
|
# Convert term format (IO12 -> Pt12)
|
||||||
pt_num = term.replace('IO', 'Pt')
|
pt_num = term.replace('IO', 'Pt')
|
||||||
pwm_fault_io = f"{tagname}:I.{pt_num}.Data"
|
pwm_fault_io = f"{tagname}:I.{pt_num}.Data"
|
||||||
print(f" PWM Fault IO (constructed): {pwm_fault_io}")
|
self._logger.debug(f"PMM {pmm_name} PWM fault IO (constructed) {pwm_fault_io}")
|
||||||
|
|
||||||
# Store PMM configuration
|
# Store PMM configuration
|
||||||
pmm_data[pmm_name] = {
|
pmm_data[pmm_name] = {
|
||||||
@ -1048,9 +1057,9 @@ class DataLoader:
|
|||||||
'parent_comm_fault': f"{dpm_name}:I.ConnectionFaulted"
|
'parent_comm_fault': f"{dpm_name}:I.ConnectionFaulted"
|
||||||
}
|
}
|
||||||
|
|
||||||
print(f" Configuration stored for {pmm_name}")
|
self._logger.debug(f"PMM {pmm_name} configuration stored")
|
||||||
|
|
||||||
print(f"\n Extracted {len(pmm_data)} PMM configurations")
|
self._logger.info(f"Data: PMM configs={len(pmm_data)}")
|
||||||
return pmm_data
|
return pmm_data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -1067,7 +1076,7 @@ class DataLoader:
|
|||||||
"""Extract CB_MONITOR data from DESC sheets."""
|
"""Extract CB_MONITOR data from DESC sheets."""
|
||||||
cb_monitor_data = {}
|
cb_monitor_data = {}
|
||||||
|
|
||||||
print("\n=== Extracting CB_MONITOR Data ===")
|
self._logger.debug("Extracting CB_MONITOR data", stage="data_extraction", type="CB_MONITOR")
|
||||||
|
|
||||||
desc_ip = self.desc_ip
|
desc_ip = self.desc_ip
|
||||||
|
|
||||||
@ -1135,14 +1144,14 @@ class DataLoader:
|
|||||||
if pd.notna(cb['TAGNAME']):
|
if pd.notna(cb['TAGNAME']):
|
||||||
pdp_fios[pdp_base].add(cb['TAGNAME'])
|
pdp_fios[pdp_base].add(cb['TAGNAME'])
|
||||||
|
|
||||||
print(f"Found CB entries for {len(pdp_cbs)} PDPs")
|
self._logger.info(f"Data: CB PDPs={len(pdp_cbs)}")
|
||||||
|
|
||||||
# Now create the CB_MONITOR configurations
|
# Now create the CB_MONITOR configurations
|
||||||
for pdp_base in sorted(pdp_cbs.keys()):
|
for pdp_base in sorted(pdp_cbs.keys()):
|
||||||
cb_ios = pdp_cbs[pdp_base]
|
cb_ios = pdp_cbs[pdp_base]
|
||||||
fios = pdp_fios[pdp_base]
|
fios = pdp_fios[pdp_base]
|
||||||
|
|
||||||
print(f"\n Processing {pdp_base} with {len(cb_ios)} CBs from {len(fios)} FIO(s)")
|
self._logger.debug(f"CB_MONITOR {pdp_base}: CBs={len(cb_ios)} FIOs={len(fios)}")
|
||||||
|
|
||||||
# Create ordered list of CB IO paths (CB1 through CB26)
|
# Create ordered list of CB IO paths (CB1 through CB26)
|
||||||
cb_list = []
|
cb_list = []
|
||||||
@ -1166,11 +1175,11 @@ class DataLoader:
|
|||||||
'connection_fault': connection_fault
|
'connection_fault': connection_fault
|
||||||
}
|
}
|
||||||
|
|
||||||
print(f" Configured {pdp_base} with {len([cb for cb in cb_list if cb != '0'])} CBs")
|
self._logger.debug(
|
||||||
print(f" CB positions: {sorted([i for i in cb_ios.keys()])}")
|
f"CB_MONITOR {pdp_base}: configured CBs={len([cb for cb in cb_list if cb != '0'])} positions={sorted([i for i in cb_ios.keys()])} fault={connection_fault}"
|
||||||
print(f" Connection fault: {connection_fault}")
|
)
|
||||||
|
|
||||||
print(f"\n Extracted {len(cb_monitor_data)} CB_MONITOR configurations")
|
self._logger.info(f"Data: CB configs={len(cb_monitor_data)}")
|
||||||
return cb_monitor_data
|
return cb_monitor_data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -1184,10 +1193,10 @@ class DataLoader:
|
|||||||
return flow_ctrl_data
|
return flow_ctrl_data
|
||||||
|
|
||||||
def _extract_flow_ctrl_data(self) -> Dict[str, Dict[str, Any]]:
|
def _extract_flow_ctrl_data(self) -> Dict[str, Dict[str, Any]]:
|
||||||
"""Extract FLOW_CTRL data from NETWORK sheet."""
|
"""Extract FLOW_CTRL data from NETWORK_PLC sheet."""
|
||||||
flow_ctrl_data = {}
|
flow_ctrl_data = {}
|
||||||
|
|
||||||
print("\n=== Extracting FLOW_CTRL Data ===")
|
self._logger.debug("Extracting FLOW_CTRL data", stage="data_extraction", type="FLOW_CTRL")
|
||||||
|
|
||||||
network = self.network
|
network = self.network
|
||||||
|
|
||||||
@ -1203,8 +1212,7 @@ class DataLoader:
|
|||||||
extendo_entries = network[name_series.str.endswith('_EX1', na=False)]
|
extendo_entries = network[name_series.str.endswith('_EX1', na=False)]
|
||||||
vfd_entries = network[name_series.str.endswith('_VFD1', na=False)]
|
vfd_entries = network[name_series.str.endswith('_VFD1', na=False)]
|
||||||
|
|
||||||
print(f"Found {len(extendo_entries)} EXTENDO devices")
|
self._logger.info(f"Data: EXTENDO={len(extendo_entries)} VFD={len(vfd_entries)}")
|
||||||
print(f"Found {len(vfd_entries)} VFD devices")
|
|
||||||
|
|
||||||
# Group by union of DPMs from EXTENDO and VFD so lanes without EXTENDO aren't dropped
|
# Group by union of DPMs from EXTENDO and VFD so lanes without EXTENDO aren't dropped
|
||||||
dpm_keys = sorted(set(extendo_entries['DPM'].dropna().unique()).union(set(vfd_entries['DPM'].dropna().unique())))
|
dpm_keys = sorted(set(extendo_entries['DPM'].dropna().unique()).union(set(vfd_entries['DPM'].dropna().unique())))
|
||||||
@ -1212,9 +1220,7 @@ class DataLoader:
|
|||||||
dpm_extendos = extendo_entries[extendo_entries['DPM'] == dpm] if 'DPM' in extendo_entries.columns else extendo_entries.iloc[0:0]
|
dpm_extendos = extendo_entries[extendo_entries['DPM'] == dpm] if 'DPM' in extendo_entries.columns else extendo_entries.iloc[0:0]
|
||||||
dpm_vfds = vfd_entries[vfd_entries['DPM'] == dpm] if 'DPM' in vfd_entries.columns else vfd_entries.iloc[0:0]
|
dpm_vfds = vfd_entries[vfd_entries['DPM'] == dpm] if 'DPM' in vfd_entries.columns else vfd_entries.iloc[0:0]
|
||||||
|
|
||||||
print(f"\n Processing DPM: {dpm}")
|
self._logger.debug(f"FLOW_CTRL DPM={dpm} extendos={len(dpm_extendos)} vfds={len(dpm_vfds)}")
|
||||||
print(f" EXTENDOs: {len(dpm_extendos)}")
|
|
||||||
print(f" VFDs: {len(dpm_vfds)}")
|
|
||||||
|
|
||||||
# Create a list of devices for this DPM
|
# Create a list of devices for this DPM
|
||||||
devices = []
|
devices = []
|
||||||
@ -1258,7 +1264,7 @@ class DataLoader:
|
|||||||
'devices': devices
|
'devices': devices
|
||||||
}
|
}
|
||||||
|
|
||||||
print(f"\n Extracted FLOW_CTRL configurations for {len(flow_ctrl_data)} DPMs")
|
self._logger.info(f"Data: FLOW_CTRL DPMs={len(flow_ctrl_data)}")
|
||||||
return flow_ctrl_data
|
return flow_ctrl_data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -1272,10 +1278,10 @@ class DataLoader:
|
|||||||
return speed_ctrl_data
|
return speed_ctrl_data
|
||||||
|
|
||||||
def _extract_speed_ctrl_data(self) -> List[str]:
|
def _extract_speed_ctrl_data(self) -> List[str]:
|
||||||
"""Extract VFD names for SPEED_CTRL from NETWORK sheet."""
|
"""Extract VFD names for SPEED_CTRL from NETWORK_PLC sheet."""
|
||||||
vfd_names = []
|
vfd_names = []
|
||||||
|
|
||||||
print("\n=== Extracting SPEED_CTRL Data ===")
|
self._logger.debug("Extracting SPEED_CTRL data", stage="data_extraction", type="SPEED_CTRL")
|
||||||
|
|
||||||
network = self.network
|
network = self.network
|
||||||
|
|
||||||
@ -1292,7 +1298,7 @@ class DataLoader:
|
|||||||
vfd_mask = vfd_mask | network['PartNumber'].str.startswith(p, na=False)
|
vfd_mask = vfd_mask | network['PartNumber'].str.startswith(p, na=False)
|
||||||
vfd_entries = network[vfd_mask]
|
vfd_entries = network[vfd_mask]
|
||||||
|
|
||||||
print(f"Found {len(vfd_entries)} VFD devices for speed control")
|
self._logger.info(f"Data: SPEED_CTRL VFDs={len(vfd_entries)}")
|
||||||
|
|
||||||
# Extract VFD names
|
# Extract VFD names
|
||||||
for _, vfd in vfd_entries.iterrows():
|
for _, vfd in vfd_entries.iterrows():
|
||||||
@ -1302,8 +1308,8 @@ class DataLoader:
|
|||||||
# Sort for consistent output
|
# Sort for consistent output
|
||||||
vfd_names.sort(key=natural_sort_key)
|
vfd_names.sort(key=natural_sort_key)
|
||||||
|
|
||||||
print(f" VFDs: {', '.join(vfd_names[:5])}..." if len(vfd_names) > 5 else f" VFDs: {', '.join(vfd_names)}")
|
self._logger.debug("SPEED_CTRL sample=" + (', '.join(vfd_names[:5]) + ('...' if len(vfd_names) > 5 else '')))
|
||||||
print(f"\n Extracted {len(vfd_names)} VFDs for SPEED_CTRL")
|
self._logger.info(f"Data: SPEED_CTRL count={len(vfd_names)}")
|
||||||
return vfd_names
|
return vfd_names
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user