Schalter --show-missing dazu gebaut

This commit is contained in:
2026-07-08 16:24:14 +02:00
parent f320551b52
commit 82d93b6b68
2 changed files with 62 additions and 1 deletions
+19
View File
@@ -92,6 +92,7 @@ bin\awl2scl_deep.bat --dir . --category A
| `--force` | vorhandene **generierte** `.scl` überschreiben |
| `--tochange <pfad>` | toChange-Liste (Default `<Projekt>\data\globals2params_tochange.json`) |
| `--log <pfad>` | Log-Datei |
| `--show-missing` | am Ende alle nicht übersetzten Bausteine (manuelle Prüfung + Fehler) mit Grund auf der Konsole auflisten |
`--improve-gotos` und `--globals2params` implizieren `--force` (Regenerierung).
Der Zielpfad je Baustein ist `<target-dir>\<Station>\<Name>.scl`.
@@ -315,3 +316,21 @@ Jeder Lauf schreibt `log\awl2scl_<Zeitstempel>.log` mit:
- **Fehler**.
Bei `--globals2params` zusätzlich der Pfad der toChange-Liste.
Mit `--show-missing` wird am Ende zusätzlich auf der **Konsole** (nicht nur im
Log) eine kompakte Liste aller nicht übersetzten Bausteine ausgegeben — sowohl
`manuelle Prüfung nötig` als auch echte `Fehler`, je mit Grund:
```text
======================================================================
Nicht uebersetzt (2)
======================================================================
MANUELL [B] =A01+UH06-KF00\Programmbausteine\Anlage\AMR_Bereich.awl
Grund: Timer-Mnemonik SE nicht unterstuetzt
MANUELL [C] =A01+UH06-KF00\Programmbausteine\Module\Set_Input.awl
Grund: registerindirekter/zeigerbasierter Operand: = E[ #AR1Zeiger]
```
Bausteine, die nur wegen falscher Kategorie oder als reiner Datenbaustein
übersprungen wurden, zählen nicht als „nicht übersetzt" (die stehen bereits
in den eigenen Log-Abschnitten).
+43 -1
View File
@@ -62,7 +62,11 @@ class ParsedBlock:
class NotABlockError(Exception):
"""Datei ist keine Baustein-AWL (z.B. leere/fremde Datei)."""
"""Datei ist keine uebersetzbare Baustein-AWL (z.B. leere/fremde Datei)."""
class DataBlockError(NotABlockError):
"""Reiner Datenbaustein (DB) ohne Programmlogik -- nichts zu uebersetzen."""
# ---------------------------------------------------------------------------
@@ -138,6 +142,10 @@ def detect_format(text):
# ---------------------------------------------------------------------------
HEADER_RE = re.compile(r"^===\s*(\w+)\s+(\S*):\s*(.+?)\s*\[STL\]\s*===\s*$")
# Kopfzeile mit beliebigem Sprach-/Typ-Tag (z.B. "[DB]") -- fuer die Erkennung
# reiner Datenbausteine, die kein STL enthalten.
ANY_HEADER_RE = re.compile(r"^===\s*(\w+)\s+(\S*):\s*(.+?)\s*\[(\w+)\]\s*===\s*$")
DATA_BLOCK_TYPES = {"GlobalDB", "InstanceDB", "DB", "ArrayDB"}
NW_RE = re.compile(r"^---\s*NW\s*\d+:\s*(.*?)\s*---\s*$")
STMT_RE = re.compile(r"^(?:(\S+:)\s+)?(\S+)(?:\s+(.*))?$")
MEMBER_RE = re.compile(r'^(\s*)(\S+|"[^"]+")\s*:\s*(\S+)(?:\s*:=\s*(\S+))?\s*(?:$|(?<=\S)\s{2,}//\s*(.*)$)')
@@ -148,6 +156,10 @@ def parse_rendered(text):
i = 0
m = HEADER_RE.match(lines[0].strip())
if not m:
any_m = ANY_HEADER_RE.match(lines[0].strip())
if any_m and (any_m.group(4) == "DB" or any_m.group(1) in DATA_BLOCK_TYPES):
raise DataBlockError(
f"Datenbaustein ({any_m.group(1)} {any_m.group(2)}: {any_m.group(3)}) - keine Logik")
raise NotABlockError("Kopfzeile nicht erkannt (gerendertes Format)")
btype, num, name = m.group(1), m.group(2), m.group(3)
i = 1
@@ -274,6 +286,9 @@ def parse_native(text):
i = 0
mh = NATIVE_HEADER_RE.match(lines[0].strip())
if not mh:
mdb = re.match(r'^DATA_BLOCK\s+"([^"]+)"', lines[0].strip())
if mdb:
raise DataBlockError(f"Datenbaustein (DATA_BLOCK {mdb.group(1)}) - keine Logik")
raise NotABlockError("Kopfzeile nicht erkannt (natives Format)")
btype = {"FUNCTION_BLOCK": "FB", "FUNCTION": "FC", "ORGANIZATION_BLOCK": "OB"}[mh.group(1)]
name = mh.group(2)
@@ -1007,6 +1022,9 @@ def _is_generated(scl_path):
def process_file(awl_path, requested_categories, target_dir, console, opts):
try:
parsed = parse_awl_file(awl_path)
except DataBlockError as exc:
console(f"UEBERSPRUNGEN [Datenbaustein, keine Logik] {awl_path}: {exc}")
return Result(awl_path, Outcome.SKIPPED_CATEGORY, reason=f"Datenbaustein (no logic): {exc}")
except NotABlockError as exc:
console(f"UEBERSPRUNGEN [kein Baustein] {awl_path}: {exc}")
return Result(awl_path, Outcome.SKIPPED_CATEGORY, reason=str(exc))
@@ -1203,6 +1221,9 @@ def main(argv=None):
parser.add_argument("--tochange", type=Path, default=None,
help="Pfad der toChange-Liste (Default: <Projekt>/data/globals2params_tochange.json)")
parser.add_argument("--log", type=Path, help="Pfad der Log-Datei")
parser.add_argument("--show-missing", action="store_true",
help="Am Ende alle nicht uebersetzten Bausteine (manuelle Pruefung + Fehler) "
"mit Grund auf der Konsole auflisten")
args = parser.parse_args(argv)
targets = list(args.files)
@@ -1252,8 +1273,29 @@ def main(argv=None):
if args.globals2params and store is not None:
print(f"toChange-Liste: {store.path}")
print(f"Log: {log_path}")
if args.show_missing:
print_missing(results)
return 1 if n_err else 0
def print_missing(results):
"""Listet alle nicht uebersetzten Bausteine (manuelle Pruefung + Fehler) mit Grund auf."""
missing = [r for r in results if r.outcome in (Outcome.MANUAL_REVIEW, Outcome.ERROR)]
print()
print("=" * 70)
print(f"Nicht uebersetzt ({len(missing)})")
print("=" * 70)
if not missing:
print("(keine)")
return
for r in sorted(missing, key=lambda r: str(r.path)):
tag = "MANUELL" if r.outcome == Outcome.MANUAL_REVIEW else "FEHLER "
cat = f"[{r.category}] " if r.category else ""
print(f"{tag} {cat}{r.path}")
print(f" Grund: {r.reason}")
if __name__ == "__main__":
sys.exit(main())