PLC_Generation/PLC Data Generator/extract_fio_fioh.py
2025-08-05 14:38:54 +04:00

189 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

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

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)