diff --git a/AutoCAD/IO/IO.xlsx b/AutoCAD/IO/IO.xlsx deleted file mode 100644 index 6c87dd5..0000000 Binary files a/AutoCAD/IO/IO.xlsx and /dev/null differ diff --git a/AutoCAD/IO/MCM_IO.xlsx b/AutoCAD/IO/MCM_IO.xlsx index a7eefa4..633e5ba 100644 Binary files a/AutoCAD/IO/MCM_IO.xlsx and b/AutoCAD/IO/MCM_IO.xlsx differ diff --git a/AutoCAD/IO/__pycache__/build_io_table.cpython-314.pyc b/AutoCAD/IO/__pycache__/build_io_table.cpython-314.pyc new file mode 100644 index 0000000..785df07 Binary files /dev/null and b/AutoCAD/IO/__pycache__/build_io_table.cpython-314.pyc differ diff --git a/AutoCAD/IO/build_io_table.py b/AutoCAD/IO/build_io_table.py index fd5c56d..f4a1abd 100644 --- a/AutoCAD/IO/build_io_table.py +++ b/AutoCAD/IO/build_io_table.py @@ -61,13 +61,28 @@ def family_keys(dev: str): seen.add(k); out.append(k) return out -def fallback_desc(dev: str) -> str: +def bcn_desc_s_stack(dev: str, all_devices: set) -> str | None: + """Returns IO LINK description for S-prefixed BCNs, or None if not applicable.""" + import re as _re + d = dev.upper() + m = _re.match(r"(S\d+)_BCN\d*", d) + if not m: + return None + base = m.group(1) + jr_pattern = _re.compile(rf"{_re.escape(base)}_JR\d") + has_jr = any(jr_pattern.search(dv) for dv in all_devices) + if has_jr: + return "IO LINK 3 STACK G/A/B BEACON" + return "IO LINK 2 STACK G/B BEACON" + +def fallback_desc(dev: str, all_devices: set | None = None) -> str: import re as _re d = dev.upper() if d == "SPARE": return "" if "VFD" in d and ("DISC" in d or "DSIC" in d): return "DISCONNECT AUX" if _re.search(r"_TPE\d+|^TPE\d+", d): return "TRACKING PHOTOEYE" if _re.search(r"_JPE\d+|^JPE\d+", d): return "JAM PHOTOEYE" + if _re.search(r"S\d+_PE\d*|_PE\d+|^PE\d+", d): return "CHUTE DISABLE PHOTOEYE" if _re.search(r"_EPC\d+|^EPC\d+", d): return "E-STOP PULLCORD" if "ENSH" in d: return "SHAFT ENCODER" if "ENW" in d: return "WHEEL ENCODER" @@ -78,6 +93,9 @@ def fallback_desc(dev: str) -> str: if any(x in d for x in ["_SOL","_SOV","_SV","_SOLV"]): return "SOLENOID VALVE" if _re.search(r"_FIOH\d+|^FIOH\d+", d): return "I/O LINK HUB" if "BCN" in d: + s_bcn_desc = bcn_desc_s_stack(d, all_devices or set()) + if s_bcn_desc: + return s_bcn_desc if d.endswith("_A"): return "AMBER BEACON LIGHT" if d.endswith("_R"): return "RED BEACON LIGHT" if d.endswith("_G"): return "GREEN BEACON LIGHT" @@ -97,13 +115,13 @@ def fallback_desc(dev: str) -> str: if d.startswith("PDP") and "_PWM" in d: return "PHASE MONITOR" return "DEVICE" -def desc_for(dev: str, canon: dict) -> str: +def desc_for(dev: str, canon: dict, all_devices: set | None = None) -> str: s = str(dev).strip().upper() if not s: return "" if s in canon: return canon[s] for k in family_keys(s): if k in canon: return canon[k] - return fallback_desc(s) + return fallback_desc(s, all_devices) def choose_order(controller_name: str, available_columns): s = str(controller_name).upper() @@ -145,6 +163,17 @@ def main(): canon_map = load_canon([Path(p) for p in args.canon]) + all_devices = set() + for _, r in df.iterrows(): + ctrl = str(r.get("P_TAG1", "")).strip() + if not ctrl: + continue + order = choose_order(ctrl, sig_cols_available) + for sig in order: + val = r.get(sig) + if pd.notna(val) and str(val).strip(): + all_devices.add(str(val).strip().upper()) + rows = [] for _, r in df.iterrows(): ctrl = str(r.get("P_TAG1","")).strip() @@ -157,7 +186,7 @@ def main(): if dev == "": dev = "SPARE" addr = f"{ctrl}_{sig}" - desc = "" if dev.upper()=="SPARE" else desc_for(dev, canon_map) + desc = "" if dev.upper()=="SPARE" else desc_for(dev, canon_map, all_devices) dU = dev.upper() if re.search(r'_ESTOP1$', dU): desc = "ESTOP OK" elif re.search(r'_SS\d+_SPB_LT$', dU): desc = "SS STATION START PUSHBUTTON LIGHT" @@ -169,6 +198,7 @@ def main(): elif re.search(r'_JR\d+_PB$', dU): desc = "JAM RESET PUSHBUTTON" elif re.search(r'_EN\d*_PB_LT$', dU): desc = "ENABLE PUSHBUTTON LIGHT" elif re.search(r'_EN\d*_PB$', dU): desc = "ENABLE PUSHBUTTON" + elif re.search(r'_PR\d*_PB$', dU): desc = "PACKAGE RELEASE PUSHBUTTON" rows.append({ "Controller name": ctrl, "Signal type": sig,