189 lines
7.0 KiB
Python
189 lines
7.0 KiB
Python
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) |