From 643164bc533d8cc4ef51b6a0d15103d95aa96284 Mon Sep 17 00:00:00 2001 From: Simon Hensch Date: Thu, 15 Oct 2026 10:33:19 +0200 Subject: [PATCH] =?UTF-8?q?[CHANGE]=20St=C3=BCcklistenpositionierung=20in?= =?UTF-8?q?=20der=20Zeichnung=20mit=20Markierung=20und=20Tabelle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dcl/stueckliste.dcl | 38 +++++ tests/test_stueckliste.lsp | 310 +++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 dcl/stueckliste.dcl create mode 100644 tests/test_stueckliste.lsp diff --git a/dcl/stueckliste.dcl b/dcl/stueckliste.dcl new file mode 100644 index 0000000..4349fab --- /dev/null +++ b/dcl/stueckliste.dcl @@ -0,0 +1,38 @@ +// ============================================================ +// stueckliste.dcl - Optionen fuer Positionen + Stueckliste +// Aufruf ueber c:SSG_FAEHNCHEN_STUECKLISTE (Lisp/ssg_stueckliste.lsp, +// ssg-sl-dialog). Vorbelegung aus cfg/stueckliste.json bzw. dem letzten Lauf. +// ============================================================ + +stueckliste : dialog { + label = "Positionen + Stueckliste"; + + : boxed_column { + label = "Layer der Positionen"; + : radio_column { + key = "modus"; + : radio_button { key = "m_suffix"; label = "Bauteil-Layer + Suffix"; } + : radio_button { key = "m_fest"; label = "Fester Layer"; } + } + : edit_box { key = "suffix"; label = "Suffix:"; edit_width = 20; } + : edit_box { key = "fest"; label = "Fester Layer:"; edit_width = 20; } + : text { label = "Regeln je Bauteiltyp: cfg/stueckliste.json"; } + } + + : boxed_column { + label = "Stueckliste"; + : edit_box { key = "tab_layer"; label = "Tabellen-Layer:"; edit_width = 20; } + : edit_box { key = "titel"; label = "Titel:"; edit_width = 30; } + } + + : boxed_column { + label = "Nummerierung / Darstellung"; + : edit_box { key = "startnr"; label = "Erste Positionsnummer:"; edit_width = 10; } + : edit_box { key = "faktor"; label = "Massstabsfaktor:"; edit_width = 10; } + : toggle { key = "alle_pos"; + label = "Alle Positionen in der Fahne (100|101|...), jede Art.-Nr. nummeriert"; } + } + + : errtile { key = "error"; } + ok_cancel; +} diff --git a/tests/test_stueckliste.lsp b/tests/test_stueckliste.lsp new file mode 100644 index 0000000..f2b9106 --- /dev/null +++ b/tests/test_stueckliste.lsp @@ -0,0 +1,310 @@ +;; ============================================================ +;; test_stueckliste.lsp - Szenario-Test fuer SSG_FAEHNCHEN_STUECKLISTE +;; (Lisp/ssg_stueckliste.lsp) +;; +;; Baut GR_-artige Bloecke per entmake (Saeulen mit Mehrfach-ARTINR, +;; Traeger mit Regel "mitte" auf einem anderen Layer, ein Block auf +;; gefrorenem Layer, einer ohne ARTINR, einer OHNE Attribute direkt vor +;; einem mit ARTINR) und fuehrt mehrere UNABHAENGIGE Laeufe ueber +;; ssg-sl-vorbereiten / ssg-sl-ausfuehren (ohne Dialog) aus: +;; Lauf 1 Standard (Baugruppen): erste Art.-Nr. nummeriert, Unterzeilen +;; ohne Nummer, Nummern ueber mehrere Layer, Zielpunkte, Rahmen +;; Lauf 2 alle Teile gewaehlt: schon positionierte werden uebersprungen, +;; Startnummer fortlaufend, eigene Tabelle rechts neben der ersten +;; Lauf 3 nur schon positionierte Teile: nichts zu tun +;; Lauf 4 "alle Positionen in der Fahne": "a|b|c", jede Art.-Nr. nummeriert +;; Lauf 5 Tabelle scheitert: Fahnen dieses Laufs werden zurueckgenommen +;; Lauf 6 gesperrter Ziel-Layer wird vor jeder Aenderung gemeldet +;; Je Lauf wird geprueft, dass jede Fahnennummer in SEINER Tabelle steht. +;; Artikelkatalog und Regeln kommen aus den echten Dateien +;; (data/json/geruest*.json, cfg/stueckliste.json). +;; +;; Aufruf in BricsCAD: +;; (load (strcat (getenv "DXFMAKRO") "/tests/test_stueckliste.lsp")) +;; TEST_STUECKLISTE +;; +;; Speichert tests/output/stueckliste_results.json (Konvention test_run_all). +;; ============================================================ + +(setq *tsl-pass* 0 *tsl-fail* 0 *tsl-fails* '()) + +(defun tsl-eq (name erwartet ist) + (if (equal erwartet ist 1e-6) + (progn (setq *tsl-pass* (1+ *tsl-pass*)) + (princ (strcat "\n [PASS] " name))) + (progn (setq *tsl-fail* (1+ *tsl-fail*)) + (setq *tsl-fails* (cons name *tsl-fails*)) + (princ (strcat "\n [FAIL] " name + "\n erwartet: " (vl-princ-to-string erwartet) + "\n ist: " (vl-princ-to-string ist)))))) + +;; Blockdefinition mit ATTDEFs ARTINR/LAENGE/TEILEART (falls noch nicht da). +(defun tsl-blockdef (name) + (if (not (tblsearch "BLOCK" name)) + (progn + (entmake (list '(0 . "BLOCK") (cons 2 name) '(70 . 2) '(10 0.0 0.0 0.0))) + (entmake '((0 . "LINE") (8 . "0") (10 0.0 0.0 0.0) (11 100.0 0.0 0.0))) + (foreach tag '("ARTINR" "LAENGE" "TEILEART") + (entmake (list '(0 . "ATTDEF") '(8 . "0") '(10 0.0 0.0 0.0) + '(40 . 10.0) '(1 . "") (cons 2 tag) (cons 3 tag) + '(70 . 0)))) + (entmake '((0 . "ENDBLK"))))) + name) + +;; Blockdefinition OHNE Attribute (wie "pfeilrot"/"ssg-stop" in Kundenzeichnungen) +(defun tsl-blockdef-leer (name) + (if (not (tblsearch "BLOCK" name)) + (progn + (entmake (list '(0 . "BLOCK") (cons 2 name) '(70 . 0) '(10 0.0 0.0 0.0))) + (entmake '((0 . "LINE") (8 . "0") (10 0.0 0.0 0.0) (11 50.0 50.0 0.0))) + (entmake '((0 . "ENDBLK"))))) + name) + +;; INSERT mit Attributen per entmake. Rueckgabe: ename des INSERT. +(defun tsl-insert (name layer pt rot werte / ins) + (entmake (list '(0 . "INSERT") (cons 8 layer) (cons 2 name) (cons 10 pt) + (cons 50 rot) '(66 . 1))) + (setq ins (entlast)) + (foreach paar werte + (entmake (list '(0 . "ATTRIB") (cons 8 layer) (cons 10 pt) '(40 . 10.0) + (cons 1 (cdr paar)) (cons 2 (car paar)) '(70 . 0)))) + (entmake '((0 . "SEQEND"))) + ins) + +(defun tsl-saeule-werte () + '(("ARTINR" . "821006051,821004201,825002022,825002022") + ("LAENGE" . "2500") ("TEILEART" . "Geruest Saeule 2 Zoll"))) + +(defun tsl-traeger-werte () + '(("ARTINR" . "825084000") ("LAENGE" . "2305") ("TEILEART" . "Geruest Traeger"))) + +(defun tsl-handle (e) (cdr (assoc 5 (entget e)))) + +;; Fahne zu einem Block (oder nil). +(defun tsl-fahne-zu (blk / erg) + (foreach e (ssg-sl-fahnen) + (if (equal (ssg-sl-xdata-wert e *ssg-sl-app-pos* 1005) (tsl-handle blk)) (setq erg e))) + erg) + +;; Erster Leader-Punkt (Zielpunkt) einer Fahne. +(defun tsl-zielpunkt (e / l) + (setq l (vlax-safearray->list + (vlax-variant-value + (vla-GetLeaderLineVertices (vlax-ename->vla-object e) 0)))) + (list (nth 0 l) (nth 1 l) (nth 2 l))) + +(defun tsl-text (e) (vla-get-TextString (vlax-ename->vla-object e))) + +;; Tabelle eines Laufs (VLA) oder nil. +(defun tsl-tabelle (lauf / e) + (setq e (car (ssg-sl-des-laufs (ssg-sl-tabellen) *ssg-sl-app-tab* lauf))) + (if e (vlax-ename->vla-object e))) + +;; Inhalt einer Tabellenzeile als Liste von 4 Strings (Pos Menge Laenge Art.-Nr.) +(defun tsl-zeile (tbo r) + (list (vla-GetText tbo r 0) (vla-GetText tbo r 1) (vla-GetText tbo r 2) (vla-GetText tbo r 3))) + +;; T, wenn jede Nummer jeder Fahne des Laufs in der Tabelle des Laufs steht. +(defun tsl-lauf-konsistent-p (lauf / tbo r nummern ok) + (setq tbo (tsl-tabelle lauf) ok (if tbo T nil)) + (if tbo + (progn + (setq r 2) + (repeat (- (vla-get-Rows tbo) 2) + (setq nummern (cons (vla-GetText tbo r 0) nummern) r (1+ r))) + (foreach e (ssg-sl-des-laufs (ssg-sl-fahnen) *ssg-sl-app-pos* lauf) + (foreach nr (ssg-sl-text-nummern (tsl-text e)) + (if (not (member (itoa nr) nummern)) (setq ok nil)))))) + ok) + +;; Lauf wie der Befehl, aber ohne Dialog: Optionen aus cfg (+ Aenderungen), +;; Startnummer = naechste freie. Rueckgabe: (plan erg) +(defun tsl-lauf (enames cfg aenderungen pt / opt plan) + (setq opt (ssg-sl-optionen-start cfg (ssg-sl-letzte-nummer))) + (setq opt (ssg-sl-opt-setzen opt "titel" "TSL_TITEL")) + (foreach a aenderungen (setq opt (ssg-sl-opt-setzen opt (car a) (cdr a)))) + (setq plan (ssg-sl-vorbereiten enames opt cfg)) + (list plan (if (ssg-sl-opt plan "recs") (ssg-sl-ausfuehren plan opt cfg pt)))) + +(defun stueckliste:tests ( / saeule traeger pfeil s1 s2 s3 s4 s5 t1 t2 t3 t4 pf fz kein + elemente cfg lauf plan erg e tbo1 vorher mn mx rand opt) + (setvar "CMDECHO" 0) + (setq *ssg-sl-katalog* nil *ssg-sl-optionen* nil) + (ssg-make-layer "D-1" "7" nil) + (ssg-make-layer "D-5" "7" nil) + (ssg-make-layer "Z-1" "7" nil) + (ssg-make-layer "TSL_FROZEN" "7" nil) + (setq saeule (tsl-blockdef "GR_Saeulen_TSL1") + traeger (tsl-blockdef "GR_Traeger_TSL1") + pfeil (tsl-blockdef-leer "TSL_PFEILROT")) + ;; 3 Saeulen R 2" auf D-1, Z=6000. VOR der zweiten liegt ein Block OHNE + ;; Attribute - die Reihenfolge, in der "pfeilrot" frueher die ARTINR des + ;; Folgeblocks erbte. + (setq s1 (tsl-insert saeule "D-1" '(0.0 0.0 6000.0) 0.0 (tsl-saeule-werte))) + (entmake (list '(0 . "INSERT") '(8 . "Z-1") (cons 2 pfeil) '(10 1500.0 0.0 0.0))) + (setq pf (entlast)) + (setq s2 (tsl-insert saeule "D-1" '(3000.0 0.0 6000.0) 0.0 (tsl-saeule-werte))) + (setq s3 (tsl-insert saeule "D-1" '(6000.0 0.0 6000.0) 0.0 (tsl-saeule-werte))) + ;; Traeger DRT 80, 2305 mm, auf einem ANDEREN Layer (D-5) + (setq t1 (tsl-insert traeger "D-5" '(0.0 1000.0 6000.0) 0.0 (tsl-traeger-werte))) + (setq t2 (tsl-insert traeger "D-5" '(10000.0 0.0 6000.0) (/ pi 2.0) (tsl-traeger-werte))) + ;; nicht positionierbar: gefrorener Layer bzw. leeres ARTINR + (setq fz (tsl-insert traeger "TSL_FROZEN" '(0.0 5000.0 0.0) 0.0 + '(("ARTINR" . "825084000") ("LAENGE" . "1000")))) + (setq kein (tsl-insert traeger "D-1" '(0.0 6000.0 0.0) 0.0 + '(("ARTINR" . "") ("LAENGE" . "1000")))) + (command "_.-LAYER" "_F" "TSL_FROZEN" "") + + ;; --- Block ohne Attribute: nichts lesen, nichts schreiben --- + (tsl-eq "ohne attribute: ssg-ent-hat-attribute-p" nil (ssg-ent-hat-attribute-p pf)) + (tsl-eq "ohne attribute: ssg-attrib-read leer" nil (ssg-attrib-read pf)) + (tsl-eq "ohne attribute: csv:get-attrib leer" nil (csv:get-attrib pf "ARTINR")) + (tsl-eq "mit attributen: ssg-ent-hat-attribute-p" T (ssg-ent-hat-attribute-p s2)) + (setq vorher (ssg-attrib-read s2)) + (ssg-attrib-set-on pf '(("LAENGE" . "9999"))) + (tsl-eq "ohne attribute: set-on aendert folgeblock nicht" vorher (ssg-attrib-read s2)) + + ;; --- Auswahl: ohne Attribute, gefroren und ohne ARTINR fallen weg --- + (setq elemente (ssg-sl-elemente-aus-ss + (ssget "_X" (list '(0 . "INSERT") (cons 410 (getvar "CTAB")))))) + (tsl-eq "auswahl: 5 positionierbare bloecke" 5 (length elemente)) + (tsl-eq "auswahl: block ohne attribute ausgelassen" nil (if (member pf elemente) T nil)) + (tsl-eq "auswahl: gefroren ausgelassen" nil (if (member fz elemente) T nil)) + (tsl-eq "auswahl: ohne artinr ausgelassen" nil (if (member kein elemente) T nil)) + (setq cfg (ssg-sl-cfg-laden)) + + ;; ============ Lauf 1: Standard, Saeulen + ein Traeger ============ + (tsl-eq "lauf 1: noch keine nummern" nil (ssg-sl-letzte-nummer)) + (setq erg (tsl-lauf (list s1 s2 s3 t1) cfg nil '(20000.0 0.0 0.0))) + (setq plan (car erg) lauf (ssg-sl-opt plan "lauf")) + (tsl-eq "lauf 1: ok" T (ssg-sl-opt (cadr erg) "ok")) + (tsl-eq "lauf 1: zeilen (unterzeilen ohne nummer)" + '((100 "821006051" "2500" 3) (nil "821004201" "2500" 3) + (nil "825002022" "" 6) (101 "825084000" "2305" 1)) + (mapcar '(lambda (z) (list (nth 0 z) (nth 1 z) (nth 2 z) (nth 3 z))) + (ssg-sl-opt plan "zeilen"))) + (tsl-eq "lauf 1: 4 fahnen" 4 (ssg-sl-opt (cadr erg) "fahnen")) + (tsl-eq "lauf 1: konsistent" T (tsl-lauf-konsistent-p lauf)) + (setq e (tsl-fahne-zu s2)) + (tsl-eq "saeule: nummer der ersten art.-nr." "100" (tsl-text e)) + (tsl-eq "saeule: layer D-1-POS" "D-1-POS" (cdr (assoc 8 (entget e)))) + (tsl-eq "saeule: zielpunkt einfuegepunkt" '(3000.0 0.0 6000.0) (tsl-zielpunkt e)) + (tsl-eq "fahne: rahmen um den text" :vlax-true + (vla-get-TextFrameDisplay (vlax-ename->vla-object e))) + (tsl-eq "fahne: textstil Pos" "Pos" (vla-get-TextStyleName (vlax-ename->vla-object e))) + (setq e (tsl-fahne-zu t1)) + (tsl-eq "traeger: nummer fortlaufend ueber layer" "101" (tsl-text e)) + (tsl-eq "traeger: layer D-5-POS" "D-5-POS" (cdr (assoc 8 (entget e)))) + (tsl-eq "traeger 0 grad: zielpunkt mitte" '(1152.5 1000.0 6000.0) (tsl-zielpunkt e)) + (tsl-eq "block ohne attribute: keine fahne" nil (tsl-fahne-zu pf)) + (setq tbo1 (tsl-tabelle lauf)) + (tsl-eq "lauf 1: tabelle zeilen" 6 (vla-get-Rows tbo1)) + (tsl-eq "lauf 1: titel" "TSL_TITEL" (vla-GetText tbo1 0 0)) + (tsl-eq "lauf 1: kopf laenge" (strcat "L" (chr 228) "nge") (vla-GetText tbo1 1 2)) + (tsl-eq "lauf 1: baugruppenzeile" '("100" "3" "2500" "821006051") (tsl-zeile tbo1 2)) + (tsl-eq "lauf 1: unterzeile ohne nummer" '("" "3" "2500" "821004201") (tsl-zeile tbo1 3)) + (tsl-eq "lauf 1: anbauteil ohne nummer/laenge" '("" "6" "" "825002022") (tsl-zeile tbo1 4)) + (tsl-eq "lauf 1: tabellen-layer" (cdr (assoc "tabellen_layer" cfg)) (vla-get-Layer tbo1)) + + ;; ============ Lauf 2: alle gewaehlt, positionierte uebersprungen ============ + (tsl-eq "lauf 2: letzte nummer" 101 (ssg-sl-letzte-nummer)) + (setq opt (ssg-sl-optionen-start cfg (ssg-sl-letzte-nummer))) + (tsl-eq "lauf 2: startnummer-vorschlag" 102 (ssg-sl-opt opt "startnr")) + (setq rand (ssg-sl-tabellen-vorschlag 500.0)) + (vla-GetBoundingBox tbo1 'mn 'mx) + (tsl-eq "lauf 2: tabellenvorschlag rechts neben lauf 1" T + (if (and rand (> (car rand) (car (vlax-safearray->list mx)))) T nil)) + (setq erg (tsl-lauf elemente cfg nil rand)) + (setq plan (car erg) lauf (ssg-sl-opt plan "lauf")) + (tsl-eq "lauf 2: 4 uebersprungen" 4 (ssg-sl-opt plan "uebersprungen")) + (tsl-eq "lauf 2: nur der neue traeger" 1 (length (ssg-sl-opt plan "recs"))) + (tsl-eq "lauf 2: nummer fortlaufend" "102" (tsl-text (tsl-fahne-zu t2))) + (tsl-eq "traeger 90 grad: zielpunkt mitte" '(10000.0 1152.5 6000.0) + (tsl-zielpunkt (tsl-fahne-zu t2))) + (tsl-eq "lauf 2: konsistent" T (tsl-lauf-konsistent-p lauf)) + (tsl-eq "lauf 2: zwei tabellen" 2 (length (ssg-sl-tabellen))) + (tsl-eq "lauf 2: lauf 1 unveraendert" "100" (tsl-text (tsl-fahne-zu s2))) + (tsl-eq "lauf 2: 5 fahnen gesamt" 5 (length (ssg-sl-fahnen))) + + ;; ============ Lauf 3: nur schon positionierte Teile ============ + (setq erg (tsl-lauf (list s1 t2) cfg nil '(0.0 -9000.0 0.0))) + (tsl-eq "lauf 3: nichts zu tun" nil (ssg-sl-opt (car erg) "recs")) + (tsl-eq "lauf 3: keine weitere tabelle" 2 (length (ssg-sl-tabellen))) + + ;; ============ Lauf 4: alle Positionen in der Fahne ============ + (setq s4 (tsl-insert saeule "D-1" '(0.0 20000.0 0.0) 0.0 (tsl-saeule-werte))) + (setq s5 (tsl-insert saeule "D-1" '(3000.0 20000.0 0.0) 0.0 (tsl-saeule-werte))) + (setq t3 (tsl-insert traeger "D-5" '(0.0 22000.0 0.0) 0.0 (tsl-traeger-werte))) + (setq erg (tsl-lauf (list s4 s5 t3) cfg (list (cons "alle_pos" T)) + (ssg-sl-tabellen-vorschlag 500.0))) + (setq plan (car erg) lauf (ssg-sl-opt plan "lauf")) + (tsl-eq "lauf 4: jede art.-nr. nummeriert" + '((103 "821004201" "2500" 2) (104 "821006051" "2500" 2) + (105 "825002022" "" 4) (106 "825084000" "2305" 1)) + (mapcar '(lambda (z) (list (nth 0 z) (nth 1 z) (nth 2 z) (nth 3 z))) + (ssg-sl-opt plan "zeilen"))) + (tsl-eq "lauf 4: fahne mit allen nummern" "104|103|105" (tsl-text (tsl-fahne-zu s4))) + (tsl-eq "lauf 4: traeger" "106" (tsl-text (tsl-fahne-zu t3))) + (tsl-eq "lauf 4: konsistent" T (tsl-lauf-konsistent-p lauf)) + (tsl-eq "lauf 4: drei tabellen" 3 (length (ssg-sl-tabellen))) + (tsl-eq "lauf 4: letzte nummer" 106 (ssg-sl-letzte-nummer)) + + ;; ============ Lauf 5: Tabelle scheitert -> Lauf zurueckgenommen ============ + (setq t4 (tsl-insert traeger "D-5" '(0.0 30000.0 0.0) 0.0 (tsl-traeger-werte))) + (setq erg (tsl-lauf (list t4) cfg nil nil)) + (tsl-eq "lauf 5: nicht ok" nil (ssg-sl-opt (cadr erg) "ok")) + (tsl-eq "lauf 5: keine fahne fuer t4" nil (tsl-fahne-zu t4)) + (tsl-eq "lauf 5: fahnen unveraendert" 8 (length (ssg-sl-fahnen))) + (tsl-eq "lauf 5: tabellen unveraendert" 3 (length (ssg-sl-tabellen))) + + ;; ============ Lauf 6: gesperrter Ziel-Layer ============ + (command "_.-LAYER" "_LO" "D-5-POS" "") + (setq opt (ssg-sl-optionen-start cfg (ssg-sl-letzte-nummer)) + plan (ssg-sl-vorbereiten (list t4) opt cfg)) + (tsl-eq "lauf 6: gesperrter layer gemeldet" '("D-5-POS") (ssg-sl-gesperrte-layer plan opt)) + (command "_.-LAYER" "_U" "D-5-POS" "") + ;; t4 ist nach dem gescheiterten Lauf weiter positionierbar + (setq erg (tsl-lauf (list t4) cfg nil (ssg-sl-tabellen-vorschlag 500.0))) + (tsl-eq "lauf 6: t4 danach positioniert" "107" (tsl-text (tsl-fahne-zu t4))) + (tsl-eq "lauf 6: konsistent" T (tsl-lauf-konsistent-p (ssg-sl-opt (car erg) "lauf"))) + (princ)) + + +(defun stueckliste:export-results (out-dir / f pfad) + (setq pfad (strcat out-dir "/stueckliste_results.json")) + (setq f (open pfad "w")) + (if f + (progn + (write-line "{" f) + (write-line (strcat " \"pass\": " (itoa *tsl-pass*) ",") f) + (write-line (strcat " \"fail\": " (itoa *tsl-fail*) ",") f) + (write-line (strcat " \"status\": \"" + (if (= *tsl-fail* 0) "OK" "FEHLER") "\"") f) + (write-line "}" f) + (close f) + (princ (strcat "\n Ergebnis: " pfad)))) + (princ)) + + +(defun c:TEST_STUECKLISTE ( / res) + (setq *tsl-pass* 0 *tsl-fail* 0 *tsl-fails* '()) + (princ "\n================================================================") + (princ "\n TEST_STUECKLISTE - Positionen + Stueckliste (ssg_stueckliste.lsp)") + (princ "\n================================================================") + (setq res (vl-catch-all-apply 'stueckliste:tests nil)) + (if (vl-catch-all-error-p res) + (progn (setq *tsl-fail* (1+ *tsl-fail*)) + (setq *tsl-fails* (cons (strcat "harter Fehler: " (vl-catch-all-error-message res)) + *tsl-fails*)))) + (princ "\n\n----------------------------------------------------------------") + (princ (strcat "\n Ergebnis: " (itoa *tsl-pass*) " PASS, " + (itoa *tsl-fail*) " FAIL")) + (if (> *tsl-fail* 0) + (progn + (princ "\n Fehlgeschlagen:") + (foreach n (reverse *tsl-fails*) (princ (strcat "\n - " n))))) + (princ "\n================================================================") + (princ)) + +(princ "\nTEST_STUECKLISTE geladen. Aufruf: TEST_STUECKLISTE") +(princ)