Created Project
This commit is contained in:
commit
40c46d6433
BIN
AutoCAD/Compare/IO_Connection.xlsx
Normal file
BIN
AutoCAD/Compare/IO_Connection.xlsx
Normal file
Binary file not shown.
BIN
AutoCAD/Compare/Results.xlsx
Normal file
BIN
AutoCAD/Compare/Results.xlsx
Normal file
Binary file not shown.
314
AutoCAD/Compare/check_io_vs_devices.py
Normal file
314
AutoCAD/Compare/check_io_vs_devices.py
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
# ==== CONFIG YOU CAN TWEAK IF NEEDED ====
|
||||||
|
# IO rows matching these regex patterns will be ignored
|
||||||
|
SKIP_PATTERNS = [
|
||||||
|
r"^PDP.*_CB", # PDP*_CB* pattern
|
||||||
|
r"^PDP.*_FWM", # PDP*_FWM* pattern
|
||||||
|
r"^PDP.*_PWM", # PDP*_PWM* pattern
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(tag: str) -> str:
|
||||||
|
"""
|
||||||
|
Apply all agreed normalization rules to a device/assigned tag.
|
||||||
|
"""
|
||||||
|
if not isinstance(tag, str):
|
||||||
|
return None
|
||||||
|
t = tag.strip()
|
||||||
|
if not t:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 1) EPC: keep up to ..._EPC<number>
|
||||||
|
# Examples:
|
||||||
|
# NCP1_3_EPC1_1 -> NCP1_3_EPC1
|
||||||
|
# NCP1_3_EPC2_99 -> NCP1_3_EPC2
|
||||||
|
m = re.search(r"(.*_EPC\d+)_.*", t)
|
||||||
|
if m:
|
||||||
|
t = m.group(1)
|
||||||
|
|
||||||
|
# 2) VFD variants -> base _VFD
|
||||||
|
# UL23_25_VFD_DISC -> UL23_25_VFD
|
||||||
|
# S02_1_VFD_STO1 -> S02_1_VFD
|
||||||
|
t = re.sub(r"_VFD_DISC$", "_VFD", t)
|
||||||
|
t = re.sub(r"_VFD_DSIC$", "_VFD", t)
|
||||||
|
t = re.sub(r"_VFD_STO\d*$", "_VFD", t)
|
||||||
|
|
||||||
|
# 3) SS buttons: SPB_LT / SPB / STPB -> base SS
|
||||||
|
# NCS2_12_SS2_SPB_LT -> NCS2_12_SS2
|
||||||
|
# NCR1_2_SS1_SPB -> NCR1_2_SS1
|
||||||
|
# NCR1_2_SS1_STPB -> NCR1_2_SS1
|
||||||
|
t = re.sub(r"_SPB_LT$", "", t)
|
||||||
|
t = re.sub(r"_STPB$", "", t)
|
||||||
|
t = re.sub(r"_SPB$", "", t)
|
||||||
|
|
||||||
|
# 4) Generic PB: PB_LT / PB -> base
|
||||||
|
# NCP1_3_JR1_PB -> NCP1_3_JR1
|
||||||
|
# NCP1_3_JR1_PB_LT -> NCP1_3_JR1
|
||||||
|
t = re.sub(r"_PB_LT$", "", t)
|
||||||
|
t = re.sub(r"_PB$", "", t)
|
||||||
|
|
||||||
|
# 5) STO suffix on SR tags -> base SR
|
||||||
|
# _SR1_STO1 -> _SR1
|
||||||
|
t = re.sub(r"(_SR\d+)_STO\d+$", r"\1", t)
|
||||||
|
|
||||||
|
# 6) R/S endings -> base
|
||||||
|
# S02_2_LRPE6_R -> S02_2_LRPE6
|
||||||
|
# S02_2_LRPE6_S -> S02_2_LRPE6
|
||||||
|
t = re.sub(r"_(R|S)$", "", t)
|
||||||
|
|
||||||
|
return t or None
|
||||||
|
|
||||||
|
|
||||||
|
def load_io_series(io_path: Path, io_column: str | None) -> pd.Series:
|
||||||
|
"""
|
||||||
|
Load IO Excel and return a cleaned Series of Assigned Device strings.
|
||||||
|
If io_column is None, uses the first column.
|
||||||
|
"""
|
||||||
|
df = pd.read_excel(io_path)
|
||||||
|
|
||||||
|
if io_column:
|
||||||
|
if io_column not in df.columns:
|
||||||
|
raise SystemExit(
|
||||||
|
f"ERROR: IO column '{io_column}' not found. Available: {list(df.columns)}"
|
||||||
|
)
|
||||||
|
series = df[io_column]
|
||||||
|
else:
|
||||||
|
# Use first column
|
||||||
|
first_col = df.columns[0]
|
||||||
|
series = df[first_col]
|
||||||
|
|
||||||
|
series = series.astype(str).str.strip()
|
||||||
|
|
||||||
|
# Drop SPARE rows
|
||||||
|
mask_spare = series.str.contains("SPARE", case=False, na=False)
|
||||||
|
|
||||||
|
# Drop rows matching SKIP_PATTERNS (e.g. PDP*_CB*, PDP*_FWM*)
|
||||||
|
mask_skip_pattern = False
|
||||||
|
for pattern in SKIP_PATTERNS:
|
||||||
|
mask_skip_pattern = mask_skip_pattern | series.str.match(pattern, na=False)
|
||||||
|
|
||||||
|
filtered = series[~(mask_spare | mask_skip_pattern)]
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
|
||||||
|
|
||||||
|
def load_device_tags(dev_path: Path, dev_column: str | None) -> pd.Series:
|
||||||
|
"""
|
||||||
|
Load device Excel and return Series of device tags.
|
||||||
|
Default column is 'P_TAG1' if exists, else first column.
|
||||||
|
"""
|
||||||
|
df = pd.read_excel(dev_path)
|
||||||
|
|
||||||
|
if dev_column:
|
||||||
|
if dev_column not in df.columns:
|
||||||
|
raise SystemExit(
|
||||||
|
f"ERROR: Device column '{dev_column}' not found. Available: {list(df.columns)}"
|
||||||
|
)
|
||||||
|
series = df[dev_column]
|
||||||
|
else:
|
||||||
|
if "P_TAG1" in df.columns:
|
||||||
|
series = df["P_TAG1"]
|
||||||
|
else:
|
||||||
|
first_col = df.columns[0]
|
||||||
|
series = df[first_col]
|
||||||
|
|
||||||
|
return series.astype(str).str.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def build_io_map(assigned_series: pd.Series) -> dict[str, set[str]]:
|
||||||
|
"""
|
||||||
|
Build mapping: canonical_tag -> set of raw IO assigned device strings.
|
||||||
|
"""
|
||||||
|
io_canon = assigned_series.map(canonical)
|
||||||
|
io_map: dict[str, set[str]] = {}
|
||||||
|
|
||||||
|
for raw, c in zip(assigned_series, io_canon):
|
||||||
|
if not c:
|
||||||
|
continue
|
||||||
|
io_map.setdefault(c, set()).add(raw)
|
||||||
|
|
||||||
|
return io_map
|
||||||
|
|
||||||
|
|
||||||
|
def detect_duplicate_assignments(assigned_series: pd.Series) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Return dataframe of raw IO assigned device strings that appear more than once.
|
||||||
|
This keeps PB/PB_LT, EPC variants, etc. separate and only flags exact duplicates.
|
||||||
|
"""
|
||||||
|
cleaned = assigned_series.dropna().str.strip()
|
||||||
|
cleaned = cleaned[cleaned != ""]
|
||||||
|
|
||||||
|
counts = cleaned.value_counts()
|
||||||
|
duplicates = counts[counts > 1]
|
||||||
|
if duplicates.empty:
|
||||||
|
return pd.DataFrame(columns=["Assigned_Device", "Occurrences"])
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for tag_value, occ in duplicates.items():
|
||||||
|
rows.append({"Assigned_Device": tag_value, "Occurrences": occ})
|
||||||
|
|
||||||
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def find_io_without_devices(io_map: dict[str, set[str]], dev_canon: pd.Series) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Return dataframe of IO canonical tags that do not exist in the device list.
|
||||||
|
"""
|
||||||
|
dev_canon_set = set(dev_canon.dropna())
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for canon_tag, raw_values in sorted(io_map.items()):
|
||||||
|
if canon_tag not in dev_canon_set:
|
||||||
|
rows.append(
|
||||||
|
{
|
||||||
|
"Canonical_Tag": canon_tag,
|
||||||
|
"IO_Assigned_Devices": ", ".join(sorted(raw_values)),
|
||||||
|
"Occurrences": len(raw_values),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def compare(io_path: Path,
|
||||||
|
dev_path: Path,
|
||||||
|
out_path: Path,
|
||||||
|
io_column: str | None = None,
|
||||||
|
dev_column: str | None = None) -> None:
|
||||||
|
"""
|
||||||
|
Main compare routine: IO vs Devices, export Excel, print summary.
|
||||||
|
"""
|
||||||
|
print(f"IO file: {io_path}")
|
||||||
|
print(f"Devices file: {dev_path}")
|
||||||
|
print(f"Output file: {out_path}")
|
||||||
|
print("Loading data...")
|
||||||
|
|
||||||
|
assigned_series = load_io_series(io_path, io_column)
|
||||||
|
dev_tags = load_device_tags(dev_path, dev_column)
|
||||||
|
|
||||||
|
print(f"IO rows after filters: {len(assigned_series)}")
|
||||||
|
print(f"Device tags: {len(dev_tags)}")
|
||||||
|
|
||||||
|
io_map = build_io_map(assigned_series)
|
||||||
|
duplicate_df = detect_duplicate_assignments(assigned_series)
|
||||||
|
dev_canon = dev_tags.map(canonical)
|
||||||
|
io_only_df = find_io_without_devices(io_map, dev_canon)
|
||||||
|
|
||||||
|
present_flags: list[str] = []
|
||||||
|
matching_assigned: list[str] = []
|
||||||
|
|
||||||
|
for d_tag, d_c in zip(dev_tags, dev_canon):
|
||||||
|
if d_c in io_map:
|
||||||
|
present_flags.append("YES")
|
||||||
|
matching_assigned.append(", ".join(sorted(io_map[d_c])))
|
||||||
|
else:
|
||||||
|
present_flags.append("NO")
|
||||||
|
matching_assigned.append("")
|
||||||
|
|
||||||
|
res_df = pd.DataFrame({
|
||||||
|
"Device_Tag": dev_tags,
|
||||||
|
"Canonical_Tag_Used_For_Check": dev_canon,
|
||||||
|
"Present_In_IO": present_flags,
|
||||||
|
"Matching_IO_Assigned_Devices": matching_assigned,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Save result (main sheet + duplicates if any)
|
||||||
|
out_path = out_path.with_suffix(".xlsx")
|
||||||
|
with pd.ExcelWriter(out_path, engine="openpyxl") as writer:
|
||||||
|
res_df.to_excel(writer, index=False, sheet_name="Devices_vs_IO")
|
||||||
|
if not duplicate_df.empty:
|
||||||
|
duplicate_df.to_excel(
|
||||||
|
writer, index=False, sheet_name="Duplicate_IO_Assignments"
|
||||||
|
)
|
||||||
|
if not io_only_df.empty:
|
||||||
|
io_only_df.to_excel(
|
||||||
|
writer, index=False, sheet_name="IO_Only_Assignments"
|
||||||
|
)
|
||||||
|
print(f"\nResult saved to: {out_path}")
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
missing_df = res_df[res_df["Present_In_IO"] == "NO"]
|
||||||
|
total_devices = len(res_df)
|
||||||
|
missing_count = len(missing_df)
|
||||||
|
found_count = total_devices - missing_count
|
||||||
|
|
||||||
|
print("\n===== SUMMARY =====")
|
||||||
|
print(f"Total devices: {total_devices}")
|
||||||
|
print(f"Found in IO: {found_count}")
|
||||||
|
print(f"Missing: {missing_count}")
|
||||||
|
print(f"IO-only tags: {len(io_only_df)}")
|
||||||
|
|
||||||
|
if missing_count > 0:
|
||||||
|
print("\nDevices NOT found in IO (after all rules):")
|
||||||
|
for tag in sorted(missing_df["Device_Tag"].tolist()):
|
||||||
|
print(" -", tag)
|
||||||
|
|
||||||
|
if duplicate_df.empty:
|
||||||
|
print("\nDuplicate IO assignments: none detected.")
|
||||||
|
else:
|
||||||
|
print("\nDuplicate IO assignments detected (same raw value repeated):")
|
||||||
|
for _, row in duplicate_df.sort_values("Assigned_Device").iterrows():
|
||||||
|
print(
|
||||||
|
f" - {row['Assigned_Device']} ({row['Occurrences']} occurrences)"
|
||||||
|
)
|
||||||
|
|
||||||
|
if io_only_df.empty:
|
||||||
|
print("\nIO-only assignments: none detected.")
|
||||||
|
else:
|
||||||
|
print("\nIO-only assignments (no matching device tag):")
|
||||||
|
for _, row in io_only_df.iterrows():
|
||||||
|
print(
|
||||||
|
f" - {row['Canonical_Tag']} ({row['Occurrences']} occurrences): "
|
||||||
|
f"{row['IO_Assigned_Devices']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Check that all devices from device list are present in IO file, "
|
||||||
|
"using EPC/VFD/PB/SS/R-S rules."
|
||||||
|
)
|
||||||
|
parser.add_argument("io_file", help="Input/Output Excel file (IO)")
|
||||||
|
parser.add_argument("devices_file", help="Devices Excel file")
|
||||||
|
parser.add_argument("output_file", help="Output Excel file path (will be .xlsx)")
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--io-column",
|
||||||
|
help="Column name in IO file to use as Assigned Device (default: first column)",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dev-column",
|
||||||
|
help="Column name in devices file (default: P_TAG1 if exists, else first column)",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
io_path = Path(args.io_file)
|
||||||
|
dev_path = Path(args.devices_file)
|
||||||
|
out_path = Path(args.output_file)
|
||||||
|
|
||||||
|
if not io_path.is_file():
|
||||||
|
raise SystemExit(f"ERROR: IO file not found: {io_path}")
|
||||||
|
if not dev_path.is_file():
|
||||||
|
raise SystemExit(f"ERROR: Devices file not found: {dev_path}")
|
||||||
|
|
||||||
|
compare(
|
||||||
|
io_path=io_path,
|
||||||
|
dev_path=dev_path,
|
||||||
|
out_path=out_path,
|
||||||
|
io_column=args.io_column,
|
||||||
|
dev_column=args.dev_column,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
BIN
AutoCAD/Compare/device_list.xlsx
Normal file
BIN
AutoCAD/Compare/device_list.xlsx
Normal file
Binary file not shown.
BIN
AutoCAD/DPM/MCM01_NETWORK.xlsx
Normal file
BIN
AutoCAD/DPM/MCM01_NETWORK.xlsx
Normal file
Binary file not shown.
BIN
AutoCAD/DPM/OUTPUT.xlsx
Normal file
BIN
AutoCAD/DPM/OUTPUT.xlsx
Normal file
Binary file not shown.
310
AutoCAD/DPM/build_network_tables.py
Normal file
310
AutoCAD/DPM/build_network_tables.py
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
Generate DPM network tables from a Summary sheet.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python build_network_tables.py INPUT.xlsx OUTPUT.xlsx
|
||||||
|
python build_network_tables.py INPUT.xlsx OUTPUT.xlsx --net2 DPM1,DPM2,...
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
python build_network_tables.py MCM08_Network.xlsx MCM08_Network_NET1_NET2.xlsx
|
||||||
|
python build_network_tables.py Amazon_CDW5_Bypass.xlsx Bypass_NET1_NET2.xlsx --net2 BYAD_6_DPM1,BYCD_14_DPM1,BYDB_6_DPM1
|
||||||
|
|
||||||
|
Requirements: pip install pandas openpyxl
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
from itertools import groupby
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.styles import PatternFill, Alignment, Border, Side, Font
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- CONSTANTS YOU PROBABLY KEEP THE SAME ----------
|
||||||
|
|
||||||
|
SUMMARY_SHEET = "Summary" # name of sheet with DPM / P_TAG1 / HP
|
||||||
|
|
||||||
|
RING_BASE = "11.200.1." # ring IPs: 11.200.1.2, 11.200.1.3, ...
|
||||||
|
STAR_NET1_BASE = "11.200.1." # NET1 STAR base
|
||||||
|
STAR_NET2_BASE = "11.200.2." # NET2 STAR base
|
||||||
|
STAR_START = 20 # first STAR IP is *.20
|
||||||
|
|
||||||
|
# VFD HP -> part number
|
||||||
|
HP_TO_PN = {
|
||||||
|
"2HP": "35S-6D2-P101",
|
||||||
|
"3HP": "35S-6D3-P101",
|
||||||
|
"5HP": "35S-6D4-P111",
|
||||||
|
"7.5HP": "35S-6D5-P111",
|
||||||
|
"10HP": "35S-6D6-P111",
|
||||||
|
"30HP": "25B-D043N114"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- HELPERS ----------
|
||||||
|
|
||||||
|
def pick_column(colnames, candidates):
|
||||||
|
"""Pick first matching column name by case-insensitive list of options."""
|
||||||
|
cand_upper = [c.upper() for c in candidates]
|
||||||
|
for c in colnames:
|
||||||
|
if str(c).strip().upper() in cand_upper:
|
||||||
|
return c
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def dpm_sort_key(dpm):
|
||||||
|
"""Natural sort: prefix (letters/underscore) + list of all numbers."""
|
||||||
|
s = str(dpm).strip()
|
||||||
|
m = re.match(r"^([A-Za-z_]+)", s)
|
||||||
|
prefix = m.group(1) if m else ""
|
||||||
|
nums = [int(x) for x in re.findall(r"\d+", s)]
|
||||||
|
return prefix, nums
|
||||||
|
|
||||||
|
|
||||||
|
def device_sort_key(tag):
|
||||||
|
"""
|
||||||
|
Sort devices inside a DPM:
|
||||||
|
1) FIO Masters/hubs (FIOM/FIOH/FIO/MASTER) first
|
||||||
|
2) Then by numbers inside name
|
||||||
|
3) VFD before others among non-masters
|
||||||
|
"""
|
||||||
|
s = str(tag).strip()
|
||||||
|
u = s.upper()
|
||||||
|
|
||||||
|
is_master = ("FIOM" in u) or ("FIOH" in u) or ("FIO" in u) or ("MASTER" in u)
|
||||||
|
master_prio = 0 if is_master else 1
|
||||||
|
|
||||||
|
nums = [int(x) for x in re.findall(r"\d+", s)]
|
||||||
|
|
||||||
|
typ_match = re.search(r"_([A-Za-z]+)\d*$", s)
|
||||||
|
typ = typ_match.group(1).upper() if typ_match else ""
|
||||||
|
|
||||||
|
if typ == "VFD":
|
||||||
|
t_order = 0
|
||||||
|
elif typ in ("FIOM", "FIO", "FIOH"):
|
||||||
|
t_order = 1
|
||||||
|
else:
|
||||||
|
t_order = 2
|
||||||
|
|
||||||
|
return (master_prio, nums, t_order, typ, s)
|
||||||
|
|
||||||
|
|
||||||
|
def part_number_for(device_name, rating_map):
|
||||||
|
"""Return part number string for a device."""
|
||||||
|
if not isinstance(device_name, str):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
u = device_name.upper()
|
||||||
|
if "SPARE" in u:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# FIO masters / hubs
|
||||||
|
if ("FIOM" in u) or ("FIO" in u) or ("FIOH" in u) or ("MASTER" in u):
|
||||||
|
return "Murr 54631"
|
||||||
|
|
||||||
|
hp = rating_map.get(device_name, "")
|
||||||
|
return HP_TO_PN.get(hp, "")
|
||||||
|
|
||||||
|
|
||||||
|
def build_assignments(df, col_dpm, col_tag, col_rating):
|
||||||
|
"""Build sorted DPM list, device assignments and rating map."""
|
||||||
|
df = df[~df[col_dpm].isna()].copy()
|
||||||
|
|
||||||
|
unique_dpms = sorted(
|
||||||
|
{str(x).strip() for x in df[col_dpm].tolist()},
|
||||||
|
key=dpm_sort_key,
|
||||||
|
)
|
||||||
|
|
||||||
|
assign = {dpm: [] for dpm in unique_dpms}
|
||||||
|
rating_map = {}
|
||||||
|
|
||||||
|
for _, row in df.iterrows():
|
||||||
|
dpm = str(row[col_dpm]).strip()
|
||||||
|
tag = (
|
||||||
|
str(row[col_tag]).strip()
|
||||||
|
if col_tag and not pd.isna(row[col_tag])
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
rating = (
|
||||||
|
str(row[col_rating]).strip()
|
||||||
|
if col_rating and not pd.isna(row[col_rating])
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
if tag:
|
||||||
|
assign.setdefault(dpm, []).append(tag)
|
||||||
|
rating_map[tag] = rating
|
||||||
|
|
||||||
|
for dpm in assign:
|
||||||
|
assign[dpm] = sorted(assign[dpm], key=device_sort_key)
|
||||||
|
|
||||||
|
return unique_dpms, assign, rating_map
|
||||||
|
|
||||||
|
|
||||||
|
def generate_rows(unique_dpms, assign, rating_map, net2_dpms):
|
||||||
|
"""
|
||||||
|
Generate rows for NET1 and NET2.
|
||||||
|
Each row: (dpm, ring_ip, assigned_dev, part_num, star_ip, dpm_port)
|
||||||
|
"""
|
||||||
|
# ring IP per DPM
|
||||||
|
ring_ip_map = {}
|
||||||
|
ring_counter = 2
|
||||||
|
for dpm in unique_dpms:
|
||||||
|
ring_ip_map[dpm] = f"{RING_BASE}{ring_counter}"
|
||||||
|
ring_counter += 1
|
||||||
|
|
||||||
|
rows_net1 = []
|
||||||
|
rows_net2 = []
|
||||||
|
|
||||||
|
star1_counter = STAR_START
|
||||||
|
star2_counter = STAR_START
|
||||||
|
|
||||||
|
for dpm in unique_dpms:
|
||||||
|
ring_ip = ring_ip_map[dpm]
|
||||||
|
tags = assign.get(dpm, [])
|
||||||
|
tags = tags[:24] + ["SPARE"] * (24 - len(tags[:24]))
|
||||||
|
|
||||||
|
for idx, port in enumerate(range(5, 29)):
|
||||||
|
dev = tags[idx]
|
||||||
|
pn = part_number_for(dev, rating_map)
|
||||||
|
|
||||||
|
if dpm in net2_dpms:
|
||||||
|
star_ip = f"{STAR_NET2_BASE}{star2_counter}"
|
||||||
|
star2_counter += 1
|
||||||
|
rows_net2.append(
|
||||||
|
(dpm, ring_ip, dev, pn, star_ip, f"{dpm}_P{port}")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
star_ip = f"{STAR_NET1_BASE}{star1_counter}"
|
||||||
|
star1_counter += 1
|
||||||
|
rows_net1.append(
|
||||||
|
(dpm, ring_ip, dev, pn, star_ip, f"{dpm}_P{port}")
|
||||||
|
)
|
||||||
|
|
||||||
|
return rows_net1, rows_net2
|
||||||
|
|
||||||
|
|
||||||
|
def write_sheet(ws, rows, title):
|
||||||
|
"""Write one NET sheet with merged yellow bands, D..I layout."""
|
||||||
|
ws.title = title
|
||||||
|
|
||||||
|
headers = ["DPM", "IP", "Assigned Device", "Part Number", "IP", "DPM PORT"]
|
||||||
|
for col, h in enumerate(headers, start=4):
|
||||||
|
c = ws.cell(row=3, column=col, value=h)
|
||||||
|
c.font = Font(bold=True)
|
||||||
|
c.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
c.border = Border(
|
||||||
|
left=Side(style="thin"),
|
||||||
|
right=Side(style="thin"),
|
||||||
|
top=Side(style="thin"),
|
||||||
|
bottom=Side(style="thin"),
|
||||||
|
)
|
||||||
|
|
||||||
|
yellow = PatternFill("solid", fgColor="FFC000")
|
||||||
|
thin = Side(style="thin", color="000000")
|
||||||
|
border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||||||
|
center = Alignment(horizontal="center", vertical="center")
|
||||||
|
left = Alignment(horizontal="left", vertical="center")
|
||||||
|
|
||||||
|
widths = {4: 18, 5: 16, 6: 30, 7: 18, 8: 16, 9: 22}
|
||||||
|
for col, w in widths.items():
|
||||||
|
ws.column_dimensions[chr(64 + col)].width = w
|
||||||
|
|
||||||
|
row_idx = 4
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
ws.freeze_panes = "D4"
|
||||||
|
return
|
||||||
|
|
||||||
|
for dpm, group in groupby(rows, key=lambda x: x[0]):
|
||||||
|
g = list(group)
|
||||||
|
# should already be 24 rows per DPM
|
||||||
|
g = g[:24]
|
||||||
|
|
||||||
|
start = row_idx
|
||||||
|
end = row_idx + 24 - 1
|
||||||
|
|
||||||
|
ws.merge_cells(start_row=start, start_column=4, end_row=end, end_column=4)
|
||||||
|
ws.merge_cells(start_row=start, start_column=5, end_row=end, end_column=5)
|
||||||
|
|
||||||
|
cD = ws.cell(row=start, column=4, value=g[0][0])
|
||||||
|
cE = ws.cell(row=start, column=5, value=g[0][1])
|
||||||
|
for cell in (cD, cE):
|
||||||
|
cell.fill = yellow
|
||||||
|
cell.alignment = center
|
||||||
|
cell.border = border
|
||||||
|
cell.font = Font(bold=False)
|
||||||
|
|
||||||
|
for i in range(24):
|
||||||
|
row_vals = {
|
||||||
|
6: g[i][2], # Assigned Device
|
||||||
|
7: g[i][3], # Part Number
|
||||||
|
8: g[i][4], # IP
|
||||||
|
9: g[i][5], # DPM PORT
|
||||||
|
}
|
||||||
|
for col, val in row_vals.items():
|
||||||
|
c = ws.cell(row=start + i, column=col, value=val)
|
||||||
|
c.alignment = left if col in (6, 7, 9) else center
|
||||||
|
c.border = border
|
||||||
|
|
||||||
|
for r in range(start + 1, end + 1):
|
||||||
|
for col in (4, 5):
|
||||||
|
c = ws.cell(row=r, column=col)
|
||||||
|
c.fill = yellow
|
||||||
|
c.border = border
|
||||||
|
|
||||||
|
row_idx = end + 1
|
||||||
|
|
||||||
|
ws.freeze_panes = "D4"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Build DPM network tables.")
|
||||||
|
parser.add_argument("input_file", help="Input Excel file (with Summary sheet)")
|
||||||
|
parser.add_argument("output_file", help="Output Excel file")
|
||||||
|
parser.add_argument(
|
||||||
|
"--net2",
|
||||||
|
help="Comma-separated list of DPM names that should go to NET2 (.2 subnet)",
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
net2_dpms = {
|
||||||
|
x.strip()
|
||||||
|
for x in args.net2.split(",")
|
||||||
|
if x.strip()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load Summary
|
||||||
|
df = pd.read_excel(args.input_file, sheet_name=SUMMARY_SHEET)
|
||||||
|
df.columns = [str(c).strip() for c in df.columns]
|
||||||
|
|
||||||
|
col_dpm = pick_column(df.columns, ["DPM"])
|
||||||
|
col_tag = pick_column(df.columns, ["P_TAG1", "P TAG1", "ASSIGNED DEVICE"])
|
||||||
|
col_rating = pick_column(df.columns, ["RATING", "HP", "POWER", "RATED"])
|
||||||
|
|
||||||
|
if not col_dpm or not col_tag:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Could not find DPM or P_TAG1/Assigned Device columns in sheet '{SUMMARY_SHEET}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
unique_dpms, assign, rating_map = build_assignments(
|
||||||
|
df, col_dpm, col_tag, col_rating
|
||||||
|
)
|
||||||
|
|
||||||
|
rows_net1, rows_net2 = generate_rows(unique_dpms, assign, rating_map, net2_dpms)
|
||||||
|
|
||||||
|
# Build workbook
|
||||||
|
wb = Workbook()
|
||||||
|
ws1 = wb.active
|
||||||
|
write_sheet(ws1, rows_net1, "NET1 STRUCTURE")
|
||||||
|
|
||||||
|
ws2 = wb.create_sheet()
|
||||||
|
write_sheet(ws2, rows_net2, "NET2 STRUCTURE")
|
||||||
|
|
||||||
|
wb.save(args.output_file)
|
||||||
|
print(f"Saved: {args.output_file}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
BIN
AutoCAD/DPM/~$OUTPUT.xlsx
Normal file
BIN
AutoCAD/DPM/~$OUTPUT.xlsx
Normal file
Binary file not shown.
BIN
AutoCAD/IO/IOrrrr.xlsx
Normal file
BIN
AutoCAD/IO/IOrrrr.xlsx
Normal file
Binary file not shown.
BIN
AutoCAD/IO/MCM_IO.xlsx
Normal file
BIN
AutoCAD/IO/MCM_IO.xlsx
Normal file
Binary file not shown.
186
AutoCAD/IO/build_io_table.py
Normal file
186
AutoCAD/IO/build_io_table.py
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# (script content truncated in preview; full content below)
|
||||||
|
|
||||||
|
import argparse, re
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Dict
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
ORDER_VFD = ["I0","I1","I2","I3","IO0","IO1","SI0","SI1","SI2","SI3","SO0"]
|
||||||
|
ORDER_FIOM = [f"X{i}_{j}" for i in [3,2,1,0,7,6,5,4] for j in (0,1)]
|
||||||
|
ORDER_FIOH = [f"C{i}_{j}" for i in [7,5,3,1,8,6,4,2] for j in ("A","B")]
|
||||||
|
|
||||||
|
def load_canon(paths: List[Path]) -> Dict[str, str]:
|
||||||
|
canon = {}
|
||||||
|
for p in paths:
|
||||||
|
if not p or not Path(p).exists():
|
||||||
|
continue
|
||||||
|
c = pd.read_excel(p, dtype=str).rename(columns=lambda x: str(x).strip())
|
||||||
|
cols = [col for col in c.columns if col.lower() in ("assigned device","assigned_device","device","name")]
|
||||||
|
descs = [col for col in c.columns if col.lower() in ("description","desc")]
|
||||||
|
if not cols or not descs:
|
||||||
|
continue
|
||||||
|
c = c[[cols[0], descs[0]]].dropna()
|
||||||
|
c[cols[0]] = c[cols[0]].astype(str).str.strip().str.upper()
|
||||||
|
c[descs[0]] = c[descs[0]].astype(str).str.strip().str.upper()
|
||||||
|
canon.update(dict(zip(c[cols[0]], c[descs[0]])))
|
||||||
|
return canon
|
||||||
|
|
||||||
|
def strip_digits(tok: str) -> str:
|
||||||
|
if tok.startswith(("EPC","FPE")):
|
||||||
|
return tok
|
||||||
|
import re as _re
|
||||||
|
return _re.sub(r'\d+$','', tok)
|
||||||
|
|
||||||
|
def family_keys(dev: str):
|
||||||
|
s = dev.strip().upper()
|
||||||
|
parts = [p for p in s.split("_") if p]
|
||||||
|
if not parts: return []
|
||||||
|
head = "PDP" if s.startswith("PDP") else "CONV"
|
||||||
|
keys = []
|
||||||
|
if len(parts) >= 3:
|
||||||
|
t2, t1 = parts[-2], parts[-1]
|
||||||
|
t1s = t1 if t1.startswith(("EPC","FPE")) else strip_digits(t1)
|
||||||
|
keys += [f"{head}_{t2}_{t1}", f"{head}_{t2}_{t1s}"]
|
||||||
|
import re as _re
|
||||||
|
last = parts[-1]
|
||||||
|
if _re.fullmatch(r"(DISC|DSIC|TPE\d*|JPE\d*|ENW\d*|ENSH\d*|PRX1?|PX1|LRPE\d*|TS\d*|BDS\d*|EPC\d+|FPE\d+|CB\d+|PWM\d+|FIOH1|STO1)", last):
|
||||||
|
keys += [f"{head}_{last}", last]
|
||||||
|
if "BCN" in s:
|
||||||
|
if s.endswith("_A"): keys.append("CONV_BCN_A")
|
||||||
|
elif s.endswith("_R"): keys.append("CONV_BCN_R")
|
||||||
|
elif s.endswith("_G"): keys.append("CONV_BCN_G")
|
||||||
|
elif s.endswith("_B"): keys.append("CONV_BCN_B")
|
||||||
|
elif s.endswith("_W"): keys.append("CONV_BCN_W")
|
||||||
|
elif s.endswith("_H"): keys.append("CONV_BCN_H")
|
||||||
|
else: keys.append("CONV_BCN")
|
||||||
|
seen=set(); out=[]
|
||||||
|
for k in keys:
|
||||||
|
if k and k not in seen:
|
||||||
|
seen.add(k); out.append(k)
|
||||||
|
return out
|
||||||
|
|
||||||
|
def fallback_desc(dev: str) -> 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"_EPC\d+|^EPC\d+", d): return "E-STOP PULLCORD"
|
||||||
|
if "ENSH" in d: return "SHAFT ENCODER"
|
||||||
|
if "ENW" in d: return "WHEEL ENCODER"
|
||||||
|
if "LRPE" in d and "_R" in d: return "LONG RANGE PHOTOEYE RCV"
|
||||||
|
if "LRPE" in d and "_S" in d: return "LONG RANGE PHOTOEYE SND"
|
||||||
|
if "LRPE" in d: return "LONG RANGE PHOTOEYE"
|
||||||
|
if any(x in d for x in ["PX1","PRX","PRX1"]): return "PROXIMITY SENSOR"
|
||||||
|
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:
|
||||||
|
if d.endswith("_A"): return "AMBER BEACON LIGHT"
|
||||||
|
if d.endswith("_R"): return "RED BEACON LIGHT"
|
||||||
|
if d.endswith("_G"): return "GREEN BEACON LIGHT"
|
||||||
|
if d.endswith("_B"): return "BLUE BEACON LIGHT"
|
||||||
|
if d.endswith("_W"): return "WHITE BEACON LIGHT"
|
||||||
|
if d.endswith("_H"): return "HORN BEACON"
|
||||||
|
return "BEACON LIGHT"
|
||||||
|
if d.endswith("_FPE1"): return "FULL PHOTOEYE 100%"
|
||||||
|
if d.endswith("_FPE2"): return "FULL PHOTOEYE 50%"
|
||||||
|
if _re.search(r"_PS\d+$", d): return "PRESSURE SENSOR"
|
||||||
|
if _re.search(r"_BDS\d+_R$", d): return "BELT DISENGAGEMENT SENSOR RCV"
|
||||||
|
if _re.search(r"_BDS\d+_S$", d): return "BELT DISENGAGEMENT SENSOR SND"
|
||||||
|
if _re.search(r"_TS\d+_R$", d): return "TRASH SENSOR RCV"
|
||||||
|
if _re.search(r"_TS\d+_S$", d): return "TRASH SENSOR SND"
|
||||||
|
if "STO1" in d: return "SAFETY TORQUE OFF"
|
||||||
|
if d.startswith("PDP") and "_CB" in d: return "CIRCUIT BREAKER MONITORING"
|
||||||
|
if d.startswith("PDP") and "_PWM" in d: return "PHASE MONITOR"
|
||||||
|
return "DEVICE"
|
||||||
|
|
||||||
|
def desc_for(dev: str, canon: dict) -> 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)
|
||||||
|
|
||||||
|
def choose_order(controller_name: str, available_columns):
|
||||||
|
s = str(controller_name).upper()
|
||||||
|
if "VFD" in s:
|
||||||
|
return [c for c in ORDER_VFD if c in available_columns]
|
||||||
|
if "FIOM" in s:
|
||||||
|
return [c for c in ORDER_FIOM if c in available_columns]
|
||||||
|
if "FIOH" in s:
|
||||||
|
return [c for c in ORDER_FIOH if c in available_columns]
|
||||||
|
return [c for c in available_columns if c in set(ORDER_FIOM+ORDER_FIOH+ORDER_VFD)]
|
||||||
|
|
||||||
|
def priority_for_controller(controller_name: str) -> int:
|
||||||
|
s = str(controller_name).upper()
|
||||||
|
if s.startswith("PDP") and "FIOM" in s: return 0
|
||||||
|
if s.startswith("PDP") and "FIOH" in s: return 1
|
||||||
|
return 2
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import pandas as pd
|
||||||
|
import argparse, re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ap = argparse.ArgumentParser(description="Build 5-column IO mapping Excel from source workbook.")
|
||||||
|
ap.add_argument("input", help="Input Excel workbook.")
|
||||||
|
ap.add_argument("output", help="Output Excel path.")
|
||||||
|
ap.add_argument("--sheet", default="Summary", help="Sheet to read (default Summary; falls back to first sheet).")
|
||||||
|
ap.add_argument("--canon", nargs="*", default=[], help="Optional canon Excel(s) with Assigned device/Description columns.")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
inp = Path(args.input)
|
||||||
|
xls = pd.ExcelFile(inp)
|
||||||
|
sheet = args.sheet if args.sheet in xls.sheet_names else xls.sheet_names[0]
|
||||||
|
df = pd.read_excel(inp, sheet_name=sheet, dtype=str).rename(columns=lambda c: str(c).strip())
|
||||||
|
|
||||||
|
if "P_TAG1" not in df.columns:
|
||||||
|
raise SystemExit("P_TAG1 column not found in the selected sheet.")
|
||||||
|
|
||||||
|
sig_cols_available = [c for c in df.columns if re.fullmatch(r"(I0|I1|I2|I3|IO0|IO1|SI0|SI1|SI2|SI3|SO0|X[0-7]_[01]|C[1-8]_[AB])", c)]
|
||||||
|
|
||||||
|
canon_map = load_canon([Path(p) for p in args.canon])
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
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)
|
||||||
|
dev = (str(val).strip() if pd.notna(val) else "")
|
||||||
|
if dev == "":
|
||||||
|
dev = "SPARE"
|
||||||
|
addr = f"{ctrl}_{sig}"
|
||||||
|
desc = "" if dev.upper()=="SPARE" else desc_for(dev, canon_map)
|
||||||
|
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"
|
||||||
|
elif re.search(r'_SS\d+_SPB$', dU): desc = "SS STATION START PUSHBUTTON"
|
||||||
|
elif re.search(r'_SS\d+_STPB$', dU): desc = "SS STATION STOP PUSHBUTTON"
|
||||||
|
elif re.search(r'_S\d+_PB_LT$', dU): desc = "START PUSHBUTTON LIGHT"
|
||||||
|
elif re.search(r'_S\d+_PB$', dU): desc = "START PUSHBUTTON"
|
||||||
|
elif re.search(r'_JR\d+_PB_LT$', dU): desc = "JAM RESET PUSHBUTTON LIGHT"
|
||||||
|
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"
|
||||||
|
rows.append({
|
||||||
|
"Controller name": ctrl,
|
||||||
|
"Signal type": sig,
|
||||||
|
"Address name": addr,
|
||||||
|
"Assigned device": dev,
|
||||||
|
"Description": desc
|
||||||
|
})
|
||||||
|
|
||||||
|
out = pd.DataFrame(rows)
|
||||||
|
out["_p"] = out["Controller name"].map(priority_for_controller)
|
||||||
|
out = out.sort_values(["_p","Controller name"], kind="mergesort").drop(columns=["_p"]).reset_index(drop=True)
|
||||||
|
out.to_excel(Path(args.output), index=False)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
26
AutoCAD/IO/compare_descriptions.py
Normal file
26
AutoCAD/IO/compare_descriptions.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
df1 = pd.read_excel('output.xlsx')
|
||||||
|
df2 = pd.read_excel('Amazon CDW5_Devices IO.xlsx')
|
||||||
|
|
||||||
|
print('=== Sample comparisons ===')
|
||||||
|
dev_rows = df1[df1['Description'].str.upper() == 'DEVICE'].head(20)
|
||||||
|
|
||||||
|
for idx, row in dev_rows.iterrows():
|
||||||
|
src = df2[(df2['Controller name'] == row['Controller name']) &
|
||||||
|
(df2['Signal type'] == row['Signal type']) &
|
||||||
|
(df2['Assigned device'] == row['Assigned device'])]
|
||||||
|
if not src.empty:
|
||||||
|
print(f"Device: {row['Assigned device']}")
|
||||||
|
print(f" Output desc: {row['Description']}")
|
||||||
|
print(f" Source desc: {src.iloc[0]['Description']}")
|
||||||
|
print()
|
||||||
|
else:
|
||||||
|
# Try partial match
|
||||||
|
src = df2[(df2['Assigned device'] == row['Assigned device'])]
|
||||||
|
if not src.empty:
|
||||||
|
print(f"Device: {row['Assigned device']}")
|
||||||
|
print(f" Output desc: {row['Description']}")
|
||||||
|
print(f" Source desc (found by device only): {src.iloc[0]['Description']}")
|
||||||
|
print()
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM14-IO.dwg
Normal file
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM14-IO.dwg
Normal file
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM14-Loop.bak
Normal file
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM14-Loop.bak
Normal file
Binary file not shown.
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM15-IO.dwg
Normal file
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM15-IO.dwg
Normal file
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM15-Loop.bak
Normal file
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM15-Loop.bak
Normal file
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM15-Loop.dwg
Normal file
BIN
AutoCAD/MCM_Installer_drawing/2433-AMZ-CDW5-MCM15-Loop.dwg
Normal file
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/2503_AMZ-BNA8-MCM02-600 1.bak
Normal file
BIN
AutoCAD/MCM_Installer_drawing/2503_AMZ-BNA8-MCM02-600 1.bak
Normal file
Binary file not shown.
410
AutoCAD/MCM_Installer_drawing/DPM_Diagram.lsp
Normal file
410
AutoCAD/MCM_Installer_drawing/DPM_Diagram.lsp
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
(defun clearDrawing ( / ss)
|
||||||
|
(setq ss (ssget "_X" '((0 . "*"))))
|
||||||
|
(if ss (command "_.erase" ss ""))
|
||||||
|
(princ "\nDrawing cleared.")
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun getVisibilityFromName (deviceName partNumber)
|
||||||
|
(cond
|
||||||
|
((wcmatch (strcase deviceName) "*APF*") "APF")
|
||||||
|
((and (wcmatch (strcase deviceName) "*VFD*")
|
||||||
|
partNumber
|
||||||
|
(wcmatch (strcase partNumber) "35S*")) "APF")
|
||||||
|
((wcmatch (strcase deviceName) "*VFD*") "VFD")
|
||||||
|
((wcmatch (strcase deviceName) "*SIO*") "SIO")
|
||||||
|
((wcmatch (strcase deviceName) "*FIO*") "FIO")
|
||||||
|
((wcmatch (strcase deviceName) "*SPARE*") "")
|
||||||
|
(T "DEFAULT")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun str-trim (s)
|
||||||
|
(vl-string-trim " \t\n\r" s)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun setDeviceAttributes (blk ip device port / tag2 tag3 isVFD spacePos slashPos baseName part2 tag ip1 ip2 spacePosIP)
|
||||||
|
(setq tag2 device)
|
||||||
|
(setq tag3 "")
|
||||||
|
(setq ip1 ip)
|
||||||
|
(setq ip2 "")
|
||||||
|
|
||||||
|
;; === VFD logic ===
|
||||||
|
(if (and device (vl-string-search "VFD" (strcase device)))
|
||||||
|
(progn
|
||||||
|
(setq spacePos (vl-string-search " " device))
|
||||||
|
(if spacePos
|
||||||
|
(progn
|
||||||
|
(setq part1 (substr device 1 spacePos))
|
||||||
|
(setq part2 (substr device (+ spacePos 2)))
|
||||||
|
(setq slashPos (vl-string-search "/VFD" part1))
|
||||||
|
(if slashPos
|
||||||
|
(setq baseName (substr part1 1 slashPos))
|
||||||
|
(setq baseName part1)
|
||||||
|
)
|
||||||
|
(setq tag2 (str-trim part1))
|
||||||
|
(setq tag3 (strcat (str-trim baseName) "/" (str-trim part2)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; === IP splitting ===
|
||||||
|
(setq spacePosIP (vl-string-search " " ip))
|
||||||
|
(if spacePosIP
|
||||||
|
(progn
|
||||||
|
(setq ip1 (str-trim (substr ip 1 spacePosIP)))
|
||||||
|
(setq ip2 (str-trim (substr ip (+ spacePosIP 2))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(foreach att (vlax-invoke blk 'GetAttributes)
|
||||||
|
(setq tag (strcase (vla-get-tagstring att)))
|
||||||
|
(cond
|
||||||
|
((= tag "IP") (vla-put-textstring att ip1))
|
||||||
|
((= tag "IP2") (vla-put-textstring att ip2))
|
||||||
|
((= tag "TAG2") (vla-put-textstring att tag2))
|
||||||
|
((= tag "PORT") (vla-put-textstring att port))
|
||||||
|
((= tag "TAG3") (if (> (strlen tag3) 0) (vla-put-textstring att tag3)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun place-enet-devices (originX originY deviceGroup / blockName width count i x y basePt lastEnt targetPt blk visibilityValue deviceName device ip fullBlockName port)
|
||||||
|
(setq blockName "HDV2_ENET_DEVICE_")
|
||||||
|
(setq width 1.2)
|
||||||
|
(setq count (length deviceGroup))
|
||||||
|
(setq i 0)
|
||||||
|
(setq y 20.4718)
|
||||||
|
|
||||||
|
(while (< i count)
|
||||||
|
(setq blockSpacing 1.2)
|
||||||
|
(setq groupWidth (* blockSpacing (1- count)))
|
||||||
|
(setq centerX 21.2)
|
||||||
|
(setq startX (- centerX (/ groupWidth 2.0)))
|
||||||
|
(setq x (+ startX (* i blockSpacing) 0.3))
|
||||||
|
(setq basePt '(0 0 0))
|
||||||
|
(setq targetPt (list (+ originX x) (+ originY y) 0))
|
||||||
|
|
||||||
|
(setq devicePair (nth i deviceGroup))
|
||||||
|
(setq device (cdr (assoc "NAME" devicePair)))
|
||||||
|
(setq ip (cdr (assoc "IP" devicePair)))
|
||||||
|
(setq port (cdr (assoc "PORT" devicePair)))
|
||||||
|
(setq partNumber (cdr (assoc "PARTNUM" devicePair)))
|
||||||
|
(setq deviceName (getVisibilityFromName device partNumber))
|
||||||
|
(setq fullBlockName (strcat blockName deviceName))
|
||||||
|
|
||||||
|
(command "_.-INSERT" fullBlockName basePt 0.8 0.8 0.8 0)
|
||||||
|
(if (setq lastEnt (entlast))
|
||||||
|
(progn
|
||||||
|
(setq blk (vlax-ename->vla-object lastEnt))
|
||||||
|
(vla-move blk
|
||||||
|
(vlax-3d-point basePt)
|
||||||
|
(vlax-3d-point targetPt)
|
||||||
|
)
|
||||||
|
(vla-put-rotation blk 0.0)
|
||||||
|
(setDeviceAttributes blk ip device port)
|
||||||
|
)
|
||||||
|
(princ "\nFailed to get last entity.")
|
||||||
|
)
|
||||||
|
(setq i (1+ i))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun labelZone32Lines (ent / vlaBlock att basePt labelPt labelStr height index rotation)
|
||||||
|
(if (and ent (eq (cdr (assoc 0 (entget ent))) "INSERT"))
|
||||||
|
(progn
|
||||||
|
(setq vlaBlock (vlax-ename->vla-object ent))
|
||||||
|
(setq index 1)
|
||||||
|
(foreach att (vlax-invoke vlaBlock 'GetAttributes)
|
||||||
|
(if (wcmatch (strcase (vla-get-tagstring att)) "LINE*")
|
||||||
|
(progn
|
||||||
|
(setq labelStr (if (< index 10) (strcat "0" (itoa index)) (itoa index)))
|
||||||
|
(setq basePt (vlax-get att 'InsertionPoint))
|
||||||
|
(setq height (vla-get-height att))
|
||||||
|
(setq rotation (vla-get-rotation att))
|
||||||
|
(setq labelPt
|
||||||
|
(list (- (car basePt) 0.05)
|
||||||
|
(+ (cadr basePt) 0.65)
|
||||||
|
(caddr basePt)))
|
||||||
|
(entmake
|
||||||
|
(list
|
||||||
|
(cons 0 "TEXT")
|
||||||
|
(cons 8 "0")
|
||||||
|
(cons 7 "WD")
|
||||||
|
(cons 62 7)
|
||||||
|
(cons 10 labelPt)
|
||||||
|
(cons 11 labelPt)
|
||||||
|
(cons 40 height)
|
||||||
|
(cons 72 1)
|
||||||
|
(cons 73 2)
|
||||||
|
(cons 1 labelStr)
|
||||||
|
(cons 50 rotation)
|
||||||
|
(cons 100 "AcDbEntity")
|
||||||
|
(cons 100 "AcDbText")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(setq index (1+ index))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(princ "\nInvalid entity passed to labelZone32Lines.")
|
||||||
|
)
|
||||||
|
(princ)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun setLayoutWireNumbers (layoutEnt zoneNumber layoutIndex / vlaBlock att tag wireNumber cblIndex layoutNum)
|
||||||
|
(if (and layoutEnt (eq (cdr (assoc 0 (entget layoutEnt))) "INSERT"))
|
||||||
|
(progn
|
||||||
|
(setq vlaBlock (vlax-ename->vla-object layoutEnt))
|
||||||
|
(setq cblIndex 5)
|
||||||
|
|
||||||
|
;; Format layout number as 3-digit string (702, 703, 704, etc.)
|
||||||
|
(setq layoutNum (itoa (+ 702 layoutIndex)))
|
||||||
|
|
||||||
|
(foreach att (vlax-invoke vlaBlock 'GetAttributes)
|
||||||
|
(setq tag (strcase (vla-get-tagstring att)))
|
||||||
|
(if (= tag "WIRENO")
|
||||||
|
(progn
|
||||||
|
;; Concatenate: zone + layoutNum + "-CBL" + cblNum
|
||||||
|
(setq wireNumber
|
||||||
|
(strcat
|
||||||
|
zoneNumber
|
||||||
|
layoutNum
|
||||||
|
"-CBL"
|
||||||
|
(if (< cblIndex 10)
|
||||||
|
(strcat "0" (itoa cblIndex))
|
||||||
|
(itoa cblIndex)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(vla-put-textstring att wireNumber)
|
||||||
|
(setq cblIndex (1+ cblIndex))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(princ "\nInvalid layout entity passed to setLayoutWireNumbers.")
|
||||||
|
)
|
||||||
|
(princ)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun parseCSVLine (line / pos result)
|
||||||
|
(setq result '())
|
||||||
|
(while (setq pos (vl-string-search "," line))
|
||||||
|
(setq result (append result (list (substr line 1 pos))))
|
||||||
|
(setq line (substr line (+ pos 2)))
|
||||||
|
)
|
||||||
|
(append result (list line))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun getDPMDataFromCSV ( / file filename line headers row dpm ip name deviceIP port partNumber dpmList deviceGroups currentGroup zoneRaw zoneNumber)
|
||||||
|
(setq filename (getfiled "Select CSV File" (strcat (getenv "USERPROFILE") "\\Desktop\\") "csv" 0))
|
||||||
|
(if (not filename)
|
||||||
|
(progn (princ "\nNo file selected.") (exit))
|
||||||
|
)
|
||||||
|
(setq file (open filename "r"))
|
||||||
|
(if (not file)
|
||||||
|
(progn (princ "\nFailed to open file.") (exit))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Skip metadata lines and header row (first 3 lines)
|
||||||
|
(read-line file)
|
||||||
|
(read-line file)
|
||||||
|
(read-line file)
|
||||||
|
(setq dpmList '())
|
||||||
|
(setq deviceGroups '())
|
||||||
|
(setq currentGroup '())
|
||||||
|
(setq zoneNumber "")
|
||||||
|
|
||||||
|
(while (setq line (read-line file))
|
||||||
|
(setq row (parseCSVLine line))
|
||||||
|
;; Ensure row has enough columns
|
||||||
|
(if (>= (length row) 10)
|
||||||
|
(progn
|
||||||
|
(setq dpm (nth 3 row))
|
||||||
|
(setq ip (nth 4 row))
|
||||||
|
(setq name (nth 5 row))
|
||||||
|
(setq partNumber (nth 6 row))
|
||||||
|
(setq deviceIP (nth 7 row))
|
||||||
|
(setq port (nth 8 row))
|
||||||
|
(setq zoneRaw (nth 9 row))
|
||||||
|
|
||||||
|
;; Extract zone number - preserve it as-is with leading zeros
|
||||||
|
(if (and zoneRaw (/= zoneRaw ""))
|
||||||
|
(progn
|
||||||
|
;; Extract only digits from zone string (e.g., "MCM01" -> "01")
|
||||||
|
(setq zoneNumber
|
||||||
|
(str-trim
|
||||||
|
(vl-list->string
|
||||||
|
(vl-remove-if-not
|
||||||
|
'(lambda (c) (<= 48 c 57))
|
||||||
|
(vl-string->list zoneRaw)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(if (and dpm (/= dpm ""))
|
||||||
|
(progn
|
||||||
|
(if (> (length currentGroup) 0)
|
||||||
|
(progn
|
||||||
|
(while (< (length currentGroup) 24)
|
||||||
|
(setq currentGroup
|
||||||
|
(append currentGroup
|
||||||
|
(list (list
|
||||||
|
(cons "NAME" "SPARE")
|
||||||
|
(cons "IP" "")
|
||||||
|
(cons "PORT" "")
|
||||||
|
(cons "ZONE" zoneNumber)
|
||||||
|
(cons "PARTNUM" "")
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(setq deviceGroups (append deviceGroups (list currentGroup)))
|
||||||
|
(setq currentGroup '())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(if (not (assoc dpm dpmList))
|
||||||
|
(setq dpmList (append dpmList (list (cons dpm (list ip zoneNumber)))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(if (and name (/= name ""))
|
||||||
|
(setq currentGroup
|
||||||
|
(append currentGroup
|
||||||
|
(list (list
|
||||||
|
(cons "NAME" name)
|
||||||
|
(cons "IP" deviceIP)
|
||||||
|
(cons "PORT" port)
|
||||||
|
(cons "ZONE" zoneNumber)
|
||||||
|
(cons "PARTNUM" partNumber)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(if (= (length currentGroup) 24)
|
||||||
|
(progn
|
||||||
|
(setq deviceGroups (append deviceGroups (list currentGroup)))
|
||||||
|
(setq currentGroup '())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(if (> (length currentGroup) 0)
|
||||||
|
(progn
|
||||||
|
(while (< (length currentGroup) 24)
|
||||||
|
(setq currentGroup
|
||||||
|
(append currentGroup
|
||||||
|
(list (list
|
||||||
|
(cons "NAME" "SPARE")
|
||||||
|
(cons "IP" "")
|
||||||
|
(cons "PORT" "")
|
||||||
|
(cons "ZONE" zoneNumber)
|
||||||
|
(cons "PARTNUM" "")
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(setq deviceGroups (append deviceGroups (list currentGroup)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(close file)
|
||||||
|
(list dpmList deviceGroups)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun c:init-diagrams ( / blockName count offsetX i x y basePt targetPt lastEnt dpmPair dpmName dpmIP deviceGroup dpmUpsPt dpmUpsObj tag ent vlaObj zoneName zoneNumber dpmData)
|
||||||
|
(clearDrawing)
|
||||||
|
(setq blockName "layout")
|
||||||
|
(setq csvData (getDPMDataFromCSV))
|
||||||
|
(setq dpmList (car csvData))
|
||||||
|
(setq deviceGroups (cadr csvData))
|
||||||
|
(setq count (length dpmList))
|
||||||
|
|
||||||
|
(if (and blockName (> count 0))
|
||||||
|
(progn
|
||||||
|
(setq offsetX 43.5)
|
||||||
|
(setq i 0)
|
||||||
|
(while (< i count)
|
||||||
|
(setq x (* i offsetX))
|
||||||
|
(setq y 0)
|
||||||
|
(setq basePt '(0 0 0))
|
||||||
|
(setq targetPt (list x y 0))
|
||||||
|
|
||||||
|
;; Get DPM data and zone number
|
||||||
|
(setq dpmPair (nth i dpmList))
|
||||||
|
(setq dpmName (car dpmPair))
|
||||||
|
(setq dpmData (cdr dpmPair))
|
||||||
|
(setq dpmIP (car dpmData))
|
||||||
|
(setq zoneNumber (cadr dpmData))
|
||||||
|
(setq deviceGroup (nth i deviceGroups))
|
||||||
|
|
||||||
|
;; Insert layout
|
||||||
|
(command "_.-INSERT" blockName basePt 1 1 0)
|
||||||
|
(setq lastEnt (entlast))
|
||||||
|
(if lastEnt
|
||||||
|
(progn
|
||||||
|
(vla-move
|
||||||
|
(vlax-ename->vla-object lastEnt)
|
||||||
|
(vlax-3d-point basePt)
|
||||||
|
(vlax-3d-point targetPt)
|
||||||
|
)
|
||||||
|
(setLayoutWireNumbers lastEnt zoneNumber i)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Insert DPM-UPS
|
||||||
|
(setq dpmUpsPt (list (+ x 16.1) (+ y 2.1173) 0))
|
||||||
|
(command "_.-INSERT" "DPM-UPS" dpmUpsPt 1 1 0)
|
||||||
|
|
||||||
|
(setq lastEnt (entlast))
|
||||||
|
(if lastEnt
|
||||||
|
(progn
|
||||||
|
(setq dpmUpsObj (vlax-ename->vla-object lastEnt))
|
||||||
|
(foreach att (vlax-invoke dpmUpsObj 'GetAttributes)
|
||||||
|
(setq tag (strcase (vla-get-tagstring att)))
|
||||||
|
(cond
|
||||||
|
((= tag "IPADDRESS") (vla-put-textstring att dpmIP))
|
||||||
|
((= tag "TAG2") (vla-put-textstring att dpmName))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Insert ZONE_32H
|
||||||
|
(setq desiredX (+ x 0.7658))
|
||||||
|
(setq desiredY (+ y 25.6873))
|
||||||
|
(command "_.-INSERT" "ZONE_32H" '(0 0 0) 1 1 0)
|
||||||
|
(setq ent (entlast))
|
||||||
|
(if ent
|
||||||
|
(progn
|
||||||
|
(setq vlaObj (vlax-ename->vla-object ent))
|
||||||
|
(vla-move vlaObj (vlax-3d-point 0 0 0) (vlax-3d-point desiredX desiredY 0))
|
||||||
|
(setq zoneName (strcat zoneNumber (itoa (+ 702 i)) "."))
|
||||||
|
(foreach att (vlax-invoke vlaObj 'GetAttributes)
|
||||||
|
(vla-put-textstring att zoneName)
|
||||||
|
)
|
||||||
|
(labelZone32Lines ent)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; layout label
|
||||||
|
(setq labelPt (list (+ desiredX 14.0) 32.0 0.0))
|
||||||
|
(command "_.text" labelPt 1.5 0 (strcat "Layout " (itoa (1+ i))))
|
||||||
|
(place-enet-devices x y deviceGroup)
|
||||||
|
(setq i (1+ i))
|
||||||
|
)
|
||||||
|
(princ (strcat "\nInserted " (itoa count) " layouts side-by-side."))
|
||||||
|
)
|
||||||
|
(princ "\nInvalid input.")
|
||||||
|
)
|
||||||
|
(princ)
|
||||||
|
)
|
||||||
1286
AutoCAD/MCM_Installer_drawing/IO.lsp
Normal file
1286
AutoCAD/MCM_Installer_drawing/IO.lsp
Normal file
File diff suppressed because it is too large
Load Diff
330
AutoCAD/MCM_Installer_drawing/Loop.lsp
Normal file
330
AutoCAD/MCM_Installer_drawing/Loop.lsp
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
(defun parseCSVLine (line / pos result)
|
||||||
|
(setq result '())
|
||||||
|
(while (setq pos (vl-string-search "," line))
|
||||||
|
(setq result (cons (substr line 1 pos) result))
|
||||||
|
(setq line (substr line (+ pos 2)))
|
||||||
|
)
|
||||||
|
(reverse (cons line result))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun readDPMNamesAndZoneFromCSV ( / file filename line zone dpmList counter row name)
|
||||||
|
;; Ask user to select CSV
|
||||||
|
(setq filename (getfiled "Select CSV File" (strcat (getenv "USERPROFILE") "\\Desktop\\") "csv" 0))
|
||||||
|
(if (not filename)
|
||||||
|
(progn (princ "\nNo file selected.") nil)
|
||||||
|
(progn
|
||||||
|
(setq file (open filename "r"))
|
||||||
|
(if (not file)
|
||||||
|
(progn (princ "\nFailed to open file.") nil)
|
||||||
|
(progn
|
||||||
|
;; Skip first line (header)
|
||||||
|
(read-line file)
|
||||||
|
|
||||||
|
;; Read first line (A2..F2) to get zone
|
||||||
|
(setq line (read-line file))
|
||||||
|
(setq row (parseCSVLine line))
|
||||||
|
(setq zone (nth 5 row)) ;; F2
|
||||||
|
;; Keep only digits from zone
|
||||||
|
(setq zone (vl-list->string (vl-remove-if-not '(lambda (c) (<= 48 c 57)) (vl-string->list zone))))
|
||||||
|
|
||||||
|
;; Initialize DPM list
|
||||||
|
(setq dpmList '())
|
||||||
|
(setq counter 1)
|
||||||
|
|
||||||
|
;; Read rest of the file
|
||||||
|
(while (setq line (read-line file))
|
||||||
|
(setq row (parseCSVLine line))
|
||||||
|
(setq name (nth 0 row)) ;; Column A
|
||||||
|
;; Only take every 24th row starting from first
|
||||||
|
(if (= (rem counter 24) 1)
|
||||||
|
(if (and name (/= name ""))
|
||||||
|
(setq dpmList (append dpmList (list (list (cons "NAME" name) (cons "ZONE" zone)))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(setq counter (1+ counter))
|
||||||
|
)
|
||||||
|
|
||||||
|
(close file)
|
||||||
|
dpmList
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(defun updateTagAttribute (ent tagValue / obj attribs att)
|
||||||
|
;; Convert entity to VLA object
|
||||||
|
(setq obj (vlax-ename->vla-object ent))
|
||||||
|
(if (and obj (eq (vla-get-hasattributes obj) :vlax-true))
|
||||||
|
(progn
|
||||||
|
(setq attribs (vlax-invoke obj 'GetAttributes))
|
||||||
|
(foreach att attribs
|
||||||
|
(if (eq (strcase (vla-get-TagString att)) "TAG1")
|
||||||
|
(vla-put-TextString att tagValue)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun addTextLabel (pt1 pt2 labelText / midPt textHeight alignPt)
|
||||||
|
;; Calculate midpoint of horizontal line
|
||||||
|
(setq midPt (list (/ (+ (car pt1) (car pt2)) 2.0) (cadr pt1)))
|
||||||
|
(setq textHeight 0.3) ;; Adjust text height as needed
|
||||||
|
(setq alignPt (list (car midPt) (+ (cadr midPt) 0.2))) ;; 0.3 units above line
|
||||||
|
|
||||||
|
;; Create text entity above the line with center alignment
|
||||||
|
(entmakex (list
|
||||||
|
'(0 . "TEXT")
|
||||||
|
(cons 10 alignPt) ;; Insertion point
|
||||||
|
(cons 11 alignPt) ;; Alignment point (required for centered text)
|
||||||
|
(cons 40 textHeight)
|
||||||
|
(cons 1 labelText)
|
||||||
|
(cons 72 1) ;; Center horizontal alignment
|
||||||
|
(cons 73 0) ;; Baseline vertical alignment
|
||||||
|
'(8 . "AS_ENET_CABLE") ;; Same layer as cables
|
||||||
|
'(62 . 7) ;; White color
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun c:Init_Network ( / dpmCount rowCount colCount i row col x y spacingX spacingY blkName scaleFactor
|
||||||
|
startPt endPt zigzagStart secondCableEnds zigzagStarts cableCounter )
|
||||||
|
|
||||||
|
(setq firstHorizontalLabeled nil)
|
||||||
|
(setq cableCounter 2)
|
||||||
|
|
||||||
|
(setq blkName "DPM")
|
||||||
|
(setq hdvBlkName "HDV2_1756-EN4TR_CHILD")
|
||||||
|
(setq scaleFactor 0.45)
|
||||||
|
(setq spacingX 15.0)
|
||||||
|
(setq spacingY 9.0)
|
||||||
|
(setq dpmOffsetX 5.0)
|
||||||
|
(setq hdvY 4.0)
|
||||||
|
(setq secondCableEnds '())
|
||||||
|
(setq zigzagStarts '())
|
||||||
|
|
||||||
|
(command "_.ERASE" "ALL" "")
|
||||||
|
(setq filename (getfiled "Select CSV File" (strcat (getenv "USERPROFILE") "\\Desktop\\") "csv" 0))
|
||||||
|
(if (not filename)
|
||||||
|
(progn (princ "\nNo file selected.") (exit))
|
||||||
|
(progn
|
||||||
|
(setq file (open filename "r"))
|
||||||
|
(if (not file)
|
||||||
|
(progn (princ "\nFailed to open file.") (exit))
|
||||||
|
(progn
|
||||||
|
;; Skip header line
|
||||||
|
(read-line file)
|
||||||
|
|
||||||
|
;; Read first data line for zone
|
||||||
|
(setq line (read-line file)) ;; <-- assign to line
|
||||||
|
(setq row (parseCSVLine line))
|
||||||
|
(setq devices '())
|
||||||
|
(setq zone (nth 5 row)) ;; column F
|
||||||
|
;; extract digits only
|
||||||
|
(setq zone (vl-list->string
|
||||||
|
(vl-remove-if-not
|
||||||
|
'(lambda (c) (<= 48 c 57))
|
||||||
|
(vl-string->list zone)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
;; Read rest of the lines for DPM names (column D, index 3)
|
||||||
|
(while (and (setq line (read-line file)) (/= line ""))
|
||||||
|
(setq row (parseCSVLine line))
|
||||||
|
(setq name (nth 3 row)) ;; Column D contains DPM names
|
||||||
|
;; Only include actual DPM names (must contain DPM and underscore, exclude header "DPM")
|
||||||
|
(if (and name (/= name "") (/= (strcase name) "DPM") (wcmatch (strcase name) "*_DPM*"))
|
||||||
|
(setq devices (append devices (list name)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(close file)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(setq dpmCount (length devices))
|
||||||
|
|
||||||
|
;; Calculate grid size
|
||||||
|
(setq colCount (fix (sqrt dpmCount)))
|
||||||
|
(if (< (* colCount colCount) dpmCount)
|
||||||
|
(setq colCount (+ colCount 1))
|
||||||
|
)
|
||||||
|
(setq rowCount (fix (/ (+ dpmCount colCount -1) colCount)))
|
||||||
|
|
||||||
|
(setq gridWidth (* (- colCount 1) spacingX))
|
||||||
|
(setq centerOffsetX (- (/ gridWidth 2.0)))
|
||||||
|
|
||||||
|
(command "_.LAYER" "S" "0" "")
|
||||||
|
(command "_.INSERT" hdvBlkName (list 0 hdvY) scaleFactor scaleFactor 0)
|
||||||
|
|
||||||
|
(setq i 0)
|
||||||
|
(repeat dpmCount
|
||||||
|
(setq row (fix (/ i colCount)))
|
||||||
|
(setq col (rem i colCount))
|
||||||
|
(setq x (+ (* col spacingX) dpmOffsetX))
|
||||||
|
(setq y (* -1 row spacingY))
|
||||||
|
(setq dpmName (nth i devices))
|
||||||
|
|
||||||
|
(command "_.LAYER" "S" "0" "")
|
||||||
|
(command "_.INSERT" blkName (list x y) scaleFactor scaleFactor 0)
|
||||||
|
|
||||||
|
(setq ent (entlast))
|
||||||
|
(updateTagAttribute ent dpmName)
|
||||||
|
|
||||||
|
;; First cable: vertical up
|
||||||
|
(setq startPt (list (+ x 7.3656) (+ y 4.4406)))
|
||||||
|
(setq endPt (list (car startPt) (+ (cadr startPt) 2.0)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 startPt) (cons 11 endPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
(setq secondCableEnds (append secondCableEnds (list endPt)))
|
||||||
|
|
||||||
|
(if (or (= (rem (1+ i) colCount) 0) (= i (- dpmCount 1)))
|
||||||
|
(progn
|
||||||
|
(setq startPt (list (+ x 7.2677) (+ y 3.6094)))
|
||||||
|
(setq elbowPt (list (- (car startPt) 0.2) (- (cadr startPt) 0.2)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 startPt) (cons 11 elbowPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
(setq endPt (list (car elbowPt) (- (cadr elbowPt) 4.5)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 elbowPt) (cons 11 endPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
(setq zigzagStarts (append zigzagStarts (list nil)))
|
||||||
|
)
|
||||||
|
(progn
|
||||||
|
(setq zigzagStart (list (+ x 7.3656) (+ y 3.7852)))
|
||||||
|
(setq elbowPt (list (+ x 7.8585) (+ y 5.1262)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 zigzagStart) (cons 11 elbowPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
(setq startPt elbowPt)
|
||||||
|
(setq endPt (list (car startPt) (+ (cadr startPt) 1.3144)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 startPt) (cons 11 endPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
(setq zigzagStarts (append zigzagStarts (list endPt)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(setq i (1+ i))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Combined loop
|
||||||
|
(setq i 0)
|
||||||
|
(repeat rowCount
|
||||||
|
(setq j (* i colCount))
|
||||||
|
(repeat (- colCount 1)
|
||||||
|
(if (< (1+ j) dpmCount)
|
||||||
|
(progn
|
||||||
|
(setq pt1 (nth j zigzagStarts))
|
||||||
|
(setq pt2 (nth (1+ j) secondCableEnds))
|
||||||
|
(if (and pt1 pt2 (listp pt1) (listp pt2))
|
||||||
|
(progn
|
||||||
|
(entmakex
|
||||||
|
(list
|
||||||
|
'(0 . "LINE")
|
||||||
|
(cons 10 pt1)
|
||||||
|
(cons 11 pt2)
|
||||||
|
'(62 . 256)
|
||||||
|
'(8 . "AS_ENET_CABLE")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(addTextLabel pt1 pt2 (strcat zone "701-CBL" (itoa cableCounter)))
|
||||||
|
(setq cableCounter (1+ cableCounter))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(setq j (1+ j))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Connect last in row to first in next row
|
||||||
|
(setq lastInRowIndex (- (* (1+ i) colCount) 1))
|
||||||
|
(setq firstInNextRowIndex (* (1+ i) colCount))
|
||||||
|
|
||||||
|
(if (and (< lastInRowIndex dpmCount) (< firstInNextRowIndex dpmCount))
|
||||||
|
(progn
|
||||||
|
(setq row i)
|
||||||
|
(setq col (- colCount 1))
|
||||||
|
(setq x (+ (* col spacingX) dpmOffsetX))
|
||||||
|
(setq y (* -1 row spacingY))
|
||||||
|
(setq downwardEndPt (list (- (+ x 7.2677) 0.2) (- (- (+ y 3.6094) 0.2) 4.5)))
|
||||||
|
|
||||||
|
(setq nextRow (1+ i))
|
||||||
|
(setq nextCol 0)
|
||||||
|
(setq nextX (+ (* nextCol spacingX) dpmOffsetX))
|
||||||
|
(setq nextY (* -1 nextRow spacingY))
|
||||||
|
(setq nextBlockFirstLineEnd (list (+ nextX 7.3656) (+ (+ nextY 4.4406) 2.0)))
|
||||||
|
|
||||||
|
(setq horizontalEndPt (list (car nextBlockFirstLineEnd) (cadr downwardEndPt)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 downwardEndPt) (cons 11 horizontalEndPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
(addTextLabel downwardEndPt horizontalEndPt (strcat zone "701-CBL" (itoa cableCounter)))
|
||||||
|
(setq cableCounter (1+ cableCounter))
|
||||||
|
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 horizontalEndPt) (cons 11 nextBlockFirstLineEnd) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(setq i (1+ i))
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Connect first block to HDV
|
||||||
|
(if (> dpmCount 0)
|
||||||
|
(progn
|
||||||
|
(setq firstX (+ (* 0 spacingX) dpmOffsetX))
|
||||||
|
(setq firstY (* -1 0 spacingY))
|
||||||
|
(setq firstBlockEnd (list (+ firstX 7.3656) (+ (+ firstY 4.4406) 2.0)))
|
||||||
|
(setq hdvEndPt (list (+ 0 1.4) (- hdvY 4.4)))
|
||||||
|
|
||||||
|
(setq horizontalEnd (list (+ (car hdvEndPt) 0.5) (cadr firstBlockEnd)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 firstBlockEnd) (cons 11 horizontalEnd) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
;; This is intentionally first cable: label as first
|
||||||
|
(addTextLabel firstBlockEnd horizontalEnd (strcat zone "701-CBL1"))
|
||||||
|
|
||||||
|
(setq verticalEnd (list (car horizontalEnd) (+ (cadr hdvEndPt) 0.5)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 horizontalEnd) (cons 11 verticalEnd) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 verticalEnd) (cons 11 hdvEndPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; Connect last block to HDV
|
||||||
|
(if (> dpmCount 0)
|
||||||
|
(progn
|
||||||
|
(setq lastBlockIndex (- dpmCount 1))
|
||||||
|
(setq lastRow (fix (/ lastBlockIndex colCount)))
|
||||||
|
(setq lastCol (rem lastBlockIndex colCount))
|
||||||
|
(setq lastX (+ (* lastCol spacingX) dpmOffsetX))
|
||||||
|
(setq lastY (* -1 lastRow spacingY))
|
||||||
|
|
||||||
|
(setq hdvLastEndPt (list (+ 0 1.1) (- hdvY 5.83)))
|
||||||
|
|
||||||
|
(if (= rowCount 1)
|
||||||
|
(progn
|
||||||
|
(setq startPt (list (- (+ lastX 7.2677) 0.2) (- (+ lastY 3.6094) 0.2)))
|
||||||
|
(setq extendedVerticalEnd (list (car startPt) (- (cadr hdvLastEndPt) 1.5)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 startPt) (cons 11 extendedVerticalEnd) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
;; Reduce final horizontal length by 1 unit
|
||||||
|
(setq horizontalEnd (list (car hdvLastEndPt) (cadr extendedVerticalEnd)))
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 extendedVerticalEnd) (cons 11 horizontalEnd) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
;; Single-row: last cable label (uses cableCounter)
|
||||||
|
(addTextLabel extendedVerticalEnd horizontalEnd (strcat zone "701-CBL" (itoa cableCounter)))
|
||||||
|
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 horizontalEnd) (cons 11 hdvLastEndPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
)
|
||||||
|
(progn
|
||||||
|
;; Multiple rows:
|
||||||
|
(setq lastBlockVerticalEnd (list (- (+ lastX 7.2677) 0.2) (- (- (+ lastY 3.6094) 0.2) 4.5)))
|
||||||
|
|
||||||
|
(setq horizontalEnd (list (car hdvLastEndPt) (cadr lastBlockVerticalEnd)))
|
||||||
|
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 lastBlockVerticalEnd) (cons 11 horizontalEnd) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
|
||||||
|
(addTextLabel lastBlockVerticalEnd horizontalEnd (strcat zone "701-CBL" (itoa cableCounter)))
|
||||||
|
|
||||||
|
(entmakex (list '(0 . "LINE") (cons 10 horizontalEnd) (cons 11 hdvLastEndPt) '(62 . 256) '(8 . "AS_ENET_CABLE")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(princ)
|
||||||
|
)
|
||||||
BIN
AutoCAD/MCM_Installer_drawing/MCM02 1.bak
Normal file
BIN
AutoCAD/MCM_Installer_drawing/MCM02 1.bak
Normal file
Binary file not shown.
BIN
AutoCAD/MCM_Installer_drawing/MCM_Installer_drawing.cdc
Normal file
BIN
AutoCAD/MCM_Installer_drawing/MCM_Installer_drawing.cdc
Normal file
Binary file not shown.
77
AutoCAD/MCM_Installer_drawing/SPLITTER_LOGIC.md
Normal file
77
AutoCAD/MCM_Installer_drawing/SPLITTER_LOGIC.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Splitter Detection Logic in IO.lsp
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
The script decides whether to create a **single cable** or **two cables + splitter** based on the DESCA values in each pair.
|
||||||
|
|
||||||
|
## Decision Flow
|
||||||
|
|
||||||
|
### Step 1: Check if both DESCA values are filled
|
||||||
|
```
|
||||||
|
If val1 = "" OR val2 = ""
|
||||||
|
→ Go to Case 2 (single value handling)
|
||||||
|
Else (both val1 and val2 have values)
|
||||||
|
→ Go to Case 1 (pair handling)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Case 1 - Both values filled (Pair handling)
|
||||||
|
|
||||||
|
#### For FIO/FIOH blocks (special handling at lines 318-427):
|
||||||
|
**SPLITTER CREATED** if:
|
||||||
|
- Both values contain "TPE" AND they're different names (e.g., TPE2 vs TPE3)
|
||||||
|
- One contains "RCV" and other contains "SND" (or vice versa)
|
||||||
|
|
||||||
|
**SINGLE CABLE** if:
|
||||||
|
- None of the splitter conditions above match → default to single cable
|
||||||
|
|
||||||
|
#### For all other blocks (VFD, SIO, etc. at lines 432-445):
|
||||||
|
**SINGLE CABLE** if values match any of these patterns:
|
||||||
|
- Both contain "SEL"
|
||||||
|
- Both contain "DPM"
|
||||||
|
- Both contain "VFD" AND both contain "STO"
|
||||||
|
- One contains "_PB" and other contains "_PB_LT" (pushbutton + light)
|
||||||
|
- Both contain "BCN" (beacon stack)
|
||||||
|
- Both contain "EPC"
|
||||||
|
- Both contain "SSP"
|
||||||
|
|
||||||
|
**TWO CABLES + SPLITTER** if:
|
||||||
|
- None of the single-cable conditions match → default to splitter
|
||||||
|
|
||||||
|
## Summary Table
|
||||||
|
|
||||||
|
| DESCA1 Pattern | DESCA2 Pattern | Result |
|
||||||
|
|----------------|----------------|--------|
|
||||||
|
| `*_SEL*` | `*_SEL*` | ✅ Single cable |
|
||||||
|
| `*_DPM*` | `*_DPM*` | ✅ Single cable |
|
||||||
|
| `*VFD*STO*` | `*VFD*STO*` | ✅ Single cable |
|
||||||
|
| `*_PB*` | `*_PB_LT*` | ✅ Single cable |
|
||||||
|
| `*_PB_LT*` | `*_PB*` | ✅ Single cable |
|
||||||
|
| `*BCN*` | `*BCN*` | ✅ Single cable |
|
||||||
|
| `*EPC*` | `*EPC*` | ✅ Single cable |
|
||||||
|
| `*SSP*` | `*SSP*` | ✅ Single cable |
|
||||||
|
| `*TPE*` (different) | `*TPE*` (different) | ⚠️ **SPLITTER** (FIO only) |
|
||||||
|
| `*RCV*` | `*SND*` | ⚠️ **SPLITTER** (FIO only) |
|
||||||
|
| `*SND*` | `*RCV*` | ⚠️ **SPLITTER** (FIO only) |
|
||||||
|
| Anything else | Anything else | ⚠️ **SPLITTER** |
|
||||||
|
|
||||||
|
## Key Code Locations
|
||||||
|
|
||||||
|
- **Lines 318-332**: FIO/FIOH splitter conditions (TPE pairs, RCV/SND pairs)
|
||||||
|
- **Lines 432-445**: All other blocks - single cable conditions
|
||||||
|
- **Lines 505-519**: Default ELSE branch - creates splitter for unmatched pairs
|
||||||
|
|
||||||
|
## Example Scenarios
|
||||||
|
|
||||||
|
1. **Beacon stack** (`BYDC_6_BCN1_A` + `BYDC_6_BCN1_H`):
|
||||||
|
- Both contain "BCN" → ✅ Single cable (line 443)
|
||||||
|
|
||||||
|
2. **Pushbutton + light** (`*_PB` + `*_PB_LT`):
|
||||||
|
- Pattern match → ✅ Single cable (lines 440-441)
|
||||||
|
|
||||||
|
3. **TPE pairs** (`*TPE1` + `*TPE2`):
|
||||||
|
- For FIO blocks → ⚠️ Splitter (lines 322-324)
|
||||||
|
- For other blocks → ⚠️ Splitter (default)
|
||||||
|
|
||||||
|
4. **Two different devices** (e.g., `*DEVICE1` + `*DEVICE2`):
|
||||||
|
- No pattern match → ⚠️ Splitter (default)
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,498 @@
|
|||||||
|
TAGNAME,ADDR,TERM,TERMDESC,DESCA,DESCB,DESCC,DESCD,DESCE,INST,LOC
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X3_0,X3_0,,PDP01_CB1,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X3_1,X3_1,,PDP01_CB2,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X2_0,X2_0,,PDP01_CB3,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X2_1,X2_1,,PDP01_CB4,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X1_0,X1_0,,PDP01_CB5,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X1_1,X1_1,,SPARE,,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X0_0,X0_0,,PDP01_CB6,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X0_1,X0_1,,SPARE,,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X7_0,X7_0,,PDP01_CB7,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X7_1,X7_1,,PDP01_CB8,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X6_0,X6_0,,PDP01_CB9,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X6_1,X6_1,,PDP01_CB10,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X5_0,X5_0,,PDP01_PWM1,PHASE MONITOR,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X5_1,X5_1,,SPARE,,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X4_0,X4_0,,PDP01_FIOH1,I/O LINK HUB,,,,,
|
||||||
|
PDP01_FIOM1,PDP01_FIOM1_X4_1,X4_1,,SPARE,,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C7_A,C7_A,,PDP01_CB11,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C7_B,C7_B,,PDP01_CB12,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C5_A,C5_A,,PDP01_CB13,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C5_B,C5_B,,PDP01_CB14,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C3_A,C3_A,,PDP01_CB15,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C3_B,C3_B,,PDP01_CB16,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C1_A,C1_A,,PDP01_CB17,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C1_B,C1_B,,PDP01_CB18,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C8_A,C8_A,,PDP01_CB19,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C8_B,C8_B,,PDP01_CB20,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C6_A,C6_A,,PDP01_CB21,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C6_B,C6_B,,PDP01_CB22,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C4_A,C4_A,,PDP01_CB23,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C4_B,C4_B,,PDP01_CB24,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C2_A,C2_A,,PDP01_CB25,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
PDP01_FIOH1,PDP01_FIOH1_C2_B,C2_B,,PDP01_CB26,CIRCUIT BREAKER MONITORING,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_I0,I0,,UL27_10_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_I2,I2,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_IO0,IO0,,UL29_8_S2_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_IO1,IO1,,UL29_8_S2_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_10_VFD,UL27_10_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_I0,I0,,UL27_2_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_I2,I2,,UL27_2_TPE2,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_IO0,IO0,,UL27_2_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_SI0,SI0,,UL27_2_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_SI1,SI1,,UL27_2_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_SI2,SI2,,UL27_2_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_SI3,SI3,,UL27_2_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_2_VFD,UL27_2_VFD_SO0,SO0,,UL27_2_SR1_STO1,SAFETY TORQUE OFF,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X3_0,X3_0,,UL27_2_SS1_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X3_1,X3_1,,UL27_2_SS1_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X2_0,X2_0,,UL27_2_SS1_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X2_1,X2_1,,SPARE,,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X1_0,X1_0,,UL27_2_SS2_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X1_1,X1_1,,UL27_2_SS2_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X0_0,X0_0,,UL27_2_SS2_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X0_1,X0_1,,SPARE,,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X7_0,X7_0,,UL27_2_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X7_1,X7_1,,SPARE,,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X6_0,X6_0,,UL27_2_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X6_1,X6_1,,SPARE,,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X5_0,X5_0,,UL27_2_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X5_1,X5_1,,SPARE,,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X4_0,X4_0,,SPARE,,,,,,
|
||||||
|
UL27_2_FIOM1,UL27_2_FIOM1_X4_1,X4_1,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_I0,I0,,UL27_3_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_I2,I2,,UL27_3_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_IO0,IO0,,UL27_3_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_3_VFD,UL27_3_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_I0,I0,,UL27_4_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_I2,I2,,UL27_4_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_IO0,IO0,,UL27_4_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_4_VFD,UL27_4_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_I0,I0,,UL27_5_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_I2,I2,,UL27_5_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_I3,I3,,UL27_5_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_IO0,IO0,,UL27_5_S2_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_IO1,IO1,,UL27_5_S2_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_SI0,SI0,,UL27_5_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_SI1,SI1,,UL27_5_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_SI2,SI2,,UL27_5_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_SI3,SI3,,UL27_5_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL27_5_VFD,UL27_5_VFD_SO0,SO0,,UL27_5_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_I0,I0,,UL27_6_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_I2,I2,,UL27_6_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_I3,I3,,UL27_6_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_IO0,IO0,,UL27_5_S1_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_IO1,IO1,,UL27_5_S1_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_6_VFD,UL27_6_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_I0,I0,,UL27_7_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_I2,I2,,UL27_7_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_I3,I3,,UL27_7_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_IO0,IO0,,UL27_5_BCN1_A,AMBER BEACON LIGHT,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_IO1,IO1,,UL27_5_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_7_VFD,UL27_7_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_I0,I0,,UL27_8_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_I2,I2,,UL27_8_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_IO0,IO0,,UL27_8_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_8_VFD,UL27_8_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_I0,I0,,UL27_9_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_I2,I2,,UL27_9_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_IO0,IO0,,UL27_9_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL27_9_VFD,UL27_9_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_I0,I0,,UL28_2_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_I2,I2,,UL28_2_TPE2,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_IO0,IO0,,UL28_2_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_SI0,SI0,,UL28_2_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_SI1,SI1,,UL28_2_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_SI2,SI2,,UL28_2_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_SI3,SI3,,UL28_2_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_2_VFD,UL28_2_VFD_SO0,SO0,,UL28_2_SR1_STO1,SAFETY TORQUE OFF,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X3_0,X3_0,,UL28_2_SS1_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X3_1,X3_1,,UL28_2_SS1_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X2_0,X2_0,,UL28_2_SS1_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X2_1,X2_1,,SPARE,,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X1_0,X1_0,,UL28_2_SS2_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X1_1,X1_1,,UL28_2_SS2_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X0_0,X0_0,,UL28_2_SS2_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X0_1,X0_1,,SPARE,,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X7_0,X7_0,,UL28_2_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X7_1,X7_1,,SPARE,,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X6_0,X6_0,,UL28_2_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X6_1,X6_1,,SPARE,,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X5_0,X5_0,,UL28_2_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X5_1,X5_1,,SPARE,,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X4_0,X4_0,,SPARE,,,,,,
|
||||||
|
UL28_2_FIOM1,UL28_2_FIOM1_X4_1,X4_1,,SPARE,,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_I0,I0,,UL28_3_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_I2,I2,,UL28_3_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_I3,I3,,UL28_3_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_IO0,IO0,,UL28_3_S2_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_IO1,IO1,,UL28_3_S2_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_SI0,SI0,,UL28_3_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_SI1,SI1,,UL28_3_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_SI2,SI2,,UL28_3_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_SI3,SI3,,UL28_3_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL28_3_VFD,UL28_3_VFD_SO0,SO0,,UL28_3_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_I0,I0,,UL28_4_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_I2,I2,,UL28_4_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_I3,I3,,UL28_4_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_IO0,IO0,,UL28_3_S1_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_IO1,IO1,,UL28_3_S1_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL28_4_VFD,UL28_4_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_I0,I0,,UL28_5_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_I2,I2,,UL28_5_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_I3,I3,,UL28_5_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_IO0,IO0,,UL28_3_BCN1_A,AMBER BEACON LIGHT,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_IO1,IO1,,UL28_3_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL28_5_VFD,UL28_5_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_I0,I0,,UL28_6_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_I2,I2,,UL28_6_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_IO0,IO0,,UL28_6_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL28_6_VFD,UL28_6_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_I0,I0,,UL28_7_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_I2,I2,,UL28_7_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_IO0,IO0,,UL28_7_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL28_7_VFD,UL28_7_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_I0,I0,,UL28_8_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_I2,I2,,UL28_8_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_IO0,IO0,,UL28_8_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL28_8_VFD,UL28_8_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_I0,I0,,UL28_9_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_I2,I2,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_IO0,IO0,,UL29_8_BCN1_A,AMBER BEACON LIGHT,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_IO1,IO1,,UL29_8_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL28_9_VFD,UL28_9_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_I0,I0,,UL29_1_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_I2,I2,,UL29_1_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_IO0,IO0,,UL29_1_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_SI0,SI0,,UL29_1_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_SI1,SI1,,UL29_1_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_SI2,SI2,,UL29_1_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_SI3,SI3,,UL29_1_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_1_VFD,UL29_1_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X3_0,X3_0,,UL29_1_SS1_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X3_1,X3_1,,UL29_1_SS1_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X2_0,X2_0,,UL29_1_SS1_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X2_1,X2_1,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X1_0,X1_0,,UL29_1_SS2_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X1_1,X1_1,,UL29_1_SS2_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X0_0,X0_0,,UL29_1_SS2_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X0_1,X0_1,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X7_0,X7_0,,UL29_1_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X7_1,X7_1,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X6_0,X6_0,,UL29_1_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X6_1,X6_1,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X5_0,X5_0,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X5_1,X5_1,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X4_0,X4_0,,SPARE,,,,,,
|
||||||
|
UL29_1_FIOM1,UL29_1_FIOM1_X4_1,X4_1,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_I0,I0,,UL29_10_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_I2,I2,,UL29_10_TPE2,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_IO0,IO0,,UL29_10_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_10_VFD,UL29_10_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_I0,I0,,UL29_11_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_I2,I2,,UL29_11_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_IO0,IO0,,UL29_11_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_11_VFD,UL29_11_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_I0,I0,,UL29_12_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_I2,I2,,UL29_12_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_IO0,IO0,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_12_VFD,UL29_12_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_I0,I0,,UL29_13_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_I2,I2,,UL29_13_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_I3,I3,,UL29_13_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_IO0,IO0,,UL29_13_BCN1_H,HORN BEACON,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_13_VFD,UL29_13_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_I0,I0,,UL29_15_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_I2,I2,,UL29_15_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_IO0,IO0,,UL29_15_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_SI0,SI0,,UL29_15_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_SI1,SI1,,UL29_15_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_SI2,SI2,,UL29_15_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_SI3,SI3,,UL29_15_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_15_VFD,UL29_15_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_I0,I0,,UL29_16_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_I2,I2,,UL29_16_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_IO0,IO0,,UL29_16_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_16_VFD,UL29_16_VFD_SO0,SO0,,UL29_17_SR1_STO1,SAFETY TORQUE OFF,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X3_0,X3_0,,UL29_15_SS1_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X3_1,X3_1,,UL29_15_SS1_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X2_0,X2_0,,UL29_15_SS1_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X2_1,X2_1,,SPARE,,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X1_0,X1_0,,UL29_15_SS2_SPB,SS STATION START PUSHBUTTON,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X1_1,X1_1,,UL29_15_SS2_SPB_LT,SS STATION START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X0_0,X0_0,,UL29_15_SS2_STPB,SS STATION STOP PUSHBUTTON,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X0_1,X0_1,,SPARE,,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X7_0,X7_0,,UL29_15_BCN1_A,AMBER BEACON LIGHT,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X7_1,X7_1,,UL29_15_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X6_0,X6_0,,UL29_15_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X6_1,X6_1,,SPARE,,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X5_0,X5_0,,SPARE,,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X5_1,X5_1,,SPARE,,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X4_0,X4_0,,SPARE,,,,,,
|
||||||
|
UL29_17_FIOM1,UL29_17_FIOM1_X4_1,X4_1,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_I0,I0,,UL29_18_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_I2,I2,,UL29_18_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_I3,I3,,UL29_17_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_IO0,IO0,,UL29_18_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_18_VFD,UL29_18_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_I0,I0,,UL29_2_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_I2,I2,,UL29_2_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_IO0,IO0,,UL29_2_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_2_VFD,UL29_2_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_I0,I0,,UL29_3_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_I2,I2,,UL29_3_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_IO0,IO0,,UL29_3_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_3_VFD,UL29_3_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_I0,I0,,UL29_30_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_I2,I2,,UL29_30_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_IO0,IO0,,UL29_30_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_SI0,SI0,,UL29_30_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_SI1,SI1,,UL29_30_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_SI2,SI2,,UL29_30_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_SI3,SI3,,UL29_30_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_30_VFD,UL29_30_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X3_0,X3_0,,UL29_30_S1_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X3_1,X3_1,,UL29_30_S1_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X2_0,X2_0,,UL29_30_S2_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X2_1,X2_1,,UL29_30_S2_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X1_0,X1_0,,UL29_30_BCN1_B,BLUE BEACON LIGHT,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X1_1,X1_1,,UL29_30_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X0_0,X0_0,,UL29_30_BCN2_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X0_1,X0_1,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X7_0,X7_0,,UL29_14_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X7_1,X7_1,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X6_0,X6_0,,UL29_31_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X6_1,X6_1,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X5_0,X5_0,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X5_1,X5_1,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X4_0,X4_0,,SPARE,,,,,,
|
||||||
|
UL29_30_FIOM1,UL29_30_FIOM1_X4_1,X4_1,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_I0,I0,,UL29_4_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_I2,I2,,UL29_4_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_IO0,IO0,,UL29_4_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_4_VFD,UL29_4_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_I0,I0,,UL29_5_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_I2,I2,,UL29_5_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_IO0,IO0,,UL29_5_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_5_VFD,UL29_5_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_I0,I0,,UL29_6_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_I2,I2,,UL29_6_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_IO0,IO0,,UL29_6_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_6_VFD,UL29_6_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_I0,I0,,UL29_7_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_I2,I2,,UL29_7_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_I3,I3,,UL29_7_ENSH1,SHAFT ENCODER,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_IO0,IO0,,UL29_8_S1_PB,START PUSHBUTTON,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_IO1,IO1,,UL29_8_S1_PB_LT,START PUSHBUTTON LIGHT,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_SI0,SI0,,UL29_8_EPC1,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_SI1,SI1,,UL29_8_EPC1_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_7_VFD,UL29_7_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_I0,I0,,UL29_8_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_I2,I2,,UL29_8_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_I3,I3,,UL29_8_TPE2,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_IO0,IO0,,UL29_8_ENW1,WHEEL ENCODER,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_SI0,SI0,,UL29_8_EPC2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_SI1,SI1,,UL29_8_EPC2_2,E-STOP PULLCORD,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_8_VFD,UL29_8_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_I0,I0,,UL29_9A_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_I2,I2,,UL29_10_TPE1,TRACKING PHOTOEYE,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_IO0,IO0,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_9A_VFD,UL29_9A_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_I0,I0,,UL29_9B_VFD_DISC,DISCONNECT AUX,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_I1,I1,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_I2,I2,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_I3,I3,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_IO0,IO0,,UL29_8_BCN1_R,RED BEACON LIGHT,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_IO1,IO1,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_SI0,SI0,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_SI1,SI1,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_SI2,SI2,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_SI3,SI3,,SPARE,,,,,,
|
||||||
|
UL29_9B_VFD,UL29_9B_VFD_SO0,SO0,,SPARE,,,,,,
|
||||||
|
Binary file not shown.
147
AutoCAD/MCM_Installer_drawing/Tables/DPM/MCM14_Diagram.csv
Normal file
147
AutoCAD/MCM_Installer_drawing/Tables/DPM/MCM14_Diagram.csv
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
Bypass,MCM15,,,,,,,,
|
||||||
|
SLOT0,1756-L83ES,IP,RING,,STAR,,,,
|
||||||
|
SLOT1,1756-N2,,DPM,IP,NAME,Part Number,IP,DPM PORT,ZONE
|
||||||
|
SLOT2,1756-EN4TR,11.200.0.15,BYAB_12_DPM1,11.200.1.2,BYCA_2_FIOM1,Murr 54631,11.200.1.20,BYAB_12_DPM1_P5,MCM15
|
||||||
|
SLOT3,1756-N2,,,,BYCD_2_FIOM1,Murr 54631,11.200.1.21,BYAB_12_DPM1_P6,
|
||||||
|
SLOT4,1756-N2,,,,BYCD_3_FIOM1,Murr 54631,11.200.1.22,BYAB_12_DPM1_P7,
|
||||||
|
SLOT5,1756-IB16,,,,BYCA_1_VFD,35S-6D3-P101,11.200.1.23,BYAB_12_DPM1_P8,
|
||||||
|
SLOT6,1756-OB16E,,,,BYCB_1_VFD,35S-6D3-P101,11.200.1.24,BYAB_12_DPM1_P9,
|
||||||
|
SLOT7,1756-IB16S,,,,BYCD_1_VFD,35S-6D3-P101,11.200.1.25,BYAB_12_DPM1_P10,
|
||||||
|
SLOT8,1756-N2,,,,BYCA_2_VFD,35S-6D3-P101,11.200.1.26,BYAB_12_DPM1_P11,
|
||||||
|
SLOT9,1756-N2,,,,BYCB_2_VFD,35S-6D3-P101,11.200.1.27,BYAB_12_DPM1_P12,
|
||||||
|
,,,,,BYCD_2_VFD,35S-6D2-P101,11.200.1.28,BYAB_12_DPM1_P13,
|
||||||
|
,,,,,BYCA_3_VFD,35S-6D3-P101,11.200.1.29,BYAB_12_DPM1_P14,
|
||||||
|
DPM COUND SLOT 3,6,,,,BYCB_3_VFD,35S-6D3-P101,11.200.1.30,BYAB_12_DPM1_P15,
|
||||||
|
FIO COUND SLOT 3,22,,,,BYCD_3_VFD,35S-6D4-P111,11.200.1.31,BYAB_12_DPM1_P16,
|
||||||
|
,,,,,BYCA_4_VFD,35S-6D2-P101,11.200.1.32,BYAB_12_DPM1_P17,
|
||||||
|
,,,,,BYCB_4_VFD,35S-6D3-P101,11.200.1.33,BYAB_12_DPM1_P18,
|
||||||
|
,,,,,BYCA_5_VFD,35S-6D4-P111,11.200.1.34,BYAB_12_DPM1_P19,
|
||||||
|
,,,,,BYCD_7_VFD,35S-6D3-P101,11.200.1.35,BYAB_12_DPM1_P20,
|
||||||
|
,,,,,BYCD_8_VFD,35S-6D3-P101,11.200.1.36,BYAB_12_DPM1_P21,
|
||||||
|
,,,,,BYCD_9_VFD,35S-6D2-P101,11.200.1.37,BYAB_12_DPM1_P22,
|
||||||
|
,,,,,BYAB_10_VFD,35S-6D2-P101,11.200.1.38,BYAB_12_DPM1_P23,
|
||||||
|
,,,,,BYBD_10_VFD,35S-6D4-P111,11.200.1.39,BYAB_12_DPM1_P24,
|
||||||
|
,,,,,BYCD_10_VFD,35S-6D2-P101,11.200.1.40,BYAB_12_DPM1_P25,
|
||||||
|
,,,,,BYAB_11_VFD,35S-6D4-P111,11.200.1.41,BYAB_12_DPM1_P26,
|
||||||
|
,,,,,BYBC_14_VFD,35S-6D3-P101,11.200.1.42,BYAB_12_DPM1_P27,
|
||||||
|
,,,,,SPARE, ,11.200.1.43,BYAB_12_DPM1_P28,
|
||||||
|
,,,BYAC_10_DPM1,11.200.1.3,BYAC_10_FIOM1,Murr 54631,11.200.1.44,BYAC_10_DPM1_P5,
|
||||||
|
,,,,,BYAC_14_FIOM1,Murr 54631,11.200.1.45,BYAC_10_DPM1_P6,
|
||||||
|
,,,,,BYBC_16_FIOM1,Murr 54631,11.200.1.46,BYAC_10_DPM1_P7,
|
||||||
|
,,,,,BYDC_20_FIOM1,Murr 54631,11.200.1.47,BYAC_10_DPM1_P8,
|
||||||
|
,,,,,BYCA_6_VFD,35S-6D4-P111,11.200.1.48,BYAC_10_DPM1_P9,
|
||||||
|
,,,,,BYCA_7_VFD,35S-6D4-P111,11.200.1.49,BYAC_10_DPM1_P10,
|
||||||
|
,,,,,BYAC_10_VFD,35S-6D5-P111,11.200.1.50,BYAC_10_DPM1_P11,
|
||||||
|
,,,,,BYAC_11_VFD,35S-6D3-P101,11.200.1.51,BYAC_10_DPM1_P12,
|
||||||
|
,,,,,BYBA_11_VFD,35S-6D3-P101,11.200.1.52,BYAC_10_DPM1_P13,
|
||||||
|
,,,,,BYAC_12_VFD,35S-6D3-P101,11.200.1.53,BYAC_10_DPM1_P14,
|
||||||
|
,,,,,BYBA_12_VFD,35S-6D3-P101,11.200.1.54,BYAC_10_DPM1_P15,
|
||||||
|
,,,,,BYAC_13_VFD,35S-6D3-P101,11.200.1.55,BYAC_10_DPM1_P16,
|
||||||
|
,,,,,BYBA_13_VFD,35S-6D5-P111,11.200.1.56,BYAC_10_DPM1_P17,
|
||||||
|
,,,,,BYAC_14_VFD,35S-6D2-P101,11.200.1.57,BYAC_10_DPM1_P18,
|
||||||
|
,,,,,BYBC_15_VFD,35S-6D2-P101,11.200.1.58,BYAC_10_DPM1_P19,
|
||||||
|
,,,,,BYBC_16_VFD,35S-6D3-P101,11.200.1.59,BYAC_10_DPM1_P20,
|
||||||
|
,,,,,BYDC_16_VFD,35S-6D5-P111,11.200.1.60,BYAC_10_DPM1_P21,
|
||||||
|
,,,,,BYDC_17_VFD,35S-6D3-P101,11.200.1.61,BYAC_10_DPM1_P22,
|
||||||
|
,,,,,BYDC_18_VFD,35S-6D3-P101,11.200.1.62,BYAC_10_DPM1_P23,
|
||||||
|
,,,,,BYDC_19_VFD,35S-6D3-P101,11.200.1.63,BYAC_10_DPM1_P24,
|
||||||
|
,,,,,BYDC_20_VFD,35S-6D2-P101,11.200.1.64,BYAC_10_DPM1_P25,
|
||||||
|
,,,,,SPARE, ,11.200.1.65,BYAC_10_DPM1_P26,
|
||||||
|
,,,,,SPARE, ,11.200.1.66,BYAC_10_DPM1_P27,
|
||||||
|
,,,,,SPARE, ,11.200.1.67,BYAC_10_DPM1_P28,
|
||||||
|
,,,BYCB_6_DPM1,11.200.1.4,BYBA_9_FIOM1,Murr 54631,11.200.1.68,BYCB_6_DPM1_P5,
|
||||||
|
,,,,,BYBD_9_FIOM1,Murr 54631,11.200.1.69,BYCB_6_DPM1_P6,
|
||||||
|
,,,,,BYAB_12_FIOM1,Murr 54631,11.200.1.70,BYCB_6_DPM1_P7,
|
||||||
|
,,,,,PDP19_FIOM1,Murr 54631,11.200.1.71,BYCB_6_DPM1_P8,
|
||||||
|
,,,,,BYCD_4_VFD,35S-6D4-P111,11.200.1.72,BYCB_6_DPM1_P9,
|
||||||
|
,,,,,BYCB_5_VFD,35S-6D2-P101,11.200.1.73,BYCB_6_DPM1_P10,
|
||||||
|
,,,,,BYCD_5_VFD,35S-6D2-P101,11.200.1.74,BYCB_6_DPM1_P11,
|
||||||
|
,,,,,BYCB_6_VFD,35S-6D3-P101,11.200.1.75,BYCB_6_DPM1_P12,
|
||||||
|
,,,,,BYCD_6_VFD,35S-6D4-P111,11.200.1.76,BYCB_6_DPM1_P13,
|
||||||
|
,,,,,BYCB_7_VFD,35S-6D5-P111,11.200.1.77,BYCB_6_DPM1_P14,
|
||||||
|
,,,,,BYBA_8_VFD,35S-6D4-P111,11.200.1.78,BYCB_6_DPM1_P15,
|
||||||
|
,,,,,BYBD_8_VFD,35S-6D4-P111,11.200.1.79,BYCB_6_DPM1_P16,
|
||||||
|
,,,,,BYBA_9_VFD,35S-6D4-P111,11.200.1.80,BYCB_6_DPM1_P17,
|
||||||
|
,,,,,BYBD_9_VFD,35S-6D4-P111,11.200.1.81,BYCB_6_DPM1_P18,
|
||||||
|
,,,,,BYBA_10_VFD,35S-6D3-P101,11.200.1.82,BYCB_6_DPM1_P19,
|
||||||
|
,,,,,BYAB_12_VFD,35S-6D4-P111,11.200.1.83,BYCB_6_DPM1_P20,
|
||||||
|
,,,,,BYBC_12_VFD,35S-6D4-P111,11.200.1.84,BYCB_6_DPM1_P21,
|
||||||
|
,,,,,BYDB_12_VFD,35S-6D5-P111,11.200.1.85,BYCB_6_DPM1_P22,
|
||||||
|
,,,,,BYAB_13_VFD,35S-6D5-P111,11.200.1.86,BYCB_6_DPM1_P23,
|
||||||
|
,,,,,BYBC_13_VFD,35S-6D3-P101,11.200.1.87,BYCB_6_DPM1_P24,
|
||||||
|
,,,,,BYDB_13_VFD,35S-6D5-P111,11.200.1.88,BYCB_6_DPM1_P25,
|
||||||
|
,,,,,SPARE, ,11.200.1.89,BYCB_6_DPM1_P26,
|
||||||
|
,,,,,SPARE, ,11.200.1.90,BYCB_6_DPM1_P27,
|
||||||
|
,,,,,SPARE, ,11.200.1.91,BYCB_6_DPM1_P28,
|
||||||
|
,,,BYCB_9_DPM1,11.200.1.5,BYBA_2_FIOM1,Murr 54631,11.200.1.92,BYCB_9_DPM1_P5,
|
||||||
|
,,,,,BYBD_2_FIOM1,Murr 54631,11.200.1.93,BYCB_9_DPM1_P6,
|
||||||
|
,,,,,BYCB_16_FIOM1,Murr 54631,11.200.1.94,BYCB_9_DPM1_P7,
|
||||||
|
,,,,,PDP20_FIOM1,Murr 54631,11.200.1.95,BYCB_9_DPM1_P8,
|
||||||
|
,,,,,BYBA_1_VFD,35S-6D3-P101,11.200.1.96,BYCB_9_DPM1_P9,
|
||||||
|
,,,,,BYBA_2_VFD,35S-6D3-P101,11.200.1.97,BYCB_9_DPM1_P10,
|
||||||
|
,,,,,BYBA_3_VFD,35S-6D3-P101,11.200.1.98,BYCB_9_DPM1_P11,
|
||||||
|
,,,,,BYBA_4_VFD,35S-6D3-P101,11.200.1.99,BYCB_9_DPM1_P12,
|
||||||
|
,,,,,BYBA_5_VFD,35S-6D2-P101,11.200.1.100,BYCB_9_DPM1_P13,
|
||||||
|
,,,,,BYBC_5_VFD,35S-6D3-P101,11.200.1.101,BYCB_9_DPM1_P14,
|
||||||
|
,,,,,BYBA_6_VFD,35S-6D2-P101,11.200.1.102,BYCB_9_DPM1_P15,
|
||||||
|
,,,,,BYBC_6_VFD,35S-6D2-P101,11.200.1.103,BYCB_9_DPM1_P16,
|
||||||
|
,,,,,BYBC_7_VFD,35S-6D2-P101,11.200.1.104,BYCB_9_DPM1_P17,
|
||||||
|
,,,,,BYCB_9_VFD,35S-6D3-P101,11.200.1.105,BYCB_9_DPM1_P18,
|
||||||
|
,,,,,BYCB_10_VFD,35S-6D3-P101,11.200.1.106,BYCB_9_DPM1_P19,
|
||||||
|
,,,,,BYAB_16_VFD,35S-6D3-P101,11.200.1.107,BYCB_9_DPM1_P20,
|
||||||
|
,,,,,BYDB_16_VFD,35S-6D3-P101,11.200.1.108,BYCB_9_DPM1_P21,
|
||||||
|
,,,,,SPARE, ,11.200.1.109,BYCB_9_DPM1_P22,
|
||||||
|
,,,,,SPARE, ,11.200.1.110,BYCB_9_DPM1_P23,
|
||||||
|
,,,,,SPARE, ,11.200.1.111,BYCB_9_DPM1_P24,
|
||||||
|
,,,,,SPARE, ,11.200.1.112,BYCB_9_DPM1_P25,
|
||||||
|
,,,,,SPARE, ,11.200.1.113,BYCB_9_DPM1_P26,
|
||||||
|
,,,,,SPARE, ,11.200.1.114,BYCB_9_DPM1_P27,
|
||||||
|
,,,,,SPARE, ,11.200.1.115,BYCB_9_DPM1_P28,
|
||||||
|
,,,BYCB_10_DPM1,11.200.1.6,BYBC_10_FIOM1,Murr 54631,11.200.1.116,BYCB_10_DPM1_P5,
|
||||||
|
,,,,,BYAB_15_FIOM1,Murr 54631,11.200.1.117,BYCB_10_DPM1_P6,
|
||||||
|
,,,,,BYDB_15_FIOM1,Murr 54631,11.200.1.118,BYCB_10_DPM1_P7,
|
||||||
|
,,,,,BYBA_7_VFD,35S-6D4-P111,11.200.1.119,BYCB_10_DPM1_P8,
|
||||||
|
,,,,,BYBD_7_VFD,35S-6D4-P111,11.200.1.120,BYCB_10_DPM1_P9,
|
||||||
|
,,,,,BYBC_8_VFD,35S-6D4-P111,11.200.1.121,BYCB_10_DPM1_P10,
|
||||||
|
,,,,,BYCB_8_VFD,35S-6D5-P111,11.200.1.122,BYCB_10_DPM1_P11,
|
||||||
|
,,,,,BYBC_9_VFD,35S-6D3-P101,11.200.1.123,BYCB_10_DPM1_P12,
|
||||||
|
,,,,,BYBC_10_VFD,35S-6D3-P101,11.200.1.124,BYCB_10_DPM1_P13,
|
||||||
|
,,,,,BYBC_11_VFD,35S-6D4-P111,11.200.1.125,BYCB_10_DPM1_P14,
|
||||||
|
,,,,,BYAB_14_VFD,35S-6D5-P111,11.200.1.126,BYCB_10_DPM1_P15,
|
||||||
|
,,,,,BYDB_14_VFD,35S-6D5-P111,11.200.1.127,BYCB_10_DPM1_P16,
|
||||||
|
,,,,,BYAB_15_VFD,35S-6D4-P111,11.200.1.128,BYCB_10_DPM1_P17,
|
||||||
|
,,,,,BYDB_15_VFD,35S-6D3-P101,11.200.1.129,BYCB_10_DPM1_P18,
|
||||||
|
,,,,,SPARE, ,11.200.1.130,BYCB_10_DPM1_P19,
|
||||||
|
,,,,,SPARE, ,11.200.1.131,BYCB_10_DPM1_P20,
|
||||||
|
,,,,,SPARE, ,11.200.1.132,BYCB_10_DPM1_P21,
|
||||||
|
,,,,,SPARE, ,11.200.1.133,BYCB_10_DPM1_P22,
|
||||||
|
,,,,,SPARE, ,11.200.1.134,BYCB_10_DPM1_P23,
|
||||||
|
,,,,,SPARE, ,11.200.1.135,BYCB_10_DPM1_P24,
|
||||||
|
,,,,,SPARE, ,11.200.1.136,BYCB_10_DPM1_P25,
|
||||||
|
,,,,,SPARE, ,11.200.1.137,BYCB_10_DPM1_P26,
|
||||||
|
,,,,,SPARE, ,11.200.1.138,BYCB_10_DPM1_P27,
|
||||||
|
,,,,,SPARE, ,11.200.1.139,BYCB_10_DPM1_P28,
|
||||||
|
,,,BYCB_11_DPM1,11.200.1.7,BYBC_2_FIOM1,Murr 54631,11.200.1.140,BYCB_11_DPM1_P5,
|
||||||
|
,,,,,BYCB_13_FIOM1,Murr 54631,11.200.1.141,BYCB_11_DPM1_P6,
|
||||||
|
,,,,,BYAB_19_FIOM1,Murr 54631,11.200.1.142,BYCB_11_DPM1_P7,
|
||||||
|
,,,,,BYDB_19_FIOM1,Murr 54631,11.200.1.143,BYCB_11_DPM1_P8,
|
||||||
|
,,,,,BYBC_1_VFD,35S-6D3-P101,11.200.1.144,BYCB_11_DPM1_P9,
|
||||||
|
,,,,,BYBD_1_VFD,35S-6D3-P101,11.200.1.145,BYCB_11_DPM1_P10,
|
||||||
|
,,,,,BYBC_2_VFD,35S-6D3-P101,11.200.1.146,BYCB_11_DPM1_P11,
|
||||||
|
,,,,,BYBD_2_VFD,35S-6D3-P101,11.200.1.147,BYCB_11_DPM1_P12,
|
||||||
|
,,,,,BYBC_3_VFD,35S-6D4-P111,11.200.1.148,BYCB_11_DPM1_P13,
|
||||||
|
,,,,,BYBD_3_VFD,35S-6D2-P101,11.200.1.149,BYCB_11_DPM1_P14,
|
||||||
|
,,,,,BYBC_4_VFD,35S-6D4-P111,11.200.1.150,BYCB_11_DPM1_P15,
|
||||||
|
,,,,,BYBD_4_VFD,35S-6D2-P101,11.200.1.151,BYCB_11_DPM1_P16,
|
||||||
|
,,,,,BYBD_5_VFD,35S-6D2-P101,11.200.1.152,BYCB_11_DPM1_P17,
|
||||||
|
,,,,,BYBD_6_VFD,35S-6D3-P101,11.200.1.153,BYCB_11_DPM1_P18,
|
||||||
|
,,,,,BYCB_11_VFD,35S-6D4-P111,11.200.1.154,BYCB_11_DPM1_P19,
|
||||||
|
,,,,,BYCB_12_VFD,35S-6D3-P101,11.200.1.155,BYCB_11_DPM1_P20,
|
||||||
|
,,,,,BYCB_13_VFD,35S-6D2-P101,11.200.1.156,BYCB_11_DPM1_P21,
|
||||||
|
,,,,,BYAB_17_VFD,35S-6D3-P101,11.200.1.157,BYCB_11_DPM1_P22,
|
||||||
|
,,,,,BYDB_17_VFD,35S-6D4-P111,11.200.1.158,BYCB_11_DPM1_P23,
|
||||||
|
,,,,,BYAB_18_VFD,35S-6D3-P101,11.200.1.159,BYCB_11_DPM1_P24,
|
||||||
|
,,,,,BYDB_18_VFD,35S-6D3-P101,11.200.1.160,BYCB_11_DPM1_P25,
|
||||||
|
,,,,,BYAB_19_VFD,35S-6D2-P101,11.200.1.161,BYCB_11_DPM1_P26,
|
||||||
|
,,,,,BYDB_19_VFD,35S-6D2-P101,11.200.1.162,BYCB_11_DPM1_P27,
|
||||||
|
,,,,,SPARE, ,11.200.1.163,BYCB_11_DPM1_P28,
|
||||||
|
396
AutoCAD/MCM_Installer_drawing/Tables/xlsx_to_csv.py
Normal file
396
AutoCAD/MCM_Installer_drawing/Tables/xlsx_to_csv.py
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def sort_key_fiom(term):
|
||||||
|
"""
|
||||||
|
Sort key for FIOM controllers with X format (X0_0, X0_1, etc.)
|
||||||
|
Sort order: X3_0, X3_1, X2_0, X2_1, X1_0, X1_1, X0_0, X0_1,
|
||||||
|
X7_0, X7_1, X6_0, X6_1, X5_0, X5_1, X4_0, X4_1
|
||||||
|
"""
|
||||||
|
if pd.isna(term) or not isinstance(term, str):
|
||||||
|
return (999, 999)
|
||||||
|
|
||||||
|
match = re.match(r'X(\d+)_([01])', term)
|
||||||
|
if not match:
|
||||||
|
return (999, term)
|
||||||
|
|
||||||
|
x_num = int(match.group(1))
|
||||||
|
suffix = int(match.group(2)) # 0 or 1
|
||||||
|
|
||||||
|
# Sort priority: X3->1, X2->2, X1->3, X0->4, X7->5, X6->6, X5->7, X4->8
|
||||||
|
sort_priority = {
|
||||||
|
3: 1, 2: 2, 1: 3, 0: 4, # First group
|
||||||
|
7: 5, 6: 6, 5: 7, 4: 8 # Second group
|
||||||
|
}
|
||||||
|
|
||||||
|
priority = sort_priority.get(x_num, 999)
|
||||||
|
# suffix 0 should come before suffix 1
|
||||||
|
return (priority, suffix)
|
||||||
|
|
||||||
|
def sort_key_fioh(term):
|
||||||
|
"""
|
||||||
|
Sort key for FIOH controllers with C format (C1_A, C1_B, etc.)
|
||||||
|
Sort order: C7_A, C7_B, C5_A, C5_B, C3_A, C3_B, C1_A, C1_B,
|
||||||
|
C8_A, C8_B, C6_A, C6_B, C4_A, C4_B, C2_A, C2_B
|
||||||
|
"""
|
||||||
|
if pd.isna(term) or not isinstance(term, str):
|
||||||
|
return (999, 999)
|
||||||
|
|
||||||
|
match = re.match(r'C(\d+)_([AB])', term)
|
||||||
|
if not match:
|
||||||
|
return (999, term)
|
||||||
|
|
||||||
|
num = int(match.group(1))
|
||||||
|
letter = match.group(2)
|
||||||
|
# Sort priority: C7->1, C5->2, C3->3, C1->4, C8->5, C6->6, C4->7, C2->8
|
||||||
|
sort_priority = {
|
||||||
|
7: 1, 5: 2, 3: 3, 1: 4, # Odd descending first
|
||||||
|
8: 5, 6: 6, 4: 7, 2: 8 # Even descending second
|
||||||
|
}
|
||||||
|
priority = sort_priority.get(num, 999)
|
||||||
|
letter_priority = 0 if letter == 'A' else 1 # A before B
|
||||||
|
return (priority, letter_priority)
|
||||||
|
|
||||||
|
def is_fiom_controller(tagname):
|
||||||
|
"""Check if controller name contains FIOM"""
|
||||||
|
if pd.isna(tagname) or not isinstance(tagname, str):
|
||||||
|
return False
|
||||||
|
return 'FIOM' in str(tagname).upper()
|
||||||
|
|
||||||
|
def is_fioh_controller(tagname):
|
||||||
|
"""Check if controller name contains FIOH"""
|
||||||
|
if pd.isna(tagname) or not isinstance(tagname, str):
|
||||||
|
return False
|
||||||
|
return 'FIOH' in str(tagname).upper()
|
||||||
|
|
||||||
|
def is_vfd_controller(tagname):
|
||||||
|
"""Check if controller name contains VFD"""
|
||||||
|
if pd.isna(tagname) or not isinstance(tagname, str):
|
||||||
|
return False
|
||||||
|
return 'VFD' in str(tagname).upper()
|
||||||
|
|
||||||
|
def get_base_prefix(tagname):
|
||||||
|
"""
|
||||||
|
Extract base prefix from controller name (before the controller type).
|
||||||
|
Examples:
|
||||||
|
PDP17_FIOM1 -> PDP17
|
||||||
|
PDP17_FIOH -> PDP17
|
||||||
|
BYAD_3_VFD -> BYAD_3
|
||||||
|
BYAD_3_FIOM -> BYAD_3
|
||||||
|
"""
|
||||||
|
if pd.isna(tagname) or not isinstance(tagname, str):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
tagname_upper = str(tagname).upper()
|
||||||
|
|
||||||
|
# Try to find controller type markers
|
||||||
|
for controller_type in ['_FIOM', '_FIOH', '_VFD']:
|
||||||
|
if controller_type in tagname_upper:
|
||||||
|
idx = tagname_upper.index(controller_type)
|
||||||
|
return str(tagname)[:idx]
|
||||||
|
|
||||||
|
# If no controller type found, return the whole name
|
||||||
|
return str(tagname)
|
||||||
|
|
||||||
|
def get_column_mapping(xlsx_df):
|
||||||
|
"""
|
||||||
|
Map original XLSX columns to new CSV column names.
|
||||||
|
Attempts to find columns by common name variations.
|
||||||
|
"""
|
||||||
|
column_mapping = {}
|
||||||
|
|
||||||
|
# Find source columns (case-insensitive search)
|
||||||
|
cols_lower = {col.lower().strip(): col for col in xlsx_df.columns}
|
||||||
|
|
||||||
|
# Controller name -> TAGNAME
|
||||||
|
tagname_sources = ['controller name', 'controller', 'tagname', 'tag name']
|
||||||
|
column_mapping['TAGNAME'] = None
|
||||||
|
# First check if TAGNAME already exists (exact match, case-insensitive)
|
||||||
|
for col in xlsx_df.columns:
|
||||||
|
if col.upper().strip() == 'TAGNAME':
|
||||||
|
column_mapping['TAGNAME'] = col
|
||||||
|
break
|
||||||
|
# If not found, search by variations
|
||||||
|
if column_mapping['TAGNAME'] is None:
|
||||||
|
for src in tagname_sources:
|
||||||
|
if src in cols_lower:
|
||||||
|
column_mapping['TAGNAME'] = cols_lower[src]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Address name -> ADDR
|
||||||
|
addr_sources = ['address name', 'address', 'addr']
|
||||||
|
column_mapping['ADDR'] = None
|
||||||
|
# Check if ADDR already exists
|
||||||
|
for col in xlsx_df.columns:
|
||||||
|
if col.upper().strip() == 'ADDR':
|
||||||
|
column_mapping['ADDR'] = col
|
||||||
|
break
|
||||||
|
if column_mapping['ADDR'] is None:
|
||||||
|
for src in addr_sources:
|
||||||
|
if src in cols_lower:
|
||||||
|
column_mapping['ADDR'] = cols_lower[src]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Signal Type -> TERM
|
||||||
|
term_sources = ['signal type', 'signal', 'term']
|
||||||
|
column_mapping['TERM'] = None
|
||||||
|
# Check if TERM already exists
|
||||||
|
for col in xlsx_df.columns:
|
||||||
|
if col.upper().strip() == 'TERM':
|
||||||
|
column_mapping['TERM'] = col
|
||||||
|
break
|
||||||
|
if column_mapping['TERM'] is None:
|
||||||
|
for src in term_sources:
|
||||||
|
if src in cols_lower:
|
||||||
|
column_mapping['TERM'] = cols_lower[src]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Assigned device -> DESCA
|
||||||
|
desca_sources = ['assigned device', 'assigned', 'device']
|
||||||
|
column_mapping['DESCA'] = None
|
||||||
|
# Check if DESCA already exists
|
||||||
|
for col in xlsx_df.columns:
|
||||||
|
if col.upper().strip() == 'DESCA':
|
||||||
|
column_mapping['DESCA'] = col
|
||||||
|
break
|
||||||
|
if column_mapping['DESCA'] is None:
|
||||||
|
for src in desca_sources:
|
||||||
|
if src in cols_lower:
|
||||||
|
column_mapping['DESCA'] = cols_lower[src]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Description -> DESCB
|
||||||
|
descb_sources = ['description', 'desc']
|
||||||
|
column_mapping['DESCB'] = None
|
||||||
|
# Check if DESCB already exists
|
||||||
|
for col in xlsx_df.columns:
|
||||||
|
if col.upper().strip() == 'DESCB':
|
||||||
|
column_mapping['DESCB'] = col
|
||||||
|
break
|
||||||
|
if column_mapping['DESCB'] is None:
|
||||||
|
for src in descb_sources:
|
||||||
|
if src in cols_lower:
|
||||||
|
column_mapping['DESCB'] = cols_lower[src]
|
||||||
|
break
|
||||||
|
|
||||||
|
return column_mapping
|
||||||
|
|
||||||
|
def convert_xlsx_to_csv(xlsx_path, csv_path=None, sheet_name=None):
|
||||||
|
"""
|
||||||
|
Convert XLSX file to CSV with specified column mappings and transformations.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
xlsx_path: Path to input XLSX file
|
||||||
|
csv_path: Path to output CSV file (default: same name as XLSX but .csv extension)
|
||||||
|
sheet_name: Name of the sheet to read (if None, tries 'MCM03' first, then first sheet)
|
||||||
|
"""
|
||||||
|
# Read XLSX file
|
||||||
|
print(f"Reading XLSX file: {xlsx_path}")
|
||||||
|
|
||||||
|
# Try to find the right sheet - look for any MCM sheet (MCM02, MCM03, MCM14, etc.)
|
||||||
|
if sheet_name is None:
|
||||||
|
xls = pd.ExcelFile(xlsx_path)
|
||||||
|
# Find any sheet that starts with 'MCM'
|
||||||
|
mcm_sheets = [s for s in xls.sheet_names if s.upper().startswith('MCM')]
|
||||||
|
if mcm_sheets:
|
||||||
|
# Prioritize MCM14 if it exists, otherwise use the highest numbered MCM sheet
|
||||||
|
if 'MCM14' in mcm_sheets:
|
||||||
|
sheet_name = 'MCM14'
|
||||||
|
print(f"Found MCM14 sheet, reading from it...")
|
||||||
|
else:
|
||||||
|
# Sort MCM sheets and use the one with highest number
|
||||||
|
def extract_mcm_number(sheet):
|
||||||
|
match = re.match(r'MCM(\d+)', sheet.upper())
|
||||||
|
return int(match.group(1)) if match else 0
|
||||||
|
mcm_sheets_sorted = sorted(mcm_sheets, key=extract_mcm_number, reverse=True)
|
||||||
|
sheet_name = mcm_sheets_sorted[0]
|
||||||
|
print(f"Found MCM sheet: '{sheet_name}', reading from it...")
|
||||||
|
else:
|
||||||
|
# If no MCM sheet, try to find sheet with expected columns
|
||||||
|
for s in xls.sheet_names:
|
||||||
|
test_df = pd.read_excel(xlsx_path, sheet_name=s, nrows=1)
|
||||||
|
cols_lower = [c.lower().strip() for c in test_df.columns]
|
||||||
|
# Check if it has any of our expected column names
|
||||||
|
if any(key in ' '.join(cols_lower) for key in ['controller', 'signal', 'address', 'assigned', 'description']):
|
||||||
|
sheet_name = s
|
||||||
|
print(f"Found sheet with expected columns: '{sheet_name}', reading from it...")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Fallback to first sheet
|
||||||
|
sheet_name = xls.sheet_names[0]
|
||||||
|
print(f"No MCM sheet found, reading from first sheet: '{sheet_name}'...")
|
||||||
|
|
||||||
|
df = pd.read_excel(xlsx_path, sheet_name=sheet_name)
|
||||||
|
|
||||||
|
print(f"Original columns: {list(df.columns)}")
|
||||||
|
print(f"Total rows: {len(df)}")
|
||||||
|
|
||||||
|
# Get column mapping
|
||||||
|
column_mapping = get_column_mapping(df)
|
||||||
|
print(f"Column mapping: {column_mapping}")
|
||||||
|
|
||||||
|
# Create new DataFrame - preserve exact row order from Excel
|
||||||
|
new_df = pd.DataFrame()
|
||||||
|
new_df.index = df.index # Preserve original index order
|
||||||
|
|
||||||
|
# Map and copy data - preserve all rows in original order
|
||||||
|
if column_mapping['TAGNAME']:
|
||||||
|
new_df['TAGNAME'] = df[column_mapping['TAGNAME']].values
|
||||||
|
else:
|
||||||
|
print("Warning: TAGNAME column not found, using first column")
|
||||||
|
new_df['TAGNAME'] = df.iloc[:, 0].values if len(df.columns) > 0 else ''
|
||||||
|
|
||||||
|
if column_mapping['ADDR']:
|
||||||
|
new_df['ADDR'] = df[column_mapping['ADDR']].values
|
||||||
|
else:
|
||||||
|
new_df['ADDR'] = ''
|
||||||
|
|
||||||
|
if column_mapping['TERM']:
|
||||||
|
new_df['TERM'] = df[column_mapping['TERM']].values # Use .values to preserve exact order
|
||||||
|
else:
|
||||||
|
new_df['TERM'] = ''
|
||||||
|
|
||||||
|
# Empty column TERMDESC
|
||||||
|
new_df['TERMDESC'] = ''
|
||||||
|
|
||||||
|
if column_mapping['DESCA']:
|
||||||
|
new_df['DESCA'] = df[column_mapping['DESCA']].values
|
||||||
|
else:
|
||||||
|
new_df['DESCA'] = ''
|
||||||
|
|
||||||
|
if column_mapping['DESCB']:
|
||||||
|
new_df['DESCB'] = df[column_mapping['DESCB']].values
|
||||||
|
else:
|
||||||
|
new_df['DESCB'] = ''
|
||||||
|
|
||||||
|
# Empty columns: DESCC, DESCD, DESCE, INST, LOC
|
||||||
|
new_df['DESCC'] = ''
|
||||||
|
new_df['DESCD'] = ''
|
||||||
|
new_df['DESCE'] = ''
|
||||||
|
new_df['INST'] = ''
|
||||||
|
new_df['LOC'] = ''
|
||||||
|
|
||||||
|
# Reorder columns to match required output format (doesn't change row order)
|
||||||
|
new_df = new_df[['TAGNAME', 'ADDR', 'TERM', 'TERMDESC', 'DESCA', 'DESCB',
|
||||||
|
'DESCC', 'DESCD', 'DESCE', 'INST', 'LOC']]
|
||||||
|
|
||||||
|
# Reset index to ensure clean sequential order, but preserve row sequence
|
||||||
|
new_df = new_df.reset_index(drop=True)
|
||||||
|
|
||||||
|
print("Sorting data by controller groups and TERM order...")
|
||||||
|
|
||||||
|
# Add base prefix column for grouping
|
||||||
|
new_df['_base_prefix'] = new_df['TAGNAME'].apply(get_base_prefix)
|
||||||
|
|
||||||
|
# Add base prefix priority to sort PDPs first
|
||||||
|
def get_base_prefix_priority(base_prefix):
|
||||||
|
"""Return priority: PDP=1, others=2 (sorted alphabetically)"""
|
||||||
|
if base_prefix.upper().startswith('PDP'):
|
||||||
|
return (1, base_prefix) # PDPs first, then sorted by name
|
||||||
|
else:
|
||||||
|
return (2, base_prefix) # Others after PDPs, sorted by name
|
||||||
|
|
||||||
|
new_df['_base_priority'] = new_df['_base_prefix'].apply(get_base_prefix_priority)
|
||||||
|
|
||||||
|
# Add controller type priority for ordering within each base prefix group
|
||||||
|
def get_controller_type_priority(tagname):
|
||||||
|
"""Return priority: VFD=1, FIOM=2, FIOH=3, Other=4"""
|
||||||
|
if is_vfd_controller(tagname):
|
||||||
|
return 1
|
||||||
|
elif is_fiom_controller(tagname):
|
||||||
|
return 2
|
||||||
|
elif is_fioh_controller(tagname):
|
||||||
|
return 3
|
||||||
|
else:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
new_df['_ctrl_type_priority'] = new_df['TAGNAME'].apply(get_controller_type_priority)
|
||||||
|
|
||||||
|
# Separate rows by controller type for TERM sorting
|
||||||
|
fiom_mask = new_df['TAGNAME'].apply(is_fiom_controller)
|
||||||
|
fioh_mask = new_df['TAGNAME'].apply(is_fioh_controller)
|
||||||
|
vfd_mask = new_df['TAGNAME'].apply(is_vfd_controller)
|
||||||
|
other_mask = ~(fiom_mask | fioh_mask | vfd_mask)
|
||||||
|
|
||||||
|
# Sort FIOM by X format: X3_0, X3_1, X2_0, X2_1, X1_0, X1_1, X0_0, X0_1,
|
||||||
|
# X7_0, X7_1, X6_0, X6_1, X5_0, X5_1, X4_0, X4_1
|
||||||
|
if fiom_mask.any():
|
||||||
|
new_df.loc[fiom_mask, '_sort_priority'] = new_df.loc[fiom_mask, 'TERM'].apply(lambda x: sort_key_fiom(x)[0])
|
||||||
|
new_df.loc[fiom_mask, '_sort_suffix'] = new_df.loc[fiom_mask, 'TERM'].apply(lambda x: sort_key_fiom(x)[1])
|
||||||
|
print(f"Sorted {fiom_mask.sum()} FIOM rows by TERM")
|
||||||
|
else:
|
||||||
|
new_df['_sort_priority'] = 0
|
||||||
|
new_df['_sort_suffix'] = 0
|
||||||
|
|
||||||
|
# Sort FIOH by C format: C7_A, C7_B, C5_A, C5_B, C3_A, C3_B, C1_A, C1_B,
|
||||||
|
# C8_A, C8_B, C6_A, C6_B, C4_A, C4_B, C2_A, C2_B
|
||||||
|
if fioh_mask.any():
|
||||||
|
new_df.loc[fioh_mask, '_sort_priority'] = new_df.loc[fioh_mask, 'TERM'].apply(lambda x: sort_key_fioh(x)[0])
|
||||||
|
new_df.loc[fioh_mask, '_sort_suffix'] = new_df.loc[fioh_mask, 'TERM'].apply(lambda x: sort_key_fioh(x)[1])
|
||||||
|
print(f"Sorted {fioh_mask.sum()} FIOH rows by TERM")
|
||||||
|
|
||||||
|
# Fill NaN values in sort columns for non-FIOM/FIOH rows
|
||||||
|
new_df['_sort_priority'] = new_df['_sort_priority'].fillna(999)
|
||||||
|
new_df['_sort_suffix'] = new_df['_sort_suffix'].fillna(999)
|
||||||
|
|
||||||
|
# Final sort: PDPs first, then by base prefix, then by controller type (VFD, FIOM, FIOH),
|
||||||
|
# then by TAGNAME, then by TERM sort order
|
||||||
|
final_df = new_df.sort_values([
|
||||||
|
'_base_priority', # PDPs first, then others alphabetically
|
||||||
|
'_ctrl_type_priority', # VFD first, then FIOM, then FIOH
|
||||||
|
'TAGNAME', # Keep same controller together
|
||||||
|
'_sort_priority', # Sort TERM by custom order
|
||||||
|
'_sort_suffix'
|
||||||
|
])
|
||||||
|
|
||||||
|
# Drop temporary sorting columns
|
||||||
|
final_df = final_df.drop(columns=['_base_prefix', '_base_priority', '_ctrl_type_priority', '_sort_priority', '_sort_suffix'])
|
||||||
|
final_df = final_df.reset_index(drop=True)
|
||||||
|
|
||||||
|
print(f"Grouped controllers by base prefix and sorted within groups")
|
||||||
|
|
||||||
|
# Determine output path
|
||||||
|
if csv_path is None:
|
||||||
|
xlsx_path_obj = Path(xlsx_path)
|
||||||
|
csv_path = xlsx_path_obj.with_suffix('.csv')
|
||||||
|
|
||||||
|
# Write to CSV
|
||||||
|
print(f"Writing CSV file: {csv_path}")
|
||||||
|
try:
|
||||||
|
final_df.to_csv(csv_path, index=False)
|
||||||
|
print(f"Conversion complete! Total rows: {len(final_df)}")
|
||||||
|
except PermissionError:
|
||||||
|
print(f"\nERROR: Cannot write to '{csv_path}' - file is likely open in another program.")
|
||||||
|
print("Please close the CSV file in Excel or any other program and try again.")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nERROR writing CSV file: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
return final_df
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Get input file and optional sheet name from command line arguments
|
||||||
|
input_file = None
|
||||||
|
sheet_name_arg = None
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
input_file = sys.argv[1]
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
sheet_name_arg = sys.argv[2]
|
||||||
|
if input_file is None:
|
||||||
|
input_file = "Amazon CDW5_Devices IO (1).xlsx"
|
||||||
|
|
||||||
|
# Check if file exists
|
||||||
|
if Path(input_file).exists():
|
||||||
|
result_df = convert_xlsx_to_csv(input_file, sheet_name=sheet_name_arg)
|
||||||
|
print(f"\nSummary:")
|
||||||
|
print(f"- Total rows in output: {len(result_df)}")
|
||||||
|
else:
|
||||||
|
print(f"Error: File '{input_file}' not found!")
|
||||||
|
print("Please ensure the XLSX file is in the same directory as this script.")
|
||||||
|
print("\nUsage: python xlsx_to_csv.py [filename.xlsx] [sheet_name]")
|
||||||
|
print("Example: python xlsx_to_csv.py 'file.xlsx' 'MCM14'")
|
||||||
BIN
AutoCAD/MCM_Installer_drawing/save13d393d4.tmp
Normal file
BIN
AutoCAD/MCM_Installer_drawing/save13d393d4.tmp
Normal file
Binary file not shown.
3069
SCADA/Undrscore/output.json
Normal file
3069
SCADA/Undrscore/output.json
Normal file
File diff suppressed because it is too large
Load Diff
3069
SCADA/Undrscore/test.json
Normal file
3069
SCADA/Undrscore/test.json
Normal file
File diff suppressed because it is too large
Load Diff
49
SCADA/Undrscore/underscore.py
Normal file
49
SCADA/Undrscore/underscore.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def fix_names(obj):
|
||||||
|
"""
|
||||||
|
Recursively traverse obj (which may be a dict or list),
|
||||||
|
and whenever we find obj['meta']['name'], replace '-' with '_'.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
# if this dict has a 'meta' sub-dict with a 'name' key, fix it
|
||||||
|
meta = obj.get("meta")
|
||||||
|
if isinstance(meta, dict) and "name" in meta:
|
||||||
|
name = meta["name"]
|
||||||
|
if isinstance(name, str) and "-" in name:
|
||||||
|
meta["name"] = name.replace("-", "_")
|
||||||
|
# recurse into all values
|
||||||
|
for v in obj.values():
|
||||||
|
fix_names(v)
|
||||||
|
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
for item in obj:
|
||||||
|
fix_names(item)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python3 process_json.py input.json [output.json]")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
in_path = sys.argv[1]
|
||||||
|
out_path = sys.argv[2] if len(sys.argv) >= 3 else None
|
||||||
|
|
||||||
|
# load
|
||||||
|
with open(in_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
# process
|
||||||
|
fix_names(data)
|
||||||
|
|
||||||
|
# write
|
||||||
|
if out_path:
|
||||||
|
with open(out_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
print(f"Processed JSON written to {out_path}")
|
||||||
|
else:
|
||||||
|
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
38
SCADA/network_topology/CNO8_MCM05.csv
Normal file
38
SCADA/network_topology/CNO8_MCM05.csv
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
DPM,DPM_IP,Name,IP
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM1,11.200.1.101
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM3,11.200.1.102
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM5,11.200.1.103
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM7,11.200.1.104
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM9,11.200.1.105
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM2,11.200.1.106
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM4,11.200.1.107
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM6,11.200.1.108
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM8,11.200.1.109
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM10,11.200.1.110
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM11,11.200.1.111
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM13,11.200.1.112
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM15,11.200.1.113
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM17,11.200.1.114
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM18,11.200.1.115
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM12,11.200.1.116
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM14,11.200.1.117
|
||||||
|
DPM01_VS01C,11.200.1.2,VS01C_FIOM16,11.200.1.118
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM1,11.200.1.121
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM3,11.200.1.122
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM5,11.200.1.123
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM7,11.200.1.124
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM9,11.200.1.125
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM2,11.200.1.126
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM4,11.200.1.127
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM6,11.200.1.128
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM8,11.200.1.129
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM10,11.200.1.130
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM11,11.200.1.131
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM13,11.200.1.132
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM15,11.200.1.133
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM17,11.200.1.134
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM19,11.200.1.135
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM12,11.200.1.136
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM14,11.200.1.137
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM16,11.200.1.138
|
||||||
|
DPM01_VS01B,11.200.1.3,VS01B_FIOM18,11.200.1.139
|
||||||
|
3525
SCADA/network_topology/MCM05_view.json
Normal file
3525
SCADA/network_topology/MCM05_view.json
Normal file
File diff suppressed because it is too large
Load Diff
697
SCADA/network_topology/generate_network_topology.py
Normal file
697
SCADA/network_topology/generate_network_topology.py
Normal file
@ -0,0 +1,697 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script to generate Ignition MCM view JSON from CSV input.
|
||||||
|
CSV format: DPM,DPM_IP,Name,IP
|
||||||
|
"""
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
def create_plc_device(mcm_name: str, x: float, y: float) -> Dict[str, Any]:
|
||||||
|
"""Create PLC device component."""
|
||||||
|
return {
|
||||||
|
"type": "ia.display.view",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"path": "Windows/Tabs/Enternet Windows/Components/Device",
|
||||||
|
"params": {
|
||||||
|
"name": mcm_name,
|
||||||
|
"type": "PLC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": "PLC"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
"height": 0.1032,
|
||||||
|
"width": 0.0522
|
||||||
|
},
|
||||||
|
"custom": {},
|
||||||
|
"propConfig": {
|
||||||
|
"props.params.tag": {
|
||||||
|
"binding": {
|
||||||
|
"config": {
|
||||||
|
"fallbackDelay": 2.5,
|
||||||
|
"mode": "indirect",
|
||||||
|
"references": {
|
||||||
|
"fc": "{session.custom.fc}"
|
||||||
|
},
|
||||||
|
"tagPath": f"[{{fc}}_SCADA_TAG_PROVIDER]System/{mcm_name}/Rack/Communication_Faulted"
|
||||||
|
},
|
||||||
|
"transforms": [
|
||||||
|
{
|
||||||
|
"expression": "coalesce({value},{view.params.forceFaultStatus},\"comm\")",
|
||||||
|
"type": "expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fallback": False,
|
||||||
|
"inputType": "scalar",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"input": True,
|
||||||
|
"output": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "comm",
|
||||||
|
"output": "comm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputType": "scalar",
|
||||||
|
"type": "map"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "tag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_plc_switch(x: float, y: float, down: bool = True, up: bool = True,
|
||||||
|
left: bool = False, right: bool = False, name: str = "PLC_Switch") -> Dict[str, Any]:
|
||||||
|
"""Create PLC Switch component."""
|
||||||
|
return {
|
||||||
|
"type": "ia.display.view",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"path": "Windows/Tabs/Enternet Windows/Components/PLC_Switch",
|
||||||
|
"params": {
|
||||||
|
"down": down,
|
||||||
|
"left": left,
|
||||||
|
"right": right,
|
||||||
|
"up": up
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": name
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
"height": 0.1032,
|
||||||
|
"width": 0.0522
|
||||||
|
},
|
||||||
|
"custom": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_switch(x: float, y: float, down: bool = True, up: bool = True,
|
||||||
|
left: bool = False, right: bool = False, name: str = "Switch") -> Dict[str, Any]:
|
||||||
|
"""Create Switch component."""
|
||||||
|
return {
|
||||||
|
"type": "ia.display.view",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"path": "Windows/Tabs/Enternet Windows/Components/Switch",
|
||||||
|
"params": {
|
||||||
|
"down": down,
|
||||||
|
"left": left,
|
||||||
|
"right": right,
|
||||||
|
"up": up
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": name
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
"height": 0.1032,
|
||||||
|
"width": 0.0522
|
||||||
|
},
|
||||||
|
"custom": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_line(x: float, y: float, down: bool = True, up: bool = True,
|
||||||
|
left: bool = False, right: bool = False, name: str = "Line") -> Dict[str, Any]:
|
||||||
|
"""Create Line component."""
|
||||||
|
return {
|
||||||
|
"type": "ia.display.view",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"path": "Windows/Tabs/Enternet Windows/Components/Line",
|
||||||
|
"params": {
|
||||||
|
"down": down,
|
||||||
|
"left": left,
|
||||||
|
"right": right,
|
||||||
|
"up": up
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": name
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
"height": 0.1032,
|
||||||
|
"width": 0.0522
|
||||||
|
},
|
||||||
|
"custom": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_dpm_device(mcm_name: str, dpm_name: str, dpm_ip: str, x: float, y: float) -> Dict[str, Any]:
|
||||||
|
"""Create DPM device component."""
|
||||||
|
return {
|
||||||
|
"type": "ia.display.view",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"path": "Windows/Tabs/Enternet Windows/Components/Device",
|
||||||
|
"params": {
|
||||||
|
"ip": dpm_ip,
|
||||||
|
"name": dpm_name,
|
||||||
|
"type": "DPM_BLOCK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": dpm_name
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
"height": 0.1032,
|
||||||
|
"width": 0.0522
|
||||||
|
},
|
||||||
|
"custom": {},
|
||||||
|
"propConfig": {
|
||||||
|
"props.params.tag": {
|
||||||
|
"binding": {
|
||||||
|
"config": {
|
||||||
|
"fallbackDelay": 2.5,
|
||||||
|
"mode": "indirect",
|
||||||
|
"references": {
|
||||||
|
"fc": "{session.custom.fc}"
|
||||||
|
},
|
||||||
|
"tagPath": f"[{{fc}}_SCADA_TAG_PROVIDER]System/{mcm_name}/IO_BLOCK/DPM/{dpm_name}/Communication_Faulted"
|
||||||
|
},
|
||||||
|
"transforms": [
|
||||||
|
{
|
||||||
|
"expression": "coalesce({value},{view.params.forceFaultStatus},\"comm\")",
|
||||||
|
"type": "expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fallback": False,
|
||||||
|
"inputType": "scalar",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"input": True,
|
||||||
|
"output": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "comm",
|
||||||
|
"output": "comm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputType": "scalar",
|
||||||
|
"type": "map"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "tag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def determine_device_type(device_name: str) -> str:
|
||||||
|
"""Determine device type from name."""
|
||||||
|
name_upper = device_name.upper()
|
||||||
|
|
||||||
|
if '_VFD' in name_upper:
|
||||||
|
return 'APF'
|
||||||
|
elif '_EX' in name_upper:
|
||||||
|
return 'EXTENDO'
|
||||||
|
elif '_FIO' in name_upper or '_SIO' in name_upper or '_FIOM' in name_upper:
|
||||||
|
return 'FIO_SIO'
|
||||||
|
elif '_TIPPER' in name_upper:
|
||||||
|
return 'TIPPER'
|
||||||
|
elif '_PMM' in name_upper:
|
||||||
|
return 'PMM'
|
||||||
|
elif '_ZMX' in name_upper:
|
||||||
|
return 'ZMX'
|
||||||
|
else:
|
||||||
|
return 'APF' # Default
|
||||||
|
|
||||||
|
|
||||||
|
def determine_device_category(device_name: str, device_type: str) -> str:
|
||||||
|
"""Determine device category for tag path."""
|
||||||
|
if device_type == 'APF':
|
||||||
|
return 'Conveyor/VFD'
|
||||||
|
elif device_type == 'EXTENDO':
|
||||||
|
return 'Conveyor/EXTENDO'
|
||||||
|
elif device_type == 'TIPPER':
|
||||||
|
return 'Conveyor/TIPPER'
|
||||||
|
elif device_type == 'FIO_SIO':
|
||||||
|
return 'IO_BLOCK/FIO'
|
||||||
|
elif device_type == 'PMM':
|
||||||
|
return 'PMM'
|
||||||
|
elif device_type == 'ZMX':
|
||||||
|
return 'Chute/D2C'
|
||||||
|
else:
|
||||||
|
return 'Conveyor/VFD'
|
||||||
|
|
||||||
|
|
||||||
|
def create_device(mcm_name: str, device_name: str, device_ip: str,
|
||||||
|
x: float, y: float) -> Dict[str, Any]:
|
||||||
|
"""Create a device component."""
|
||||||
|
device_type = determine_device_type(device_name)
|
||||||
|
category = determine_device_category(device_name, device_type)
|
||||||
|
|
||||||
|
# For ZMX devices, replace _ZMX* suffix with _D2C_CHUTE in the tag path
|
||||||
|
tag_device_name = device_name
|
||||||
|
if device_type == 'ZMX':
|
||||||
|
# Replace _ZMX followed by any digits with _D2C_CHUTE
|
||||||
|
tag_device_name = re.sub(r'_ZMX\d*', '_D2C_CHUTE', device_name)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "ia.display.view",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"path": "Windows/Tabs/Enternet Windows/Components/Device",
|
||||||
|
"params": {
|
||||||
|
"ip": device_ip,
|
||||||
|
"name": device_name,
|
||||||
|
"type": device_type
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": device_name
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
"height": 0.1032,
|
||||||
|
"width": 0.0522
|
||||||
|
},
|
||||||
|
"custom": {},
|
||||||
|
"propConfig": {
|
||||||
|
"props.params.tag": {
|
||||||
|
"binding": {
|
||||||
|
"config": {
|
||||||
|
"fallbackDelay": 2.5,
|
||||||
|
"mode": "indirect",
|
||||||
|
"references": {
|
||||||
|
"fc": "{session.custom.fc}"
|
||||||
|
},
|
||||||
|
"tagPath": f"[{{fc}}_SCADA_TAG_PROVIDER]System/{mcm_name}/{category}/{tag_device_name}/Communication_Faulted"
|
||||||
|
},
|
||||||
|
"transforms": [
|
||||||
|
{
|
||||||
|
"expression": "coalesce({value},{view.params.forceFaultStatus},\"comm\")",
|
||||||
|
"type": "expression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fallback": False,
|
||||||
|
"inputType": "scalar",
|
||||||
|
"mappings": [
|
||||||
|
{
|
||||||
|
"input": True,
|
||||||
|
"output": True
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "comm",
|
||||||
|
"output": "comm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputType": "scalar",
|
||||||
|
"type": "map"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "tag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def extract_mcm_number(full_name: str) -> str:
|
||||||
|
"""Extract MCM number from full name (e.g., 'SAT9_MCM02' -> 'MCM02')."""
|
||||||
|
# Look for MCM followed by digits
|
||||||
|
match = re.search(r'MCM\d+', full_name, re.IGNORECASE)
|
||||||
|
if match:
|
||||||
|
return match.group().upper()
|
||||||
|
return full_name
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mcm_view(mcm_name: str, dpm_data: Dict[str, Dict]) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Generate the complete MCM view structure with multi-row support.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mcm_name: Name of the MCM (e.g., "MCM02")
|
||||||
|
dpm_data: Dictionary mapping DPM names to their info and devices
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List containing the root container with all children
|
||||||
|
"""
|
||||||
|
children = []
|
||||||
|
switch_counter = 1
|
||||||
|
|
||||||
|
# Layout constants
|
||||||
|
start_x = 0.0188
|
||||||
|
plc_y = 0.0024
|
||||||
|
plc_switch_y = 0.1028
|
||||||
|
dpm_start_y = 0.2034
|
||||||
|
vertical_switch_offset = 0.0966 # Distance from DPM to vertical switch between DPMs
|
||||||
|
base_dpm_spacing = 0.1974 # Base vertical spacing between DPMs
|
||||||
|
horizontal_spacing = 0.0527 # Horizontal spacing between devices
|
||||||
|
device_below_switch = 0.0997 # Distance from switch to device below it
|
||||||
|
row_height = 0.20 # Height for each row (switch + device + gap)
|
||||||
|
|
||||||
|
# Maximum devices per row (fits in 1920px width)
|
||||||
|
max_devices_per_row = 17
|
||||||
|
|
||||||
|
# Add PLC at the top
|
||||||
|
children.append(create_plc_device(mcm_name, start_x, plc_y))
|
||||||
|
|
||||||
|
# Add PLC Switch connecting to DPMs
|
||||||
|
children.append(create_plc_switch(start_x, plc_switch_y, down=True, up=True))
|
||||||
|
|
||||||
|
# Track cumulative Y offset for variable DPM spacing
|
||||||
|
cumulative_y = dpm_start_y
|
||||||
|
|
||||||
|
# Add DPMs with their devices
|
||||||
|
for dpm_idx, (dpm_name, dpm_info) in enumerate(dpm_data.items()):
|
||||||
|
dpm_y = cumulative_y
|
||||||
|
|
||||||
|
# Add DPM
|
||||||
|
children.append(create_dpm_device(
|
||||||
|
mcm_name,
|
||||||
|
dpm_name,
|
||||||
|
dpm_info['dpm_ip'],
|
||||||
|
start_x,
|
||||||
|
dpm_y
|
||||||
|
))
|
||||||
|
|
||||||
|
# Add devices horizontally from this DPM (with multi-row support)
|
||||||
|
devices = dpm_info.get('devices', [])
|
||||||
|
num_rows = max(1, (len(devices) + max_devices_per_row - 1) // max_devices_per_row) if devices else 1
|
||||||
|
|
||||||
|
# If multiple rows, create vertical chain from DPM (Lines + Switch)
|
||||||
|
# Position vertical chain at DPM location to avoid overlap with first row
|
||||||
|
row_connector_x = start_x
|
||||||
|
|
||||||
|
if num_rows > 1:
|
||||||
|
# Calculate position of last device in first row
|
||||||
|
first_row_device_count = min(max_devices_per_row, len(devices))
|
||||||
|
last_device_pos_in_row = first_row_device_count - 1
|
||||||
|
first_row_last_device_x = start_x + ((last_device_pos_in_row + 1) * horizontal_spacing)
|
||||||
|
|
||||||
|
# Add 3 Lines connecting last device of first row to vertical chain
|
||||||
|
first_row_switch_y = dpm_y # Y position of first row switches
|
||||||
|
first_row_device_y = first_row_switch_y + device_below_switch # Y position of first row devices
|
||||||
|
second_row_switch_y = dpm_y + row_height # Y position of second row switches
|
||||||
|
|
||||||
|
# Position lines to the right - full spacing away (same distance as between switches)
|
||||||
|
lines_x = first_row_last_device_x + horizontal_spacing
|
||||||
|
|
||||||
|
# Line 1: At same height as first row switch, connects left to switch and down
|
||||||
|
line1_y = first_row_switch_y
|
||||||
|
children.append(create_line(
|
||||||
|
lines_x,
|
||||||
|
line1_y,
|
||||||
|
down=True,
|
||||||
|
up=False,
|
||||||
|
left=True,
|
||||||
|
right=False,
|
||||||
|
name="FirstRowLine1"
|
||||||
|
))
|
||||||
|
|
||||||
|
# Line 2: At same height as first row devices, vertical line up and down
|
||||||
|
line2_y = first_row_device_y
|
||||||
|
children.append(create_line(
|
||||||
|
lines_x,
|
||||||
|
line2_y,
|
||||||
|
down=True,
|
||||||
|
up=True,
|
||||||
|
left=False,
|
||||||
|
right=False,
|
||||||
|
name="FirstRowLine2"
|
||||||
|
))
|
||||||
|
|
||||||
|
# Line 3: At same height as second row switches, connects up and left to vertical chain
|
||||||
|
line3_y = second_row_switch_y
|
||||||
|
children.append(create_line(
|
||||||
|
lines_x,
|
||||||
|
line3_y,
|
||||||
|
down=False,
|
||||||
|
up=True,
|
||||||
|
left=True,
|
||||||
|
right=False,
|
||||||
|
name="FirstRowLine3"
|
||||||
|
))
|
||||||
|
|
||||||
|
# Create vertical connectors: Line - Switch - Line pattern
|
||||||
|
# Need 3 connectors for proper connection: Line, Switch (branches), Line
|
||||||
|
num_vertical_connectors = 3 if num_rows == 2 else (num_rows - 1) * 2 + 1
|
||||||
|
|
||||||
|
for v_idx in range(num_vertical_connectors):
|
||||||
|
v_y = dpm_y + ((v_idx + 1) * row_height / 2)
|
||||||
|
|
||||||
|
# Determine which row this connector branches to (if any)
|
||||||
|
connects_to_row = None
|
||||||
|
if num_rows == 2 and v_idx == 1:
|
||||||
|
connects_to_row = 1 # Second connector (Switch) connects to second row
|
||||||
|
elif num_rows > 2:
|
||||||
|
# For more rows, connect at appropriate intervals
|
||||||
|
for r in range(1, num_rows):
|
||||||
|
if v_idx == (r * 2 - 1):
|
||||||
|
connects_to_row = r
|
||||||
|
break
|
||||||
|
|
||||||
|
# All connectors connect upward and downward in the chain
|
||||||
|
has_up = True
|
||||||
|
has_down = True
|
||||||
|
|
||||||
|
# Pattern: Line (idx 0), Switch (idx 1), Line (idx 2)
|
||||||
|
if v_idx == 1:
|
||||||
|
# Middle is Switch (vertical only, no branching)
|
||||||
|
children.append(create_switch(
|
||||||
|
row_connector_x,
|
||||||
|
v_y,
|
||||||
|
down=has_down,
|
||||||
|
up=has_up,
|
||||||
|
left=False,
|
||||||
|
right=False, # No branching
|
||||||
|
name=f"Switch{switch_counter}"
|
||||||
|
))
|
||||||
|
switch_counter += 1
|
||||||
|
else:
|
||||||
|
# First and third are Lines
|
||||||
|
line_num = 1 if v_idx == 0 else 2
|
||||||
|
children.append(create_line(
|
||||||
|
row_connector_x,
|
||||||
|
v_y,
|
||||||
|
down=has_down,
|
||||||
|
up=has_up,
|
||||||
|
left=False,
|
||||||
|
right=False, # Lines don't branch
|
||||||
|
name=f"VerticalLine{line_num}"
|
||||||
|
))
|
||||||
|
|
||||||
|
for dev_idx, device in enumerate(devices):
|
||||||
|
# Calculate which row and position in row
|
||||||
|
row_num = dev_idx // max_devices_per_row
|
||||||
|
pos_in_row = dev_idx % max_devices_per_row
|
||||||
|
|
||||||
|
# Calculate position
|
||||||
|
if row_num == 0:
|
||||||
|
# First row: left to right, starting after DPM
|
||||||
|
device_x = start_x + ((pos_in_row + 1) * horizontal_spacing)
|
||||||
|
else:
|
||||||
|
# Additional rows: right to left, starting from first row's last switch position
|
||||||
|
# Start at same X as last switch of first row and go backwards (leftward)
|
||||||
|
if num_rows > 1:
|
||||||
|
device_x = first_row_last_device_x - (pos_in_row * horizontal_spacing)
|
||||||
|
else:
|
||||||
|
device_x = start_x + ((pos_in_row + 1) * horizontal_spacing)
|
||||||
|
|
||||||
|
switch_y = dpm_y + (row_num * row_height)
|
||||||
|
device_y = switch_y + device_below_switch
|
||||||
|
|
||||||
|
# Determine switch connections
|
||||||
|
is_first_in_row = (pos_in_row == 0)
|
||||||
|
is_last_in_row = (pos_in_row == max_devices_per_row - 1) or (dev_idx == len(devices) - 1)
|
||||||
|
is_last_device_of_first_row = (row_num == 0 and is_last_in_row and num_rows > 1)
|
||||||
|
|
||||||
|
# Switch connections
|
||||||
|
if row_num == 0:
|
||||||
|
# First row: left to right
|
||||||
|
connects_left = True
|
||||||
|
connects_right = (not is_last_in_row) or (num_rows > 1) # Last switch connects right if multiple rows
|
||||||
|
else:
|
||||||
|
# Additional rows: right to left (reversed)
|
||||||
|
connects_right = True
|
||||||
|
connects_left = not is_last_in_row
|
||||||
|
|
||||||
|
# Add switch above device
|
||||||
|
children.append(create_switch(
|
||||||
|
device_x,
|
||||||
|
switch_y,
|
||||||
|
down=True,
|
||||||
|
up=False,
|
||||||
|
left=connects_left,
|
||||||
|
right=connects_right,
|
||||||
|
name=f"Switch{switch_counter}"
|
||||||
|
))
|
||||||
|
switch_counter += 1
|
||||||
|
|
||||||
|
# Add device
|
||||||
|
children.append(create_device(
|
||||||
|
mcm_name,
|
||||||
|
device['name'],
|
||||||
|
device['ip'],
|
||||||
|
device_x,
|
||||||
|
device_y
|
||||||
|
))
|
||||||
|
|
||||||
|
# Calculate total height used by this DPM including all rows
|
||||||
|
if devices and num_rows > 1:
|
||||||
|
# Multi-row: With vertical connectors, height includes the vertical Line/Switch chain
|
||||||
|
num_vertical_connectors = 3 if num_rows == 2 else (num_rows - 1) * 2 + 1
|
||||||
|
# Vertical connectors start at (v_idx + 1) * row_height / 2
|
||||||
|
# Last connector position will be where next DPM connects
|
||||||
|
dpm_total_height = ((num_vertical_connectors + 1) * row_height / 2)
|
||||||
|
cumulative_y += dpm_total_height
|
||||||
|
else:
|
||||||
|
# Single row or no devices - use original simple spacing
|
||||||
|
# Just add base DPM spacing, vertical switch will be added separately
|
||||||
|
cumulative_y += base_dpm_spacing
|
||||||
|
|
||||||
|
# Add vertical switch below DPM to connect to next DPM (except for the last DPM)
|
||||||
|
# Only needed if single row or no devices - vertical chain already handles multi-row
|
||||||
|
if dpm_idx < len(dpm_data) - 1 and (num_rows == 1 or not devices):
|
||||||
|
switch_y = dpm_y + vertical_switch_offset
|
||||||
|
|
||||||
|
children.append(create_switch(
|
||||||
|
start_x,
|
||||||
|
switch_y,
|
||||||
|
down=True,
|
||||||
|
up=True,
|
||||||
|
left=False,
|
||||||
|
right=False,
|
||||||
|
name=f"Switch{switch_counter}"
|
||||||
|
))
|
||||||
|
switch_counter += 1
|
||||||
|
|
||||||
|
# Create root container
|
||||||
|
root = {
|
||||||
|
"type": "ia.container.coord",
|
||||||
|
"version": 0,
|
||||||
|
"props": {
|
||||||
|
"mode": "percent"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": mcm_name
|
||||||
|
},
|
||||||
|
"position": {},
|
||||||
|
"custom": {},
|
||||||
|
"children": children
|
||||||
|
}
|
||||||
|
|
||||||
|
return [root]
|
||||||
|
|
||||||
|
|
||||||
|
def read_csv_file(csv_path: str) -> Dict[str, Dict]:
|
||||||
|
"""
|
||||||
|
Read CSV file and extract DPM and device information.
|
||||||
|
|
||||||
|
CSV format: DPM,DPM_IP,Name,IP
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mapping DPM names to their info and devices
|
||||||
|
"""
|
||||||
|
dpm_data = defaultdict(lambda: {'dpm_ip': None, 'devices': []})
|
||||||
|
|
||||||
|
with open(csv_path, 'r', encoding='utf-8-sig') as f: # utf-8-sig handles BOM
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
|
||||||
|
# Debug: print available columns
|
||||||
|
first_row = True
|
||||||
|
for row in reader:
|
||||||
|
if first_row:
|
||||||
|
print(f"CSV columns found: {list(row.keys())}")
|
||||||
|
first_row = False
|
||||||
|
|
||||||
|
# Handle potential column name variations
|
||||||
|
dpm_key = 'DPM' if 'DPM' in row else next((k for k in row.keys() if 'DPM' in k.upper()), None)
|
||||||
|
dpm_ip_key = 'DPM_IP' if 'DPM_IP' in row else next((k for k in row.keys() if 'DPM_IP' in k.upper() or 'DPM IP' in k.upper()), None)
|
||||||
|
name_key = 'Name' if 'Name' in row else next((k for k in row.keys() if k.upper() == 'NAME'), None)
|
||||||
|
ip_key = 'IP' if 'IP' in row else next((k for k in row.keys() if k.upper() == 'IP' and k != dpm_ip_key), None)
|
||||||
|
|
||||||
|
if not dpm_key or not dpm_ip_key:
|
||||||
|
print(f"Warning: Could not find DPM columns. Available: {list(row.keys())}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
dpm_name = row[dpm_key].strip()
|
||||||
|
dpm_ip = row[dpm_ip_key].strip()
|
||||||
|
|
||||||
|
# Skip empty rows
|
||||||
|
if not dpm_name or not dpm_ip:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Set DPM IP if not already set
|
||||||
|
if dpm_data[dpm_name]['dpm_ip'] is None:
|
||||||
|
dpm_data[dpm_name]['dpm_ip'] = dpm_ip
|
||||||
|
|
||||||
|
# Add device if name and IP are provided
|
||||||
|
if name_key and ip_key:
|
||||||
|
device_name = row[name_key].strip()
|
||||||
|
device_ip = row[ip_key].strip()
|
||||||
|
|
||||||
|
if device_name and device_ip:
|
||||||
|
dpm_data[dpm_name]['devices'].append({
|
||||||
|
'name': device_name,
|
||||||
|
'ip': device_ip
|
||||||
|
})
|
||||||
|
|
||||||
|
return dict(dpm_data)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: python generate_network_topology.py <csv_file> <mcm_name> [output_file]")
|
||||||
|
print("\nCSV format: DPM,DPM_IP,Name,IP")
|
||||||
|
print("Example: python generate_network_topology.py dpms.csv SAT9_MCM02 output.json")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
csv_file = sys.argv[1]
|
||||||
|
mcm_name_input = sys.argv[2]
|
||||||
|
|
||||||
|
# Extract just the MCM number (e.g., "SAT9_MCM02" -> "MCM02")
|
||||||
|
mcm_name = extract_mcm_number(mcm_name_input)
|
||||||
|
output_file = sys.argv[3] if len(sys.argv) > 3 else f"{mcm_name}_view.json"
|
||||||
|
|
||||||
|
print(f"Processing {csv_file}...")
|
||||||
|
print(f"MCM Name: {mcm_name}")
|
||||||
|
|
||||||
|
# Read CSV
|
||||||
|
dpm_data = read_csv_file(csv_file)
|
||||||
|
|
||||||
|
# Count total devices
|
||||||
|
total_devices = sum(len(dpm_info['devices']) for dpm_info in dpm_data.values())
|
||||||
|
|
||||||
|
# Generate view
|
||||||
|
view = generate_mcm_view(mcm_name, dpm_data)
|
||||||
|
|
||||||
|
# Write output
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
json.dump(view, f, indent=2)
|
||||||
|
|
||||||
|
print(f"\n✓ Generated view for {mcm_name}")
|
||||||
|
print(f" - {len(dpm_data)} DPMs")
|
||||||
|
print(f" - {total_devices} total devices")
|
||||||
|
print(f"✓ Output written to: {output_file}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
1
SCADA/tag_props_scada
Submodule
1
SCADA/tag_props_scada
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 4b861e5b9fe44d233218b53a13acda650797dfe9
|
||||||
Loading…
x
Reference in New Issue
Block a user