290 lines
8.2 KiB
Python
290 lines
8.2 KiB
Python
import os
|
|
import pandas as pd
|
|
|
|
SHEET_NAME = "BLOCK LIST_SORTED"
|
|
|
|
# MCP panel extra descriptions (26 rows)
|
|
PANEL_EXTRA_DESCRIPTIONS = [
|
|
"120VAC CB01 AUX",
|
|
"RECP CB02 AUX",
|
|
"PS CB03 AUX",
|
|
"RACK1 CB04 AUX",
|
|
"RACK2 CB05 AUX",
|
|
"ENET SW CB06 AUX",
|
|
"PNL 24 CB07 AUX",
|
|
|
|
"MCP RESET PB",
|
|
"MCP START PB",
|
|
"PS1 MONITORING",
|
|
"ENET SWITCH MONITORING",
|
|
"LAMP TEST PB",
|
|
|
|
"PANEL E-STOP PB",
|
|
"PANEL STOP PB",
|
|
"MAINTENANCE MODE PB",
|
|
"MASTER_ESTOP/EVAC SIGNAL",
|
|
|
|
"PANEL STOP PB LIGHT",
|
|
"PS1 OK LIGHT",
|
|
"SYSTEM START LIGHT",
|
|
"RESET BUTTON LIGHT",
|
|
"MAINT MODE ON LIGHT",
|
|
"SYSTEM ALARM LIGHT",
|
|
"VFD FAULT LIGHT",
|
|
"DC FAULT LIGHT",
|
|
|
|
"PANEL STACKLIGHT",
|
|
"BUILDING E-STOP LIGHT",
|
|
"REMOTE E-STOP LIGHT"
|
|
]
|
|
|
|
# PANEL extra names (26 rows)
|
|
PANEL_EXTRA_NAMES = [
|
|
|
|
# 7 rows group 1
|
|
"R02S00:1:I.0",
|
|
"R02S00:1:I.1",
|
|
"R02S00:1:I.2",
|
|
"R02S00:1:I.3",
|
|
"R02S00:1:I.4",
|
|
"R02S00:1:I.5",
|
|
"R02S00:1:I.6",
|
|
|
|
# 5 rows group 2
|
|
"R02S00:2:I.0",
|
|
"R02S00:2:I.1",
|
|
"R02S00:2:I.2",
|
|
"R02S00:2:I.3",
|
|
"R02S00:2:I.4",
|
|
|
|
# 3 rows group 3
|
|
"R02S00:3:I.Pt00Data",
|
|
"R02S00:3:I.Pt01Data",
|
|
"R02S00:3:I.Pt02Data",
|
|
"R02S00:3:I.Pt03Data",
|
|
|
|
# 8 rows group 4
|
|
"R02S00:4:O.0",
|
|
"R02S00:4:O.1",
|
|
"R02S00:4:O.2",
|
|
"R02S00:4:O.3",
|
|
"R02S00:4:O.4",
|
|
"R02S00:4:O.5",
|
|
"R02S00:4:O.6",
|
|
"R02S00:4:O.7",
|
|
|
|
# 3 rows group 5
|
|
"R02S00:5:O.Pt00Data",
|
|
"R02S00:5:O.Pt01Data",
|
|
"R02S00:5:O.Pt02Data",
|
|
]
|
|
|
|
# APF/VFD suffixes for last 5 rows
|
|
SI_SO_SUFFIXES = [
|
|
"SI.In00Data",
|
|
"SI.In01Data",
|
|
"SI.In02Data",
|
|
"SI.In03Data",
|
|
"SO.Out00Output"
|
|
]
|
|
|
|
# ======================================================
|
|
# KEYWORD RULES
|
|
# ======================================================
|
|
INPUT_KEYWORDS = [
|
|
"E.STOP", "EMERGENCY", "PULL CORD", "EPC", "SSP",
|
|
"PHOTOEYE", "PE", "TPE", "SDP", "SSDP",
|
|
"PB", "PUSHBUTTON", "BUTTON", "ACK", "DEBRIS",
|
|
"PROX", "PROXIMITY", "RECEIVE", "SEND"
|
|
]
|
|
|
|
OUTPUT_KEYWORDS = [
|
|
"BEACON", "BCN", "RED BEACON", "HORN", "BUZZER",
|
|
"STATION LIGHT", "LIGHT", "LT",
|
|
"DCM", "DCM_CABLE", "DIVERT CONTROL",
|
|
"OILER", "PUMP", "PUMP SIGNAL", "OILER PUMP"
|
|
]
|
|
|
|
def is_output(desc):
|
|
U = desc.upper()
|
|
return any(k in U for k in OUTPUT_KEYWORDS)
|
|
|
|
def is_input(desc):
|
|
U = desc.upper()
|
|
return any(k in U for k in INPUT_KEYWORDS)
|
|
|
|
# keep original device name, only replace "-" with "_"
|
|
def normalize_name(name):
|
|
return name.replace("-", "_")
|
|
|
|
def process_file(filepath):
|
|
file = os.path.basename(filepath)
|
|
subsystem = file.split("_")[0]
|
|
|
|
print(f"\nProcessing: {file}")
|
|
|
|
try:
|
|
df = pd.read_excel(filepath, sheet_name=SHEET_NAME, dtype=str)
|
|
except Exception as e:
|
|
print(" ❌ Error reading:", e)
|
|
return
|
|
|
|
df = df.fillna("")
|
|
df["OriginalIndex"] = df.index
|
|
|
|
TAG = df.iloc[:, 0]
|
|
DESCA = df.iloc[:, 4]
|
|
DESCB = df.iloc[:, 5]
|
|
|
|
df["Description"] = (DESCA.str.strip() + " " + DESCB.str.strip()).str.strip()
|
|
df["TAGNAME"] = TAG
|
|
|
|
output = pd.DataFrame(columns=["Name", "Description", "Subsystem"])
|
|
|
|
groups = df.groupby("TAGNAME", sort=False)
|
|
|
|
# ======================================================
|
|
# ADD 26 MCMP PANEL INPUT/OUTPUT ROWS AT TOP
|
|
# ======================================================
|
|
for name_value, desc in zip(PANEL_EXTRA_NAMES, PANEL_EXTRA_DESCRIPTIONS):
|
|
full_desc = f"{subsystem} {desc}"
|
|
output.loc[len(output)] = [name_value, full_desc, subsystem]
|
|
|
|
# ======================================================
|
|
# MAIN DEVICE PROCESSING
|
|
# ======================================================
|
|
for device_name, group in groups:
|
|
|
|
group = group.sort_values("OriginalIndex").reset_index(drop=True)
|
|
rows = len(group)
|
|
|
|
# normalize device name
|
|
device_norm = normalize_name(device_name)
|
|
|
|
# ======================================================
|
|
# RULE 1: APF/VFD (11 rows)
|
|
# ======================================================
|
|
if rows == 11:
|
|
|
|
# First 4 inputs
|
|
for i in range(4):
|
|
name_value = f"{device_norm}:I.In_{i}"
|
|
output.loc[len(output)] = [name_value, group.loc[i, "Description"], subsystem]
|
|
|
|
# IO_0 / IO_1
|
|
for i in range(4, 6):
|
|
desc_value = group.loc[i, "Description"]
|
|
desc_upper = desc_value.upper()
|
|
|
|
if "[IN]" in desc_upper:
|
|
io = "I"
|
|
elif "[OUT]" in desc_upper:
|
|
io = "O"
|
|
elif desc_upper.strip() in ("", "SPARE"):
|
|
io = "I"
|
|
elif is_output(desc_value):
|
|
io = "O"
|
|
elif is_input(desc_value):
|
|
io = "I"
|
|
else:
|
|
io = "I"
|
|
|
|
io_number = i - 4
|
|
name_value = f"{device_norm}:{io}.IO_{io_number}"
|
|
|
|
output.loc[len(output)] = [name_value, desc_value, subsystem]
|
|
|
|
# SI / SO suffix rows
|
|
for idx, suffix in enumerate(SI_SO_SUFFIXES):
|
|
name_value = f"{device_norm}:{suffix}"
|
|
output.loc[len(output)] = [
|
|
name_value,
|
|
group.loc[6 + idx, "Description"],
|
|
subsystem
|
|
]
|
|
|
|
# ======================================================
|
|
# RULE 2: SIO (16 rows)
|
|
# ======================================================
|
|
elif rows == 16 and "SIO" in device_name.upper():
|
|
|
|
for i in range(8):
|
|
name_value = f"{device_norm}:I.Pt{str(i).zfill(2)}Data"
|
|
output.loc[len(output)] = [name_value, group.loc[i, "Description"], subsystem]
|
|
|
|
for i in range(8):
|
|
name_value = f"{device_norm}:O.Pt{str(i).zfill(2)}Data"
|
|
output.loc[len(output)] = [
|
|
name_value,
|
|
group.loc[8 + i, "Description"],
|
|
subsystem
|
|
]
|
|
|
|
# ======================================================
|
|
# RULE 3: FIO (16 rows)
|
|
# ======================================================
|
|
elif rows == 16 and "FIO" in device_name.upper():
|
|
|
|
for i in range(16):
|
|
desc = group.loc[i, "Description"]
|
|
|
|
if is_output(desc):
|
|
io = "O"
|
|
else:
|
|
io = "I"
|
|
|
|
name_value = f"{device_norm}:{io}.Pt{str(i).zfill(2)}.Data"
|
|
output.loc[len(output)] = [name_value, desc, subsystem]
|
|
|
|
# ======================================================
|
|
# RULE 4: Unknown → keyword-based fallback
|
|
# ======================================================
|
|
else:
|
|
for i in range(rows):
|
|
desc = group.loc[i, "Description"]
|
|
|
|
if is_output(desc):
|
|
io = "O"
|
|
elif is_input(desc):
|
|
io = "I"
|
|
else:
|
|
io = None
|
|
|
|
if io is None:
|
|
output.loc[len(output)] = ["", desc, subsystem]
|
|
else:
|
|
name_value = f"{device_norm}:{io}.{i}"
|
|
output.loc[len(output)] = [name_value, desc, subsystem]
|
|
|
|
# ======================================================
|
|
# SAVE OUTPUT FILE
|
|
# ======================================================
|
|
current_folder = os.path.dirname(os.path.abspath(__file__))
|
|
parent_folder = os.path.dirname(current_folder)
|
|
io_tool_folder = os.path.join(parent_folder, "IO_TOOL")
|
|
|
|
os.makedirs(io_tool_folder, exist_ok=True)
|
|
|
|
out_name = subsystem + ".csv"
|
|
out_path = os.path.join(io_tool_folder, out_name)
|
|
output.to_csv(out_path, index=False)
|
|
|
|
print(f" ✅ Created: {out_path}")
|
|
|
|
|
|
def main():
|
|
base = os.path.dirname(os.path.abspath(__file__))
|
|
print("Scanning directory:", base)
|
|
|
|
for file in os.listdir(base):
|
|
if file.startswith("~$"):
|
|
continue
|
|
if file.lower().endswith((".xls", ".xlsx", ".xlsm")):
|
|
process_file(os.path.join(base, file))
|
|
|
|
print("\n✔ DONE")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|