ErrorCollector standarisiert. Warnings werden von getpositions zur routing zu draw durchgereicht.
This commit is contained in:
+21
-1
@@ -385,6 +385,12 @@ def model_from_json(json_file):
|
||||
if error_fields['errors_missing_attributes']:
|
||||
warnings_dict['missing_attributes'] = error_fields['errors_missing_attributes']
|
||||
|
||||
# Warnungen aus getpositions und routing übernehmen
|
||||
if 'warnings' in data:
|
||||
warnings_from_upstream = data.pop('warnings')
|
||||
if isinstance(warnings_from_upstream, dict):
|
||||
warnings_dict.update(warnings_from_upstream)
|
||||
|
||||
if errors_dict:
|
||||
error_collector.add_errors(errors_dict)
|
||||
if warnings_dict:
|
||||
@@ -697,6 +703,20 @@ def _create_error_sheets(wb, error_collector: ErrorCollector = None):
|
||||
for sname, err_msg in missing_attributes.items():
|
||||
ws.append([sname, err_msg])
|
||||
|
||||
# Sheet: WARNINGS (für allgemeine Warnungen wie z-Koordinaten-Abweichungen)
|
||||
general_warnings = {k: v for k, v in warnings.items() if k != 'missing_attributes'}
|
||||
|
||||
if general_warnings:
|
||||
ws = wb.create_sheet("WARNINGS")
|
||||
ws.append(["Warning Type", "Details"])
|
||||
ws.column_dimensions['A'].width = 30
|
||||
ws.column_dimensions['B'].width = 80
|
||||
|
||||
for warning_type, warning_msg in general_warnings.items():
|
||||
# Formatiere den Warning Type lesbarer
|
||||
readable_type = warning_type.replace('_', ' ').title()
|
||||
ws.append([readable_type, str(warning_msg)])
|
||||
|
||||
def _create_bom_workbook(outpath, processed_data, bezeichner_cfg):
|
||||
"""
|
||||
Erstellt eine separate Excel-Arbeitsmappe für die Stückliste (BOM) und speichert sie ab.
|
||||
@@ -927,7 +947,7 @@ if __name__ == '__main__':
|
||||
parser.add_argument('-d', '--dxf', action='store', help='this dxf drawing will be copied and the new layer with the cables will be added. Original file must be added with --origin', metavar='myfile.dxf')
|
||||
parser.add_argument('-n', '--new', action='store', help='create a new dxf file only with cables in it. Name is basename and a timestamp')
|
||||
parser.add_argument('-x', '--excel', action='store', help='create a xlsx file with cables data', metavar='allCables.xls')
|
||||
parser.add_argument('-o', '--origin', action='store', help='name of original .dxf file used by -d and -a', metavar='original.dxf')
|
||||
parser.add_argument('-o', '--origin', action='store', help='name of original .dxf file used by -d', metavar='original.dxf')
|
||||
parser.add_argument('-l', '--local', action='store_true', help='using only local data for naming of article numbers. If not set: fetching names from SIVAS.')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -62,7 +62,7 @@ class ErrorCollector:
|
||||
all_issues = self.get_all_issues()
|
||||
write_json_file(all_issues, work_dir, filename)
|
||||
else:
|
||||
print("Keine Fehler oder Warnungen gefunden. Keine Error-Datei geschrieben.")
|
||||
print("Keine Fehler gefunden. Keine Error-Datei geschrieben.")
|
||||
|
||||
|
||||
def write_json_file(data: dict, out_dir: Path, filename: str) -> None:
|
||||
|
||||
@@ -762,6 +762,47 @@ def check_file_in_work(work_dir: Path, filename: Path) -> tuple[Path, bool]:
|
||||
mypath = filename
|
||||
return mypath, fexists
|
||||
|
||||
def check_rack_z_coordinates(res_racks: dict, error_collector, config) -> None:
|
||||
"""
|
||||
Prüft die z-Koordinaten aller Racks und gibt eine Warnung aus,
|
||||
wenn die Differenz zwischen min und max größer als der konfigurierte Schwellwert ist.
|
||||
|
||||
Args:
|
||||
res_racks: Dictionary mit Rack-Daten (key: rack_name, value: list of coordinates)
|
||||
error_collector: ErrorCollector Instanz zum Hinzufügen von Warnungen
|
||||
config: ConfigParser Objekt mit allgemein.cfg
|
||||
"""
|
||||
if not res_racks:
|
||||
return
|
||||
|
||||
# Lese Schwellwert aus Config (Standard: 2000.0 mm)
|
||||
max_height_diff = config.getfloat("Racks", "MaximalTotalHeightDifferences", fallback=2000.0)
|
||||
|
||||
all_z_coords = []
|
||||
|
||||
# Sammle alle z-Koordinaten aus allen Racks
|
||||
for rack_name, coordinates in res_racks.items():
|
||||
if isinstance(coordinates, list):
|
||||
for coord in coordinates:
|
||||
if isinstance(coord, (list, tuple)) and len(coord) >= 3:
|
||||
all_z_coords.append(coord[2])
|
||||
elif isinstance(coord, dict) and 'z' in coord:
|
||||
all_z_coords.append(coord['z'])
|
||||
|
||||
if not all_z_coords:
|
||||
return
|
||||
|
||||
# Berechne Min und Max
|
||||
min_z = min(all_z_coords)
|
||||
max_z = max(all_z_coords)
|
||||
diff = max_z - min_z
|
||||
|
||||
# Prüfe, ob Differenz > Schwellwert
|
||||
if diff > max_height_diff:
|
||||
warning_msg = f"The z-coordinates of the racks differ strong between each other from {min_z:.1f} to {max_z:.1f}"
|
||||
error_collector.add_warnings({"rack_z_coordinate_deviation": warning_msg})
|
||||
#print(f"WARNING: {warning_msg}")
|
||||
|
||||
def check_existance(res_mappings: dict, res_dist: dict, res_pos: dict, res_tunnel: dict) -> dict:
|
||||
ret = dict()
|
||||
|
||||
@@ -963,6 +1004,9 @@ if __name__ == '__main__':
|
||||
|
||||
output_results['racks'] = res_rac
|
||||
|
||||
# Prüfe z-Koordinaten der Racks auf starke Abweichungen
|
||||
check_rack_z_coordinates(res_rac, error_collector, config)
|
||||
|
||||
if args.console:
|
||||
print(to_json(res_rac))
|
||||
|
||||
|
||||
+97
-3
@@ -383,19 +383,33 @@ class ExcelConverter:
|
||||
else:
|
||||
raise ValueError("Unbekannter Export-Typ")
|
||||
|
||||
def export_excel(self, data, export_type, export_path):
|
||||
def export_excel(self, data, export_type, export_path, getpos_errors=None, getpos_warnings=None):
|
||||
"""
|
||||
Wrapper, der aufgerufen wird und in sich die Aufbereitung der Daten und das Abspeichern als Excel abruft.
|
||||
|
||||
Args:
|
||||
data: Die zu exportierenden Daten.
|
||||
export_type (str): Art des Exports (EA, TIA, WSCAD).
|
||||
export_path (str): Pfad zur Ausgabedatei.
|
||||
getpos_errors (dict, optional): Fehler-Dictionary aus getpositions.py.
|
||||
getpos_warnings (dict, optional): Warnungen-Dictionary aus getpositions.py.
|
||||
"""
|
||||
# Daten vorbereiten
|
||||
dataframes = self._prep_data(data, export_type)
|
||||
|
||||
# Excel Datei schreiben und speichern
|
||||
self._write_excel(dataframes, export_type, export_path)
|
||||
self._write_excel(dataframes, export_type, export_path, getpos_errors, getpos_warnings)
|
||||
|
||||
def _write_excel(self, dataframes, export_type, export_path):
|
||||
def _write_excel(self, dataframes, export_type, export_path, getpos_errors=None, getpos_warnings=None):
|
||||
"""
|
||||
Erzeugung der Excel-Datei im gewünschten Muster
|
||||
|
||||
Args:
|
||||
dataframes: Die vorbereiteten DataFrames für den Export.
|
||||
export_type (str): Art des Exports (EA, TIA, WSCAD).
|
||||
export_path (str): Pfad zur Ausgabedatei.
|
||||
getpos_errors (dict, optional): Fehler-Dictionary aus getpositions.py.
|
||||
getpos_warnings (dict, optional): Warnungen-Dictionary aus getpositions.py.
|
||||
"""
|
||||
if export_type == "EA":
|
||||
# Format für Simpel-Ausgabe
|
||||
@@ -413,6 +427,9 @@ class ExcelConverter:
|
||||
# Duplikate und Errors
|
||||
self._write_duplicates_errors(writer, df_gleiche_eingaenge, df_gleiche_ausgaenge, df_fehler)
|
||||
|
||||
# Fehler und Warnungen aus getpositions.py
|
||||
self._write_getpositions_errors(writer, getpos_errors, getpos_warnings)
|
||||
|
||||
elif export_type == "TIA":
|
||||
df_gleiche_eingaenge, df_gleiche_ausgaenge, df_fehler, df_consts, df_tags = dataframes
|
||||
|
||||
@@ -426,6 +443,9 @@ class ExcelConverter:
|
||||
# Duplikate und Errors
|
||||
self._write_duplicates_errors(writer, df_gleiche_eingaenge, df_gleiche_ausgaenge, df_fehler)
|
||||
|
||||
# Fehler und Warnungen aus getpositions.py
|
||||
self._write_getpositions_errors(writer, getpos_errors, getpos_warnings)
|
||||
|
||||
elif export_type in ["WSCAD", "WSCAD mit Bezug"]:
|
||||
|
||||
df_gleiche_eingaenge, df_gleiche_ausgaenge, df_fehler, df_eingaenge, df_ausgaenge = dataframes
|
||||
@@ -470,6 +490,9 @@ class ExcelConverter:
|
||||
# Duplikate und Errors
|
||||
self._write_duplicates_errors(writer, df_gleiche_eingaenge, df_gleiche_ausgaenge, df_fehler)
|
||||
|
||||
# Fehler und Warnungen aus getpositions.py
|
||||
self._write_getpositions_errors(writer, getpos_errors, getpos_warnings)
|
||||
|
||||
def _append_df(self, target_df, source_df, include_bezug=False):
|
||||
"""
|
||||
Hilfsfunktion, die Dataframes für WSCAD Export aneinander reiht und entweder Bezug inkludiert oder nicht
|
||||
@@ -515,6 +538,77 @@ class ExcelConverter:
|
||||
if not df_fehler.empty:
|
||||
df_fehler.to_excel(writer, sheet_name='Fehler', index=False)
|
||||
|
||||
def _write_getpositions_errors(self, writer, errors_dict=None, warnings_dict=None):
|
||||
"""
|
||||
Schreibt Fehler und Warnungen aus getpositions.py in separate Excel-Sheets.
|
||||
Diese Methode erstellt ähnliche Sheets wie in drawdxf.py für den getexdraw-Workflow.
|
||||
|
||||
Args:
|
||||
writer (pd.ExcelWriter): Der Excel-Writer für die Ausgabedatei.
|
||||
errors_dict (dict, optional): Dictionary mit Fehlern aus getpositions.
|
||||
warnings_dict (dict, optional): Dictionary mit Warnungen aus getpositions.
|
||||
"""
|
||||
if not errors_dict and not warnings_dict:
|
||||
return
|
||||
|
||||
# Sheet: ERR-Equipment-Connection (nur für ioconverter relevant)
|
||||
if errors_dict:
|
||||
errors_sensors = errors_dict.get('sensors_connection', [])
|
||||
errors_dists = errors_dict.get('dists_connection', [])
|
||||
|
||||
if errors_sensors or errors_dists:
|
||||
data = []
|
||||
for error in errors_sensors:
|
||||
data.append({
|
||||
"Type": "Sensor / Actuator",
|
||||
"ID": error['name'],
|
||||
"x": int(error['coords']['x']),
|
||||
"y": int(error['coords']['y'])
|
||||
})
|
||||
for error in errors_dists:
|
||||
data.append({
|
||||
"Type": "Subdistributor",
|
||||
"ID": error['name'],
|
||||
"x": int(error['coords']['x']),
|
||||
"y": int(error['coords']['y'])
|
||||
})
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
df.to_excel(writer, sheet_name='ERR-Equipment-Connection', index=False)
|
||||
|
||||
# Sheet: Duplicate IDs
|
||||
errors_duplicates = errors_dict.get('duplicate_ids', {})
|
||||
if errors_duplicates:
|
||||
data = []
|
||||
for id_name, positions in errors_duplicates.items():
|
||||
for pos in positions:
|
||||
data.append({
|
||||
"ID": id_name,
|
||||
"x": int(pos[0]),
|
||||
"y": int(pos[1])
|
||||
})
|
||||
df = pd.DataFrame(data)
|
||||
df.to_excel(writer, sheet_name='ERR-Duplicate-IDs', index=False)
|
||||
|
||||
# Sheet: Tunnel Errors
|
||||
errors_tunnels = errors_dict.get('overdefined_tunnel', [])
|
||||
if errors_tunnels:
|
||||
df = pd.DataFrame({"Tunnel ID": errors_tunnels})
|
||||
df.to_excel(writer, sheet_name='ERR-Tunnels', index=False)
|
||||
|
||||
# Sheet: Warnungen (fehlende Attribute)
|
||||
if warnings_dict:
|
||||
missing_attributes = warnings_dict.get('missing_attributes', {})
|
||||
if missing_attributes:
|
||||
data = []
|
||||
for id_name, error_msg in missing_attributes.items():
|
||||
data.append({
|
||||
"ID": id_name,
|
||||
"Error Detail": error_msg
|
||||
})
|
||||
df = pd.DataFrame(data)
|
||||
df.to_excel(writer, sheet_name='WARN-Attributes', index=False)
|
||||
|
||||
def call_portal_to_excel(self, textlines, konvertierungsart):
|
||||
|
||||
ec = ExcelConverter()
|
||||
|
||||
+52
-9
@@ -73,8 +73,47 @@ def check_file_in_work(work_dir:Path, filename:Path):
|
||||
mypath = filename
|
||||
return (mypath, fexists)
|
||||
|
||||
# Helper zum Laden der Error-JSON
|
||||
def load_error_json(json_path: Path, work_dir: Path):
|
||||
"""
|
||||
Lädt die Fehler-JSON-Datei, falls vorhanden.
|
||||
|
||||
Args:
|
||||
json_path (Path): Pfad zur Haupt-JSON-Datei.
|
||||
work_dir (Path): Arbeitsverzeichnis.
|
||||
|
||||
Returns:
|
||||
tuple: (errors_dict, warnings_dict) oder (None, None) wenn keine Fehler vorhanden.
|
||||
"""
|
||||
# Konstruiere Error-JSON-Dateinamen basierend auf der Positions-JSON
|
||||
basename = json_path.stem
|
||||
if basename.endswith("_positionsconv"):
|
||||
basename = basename.replace("_positionsconv", "")
|
||||
elif basename.endswith("_positionsdraw"):
|
||||
basename = basename.replace("_positionsdraw", "")
|
||||
|
||||
error_json_name = f"{basename}_errors.json"
|
||||
error_json_path = work_dir / error_json_name
|
||||
|
||||
# Prüfen ob Error-JSON existiert
|
||||
if not error_json_path.exists():
|
||||
# Auch im gleichen Verzeichnis wie die Positions-JSON prüfen
|
||||
error_json_path = json_path.parent / error_json_name
|
||||
if not error_json_path.exists():
|
||||
return None, None
|
||||
|
||||
# Error-JSON laden
|
||||
try:
|
||||
error_data = load_json(error_json_path)
|
||||
errors_dict = error_data.get("errors", None)
|
||||
warnings_dict = error_data.get("warnings", None)
|
||||
return errors_dict, warnings_dict
|
||||
except Exception as e:
|
||||
print(f"Warnung: Fehler beim Laden der Error-JSON: {e}")
|
||||
return None, None
|
||||
|
||||
# Helper für Export
|
||||
def do_export(conv_obj:ExcelConverter, mode_str, base_input, output_dir, lines):
|
||||
def do_export(conv_obj:ExcelConverter, mode_str, base_input, output_dir, lines, getpos_errors=None, getpos_warnings=None):
|
||||
# Für WSCAD Dateinamen anpassen (Export-Typ bleibt "WSCAD" oder "WSCAD mit Bezug")
|
||||
if mode_str == "WSCAD":
|
||||
if args.no_bezug:
|
||||
@@ -90,19 +129,19 @@ def do_export(conv_obj:ExcelConverter, mode_str, base_input, output_dir, lines):
|
||||
export_path = os.path.join(output_dir, filename)
|
||||
|
||||
try:
|
||||
conv_obj.export_excel(lines, export_type, export_path)
|
||||
conv_obj.export_excel(lines, export_type, export_path, getpos_errors, getpos_warnings)
|
||||
print(f"Export '{export_type}' erfolgreich: {export_path}")
|
||||
except Exception as e:
|
||||
print(f"Fehler bei Export '{export_type}': {e}")
|
||||
|
||||
def do_exports(converter, export_mode, output_dir, base_input, lines):
|
||||
def do_exports(converter, export_mode, output_dir, base_input, lines, getpos_errors=None, getpos_warnings=None):
|
||||
if export_mode:
|
||||
do_export(converter, export_mode, base_input, output_dir, lines)
|
||||
do_export(converter, export_mode, base_input, output_dir, lines, getpos_errors, getpos_warnings)
|
||||
else:
|
||||
# Kein Modus → alle drei exportieren
|
||||
do_export(converter, "EA", base_input, output_dir, lines)
|
||||
do_export(converter, "TIA", base_input, output_dir, lines)
|
||||
do_export(converter, "WSCAD", base_input, output_dir, lines)
|
||||
do_export(converter, "EA", base_input, output_dir, lines, getpos_errors, getpos_warnings)
|
||||
do_export(converter, "TIA", base_input, output_dir, lines, getpos_errors, getpos_warnings)
|
||||
do_export(converter, "WSCAD", base_input, output_dir, lines, getpos_errors, getpos_warnings)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Excel-Konverter für BMK-Daten")
|
||||
@@ -176,12 +215,16 @@ if __name__ == "__main__":
|
||||
|
||||
rawdata = load_json(json_path)
|
||||
(dsensors, lsensors) = prepare_data(rawdata)
|
||||
|
||||
# Fehler-JSON laden (falls vorhanden)
|
||||
getpos_errors, getpos_warnings = load_error_json(json_path, work_dir)
|
||||
|
||||
if len(dsensors) == 0:
|
||||
do_exports(converter, export_mode, output_dir, basename_out, lsensors)
|
||||
do_exports(converter, export_mode, output_dir, basename_out, lsensors, getpos_errors, getpos_warnings)
|
||||
else:
|
||||
for sps_nr, lines in dsensors.items():
|
||||
name = f"{basename_out}-{sps_nr}"
|
||||
do_exports(converter, export_mode, output_dir, name, lines)
|
||||
do_exports(converter, export_mode, output_dir, name, lines, getpos_errors, getpos_warnings)
|
||||
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
+8
-2
@@ -161,8 +161,12 @@ def prepare_data(rawdata:dict):
|
||||
if "missing_attributes" in rawdata["not_found"]:
|
||||
errors_attributes = rawdata["not_found"]["missing_attributes"]
|
||||
|
||||
# Warnungen aus getpositions übernehmen
|
||||
warnings = dict()
|
||||
if "warnings" in rawdata:
|
||||
warnings = rawdata["warnings"]
|
||||
|
||||
return (dracks, dsensors, dsubdists, mapping, dtunnels, dtunlength, errors_dists, errors_sensors, errors_attributes)
|
||||
return (dracks, dsensors, dsubdists, mapping, dtunnels, dtunlength, errors_dists, errors_sensors, errors_attributes, warnings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -188,7 +192,7 @@ if __name__ == "__main__":
|
||||
|
||||
# Einlesen und Vorbereiten der Daten
|
||||
rawdata = load_json(sensors_path)
|
||||
(racks, sensors, subdists, mapping, tunnels, tunlength, errors_dists, errors_sensors, errors_attributes) = prepare_data(rawdata)
|
||||
(racks, sensors, subdists, mapping, tunnels, tunlength, errors_dists, errors_sensors, errors_attributes, warnings) = prepare_data(rawdata)
|
||||
|
||||
config = configparser.ConfigParser(allow_no_value=True, delimiters=("="))
|
||||
config.optionxform = lambda optionstr: optionstr # preserve case for letters
|
||||
@@ -212,6 +216,8 @@ if __name__ == "__main__":
|
||||
cable_paths["errors_dists_not_in_layout"] = errors_dists
|
||||
cable_paths["errors_sensors_not_in_layout"] = errors_sensors
|
||||
cable_paths["errors_missing_attributes"] = errors_attributes
|
||||
# Warnungen aus getpositions übertragen
|
||||
cable_paths["warnings"] = warnings
|
||||
write_results(to_json(cable_paths), work_dir, f"{basename}.json")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user