Debugging: Abarbeitung im Script in Systemvariable ausgeben

Problemlösungen und Hinweise von allgemeinem Interesse zur Haussteuerung mit HomeMatic

Moderator: Co-Administratoren

Antworten
Sternthaler
Beiträge: 4
Registriert: 14.01.2023, 08:48
System: CCU
Hat sich bedankt: 2 Mal
Danksagung erhalten: 1 Mal

Debugging: Abarbeitung im Script in Systemvariable ausgeben

Beitrag von Sternthaler » 17.03.2023, 23:02

Hallo in die Runde,

da ich vor kurzem auf den total guten Beitrag von MichaelN zum Thema Debugging viewtopic.php?p=661365 gestoßen war, wollte ich hier eine bei mir schon lange benutzt Vorgehensweise zum Debuggen eines syntaktisch korrekten HM-Scripts zeigen. (( Soll ein bisschen Danke an dieses Forum sagen. ))

Die Aufgabe besteht darin, dass das laufende Script Text-Informationen sammelt und diese dann zum Schluss in einer zum Script 'gehörenden' Systemvariablen formatiert ausgibt.
Somit kann in der Systemvariablen nachgesehen werden wie und warum das Script so oder so gearbeitet hatte.
Klar, das funktioniert dann natürlich nur, wenn es in den If/Then/Else-Zeigen jeweils eigene Debug-Texte gibt.

Um nun zum Punkt zu kommen: (Ein Bild sagt ja mehr als 1000 Worte.)

So soll der Debugtext dann ausgegeben werden:
Bild

Mit ein paar Demo-Codezeilen im Muster-Script wurde der Inhalt für die Systemvariable erzeugt:

Code: Alles auswählen

var V="V01.01 Debugausgabe in Systemvariable schreiben MUSTER";
!! -------------------------------------------------------------------------------
!! V01.00 18.02.2023 - Start-Revision
!! V01.01 dd.mm.yyyy - Eine wirklich wichtige Aenderung.
!!
!! -------------------------------------------------------------------------------
!! Funktion:
!! -------------------------------------------------------------------------------
!! Einzustellende / Werte in den folgenden Systemvariablen.
!! -------------------------------------------------------------------------------
!! SYSTEMVARIABLEN  Zwingend benoetigt:
!! -------------------------------------------------------------------------------
!! SYSTEMVARIABLEN  Optional
!! - "ScrDeb_[SCRIPT]"                      ====== S V - T Y P    Zeichenkette
!!   Debuginformationen zur Laufzeit vom Script.

!! -------------------------------------------------------------------------------
!!                            AB HIER EINSTELLEN
!! -------------------------------------------------------------------------------
!! Name von diesem [SCRIPT]
var     tmpD  = "Muster";

!! Prompt-Prefix / Prompt-Suffix fuer Historie und Debug-Ausgabe.
var     ProPre = "<pre style='text-align:left !important; font-family:Courier,monospace !important'>";
var     ProSuf = "</pre>";

!! -------------------------------------------------------------------------------
!!                            AB HIER KONSTANT
!! -------------------------------------------------------------------------------
!! KONSTANT benutzte Variablen
var     tmpA  = dom.GetObject("$src$");
var     tmpB  = system.Date("%F %X");
var     tmpC  = true; !! Flag, ob folgender Code weiter ausgefuehrt wird.
var     tmpD1 = "";   !! Debug: gesammelter Texte fuer die Debug-Ausgabe.
string  tmpD2;
string  tmpD3;

!! Lokal benoetigte Variablen
        !!    Eure ansonsten benoetigten Variablen

!! -------------------------------------------------------------------------------
!! Debug-Text mit Startmeldung initialisieren.
tmpD1 = tmpD1 # tmpD # " - Startzeit :" # tmpB # ":";
if (tmpA) { tmpD1 = tmpD1 # "#Start durch :" # tmpA.Name() # ":"; }
tmpD1 = tmpD1 # "##" # V # "#";

!! ----------  H A U P T P R O G R A M M  ----------------------------------------

        !!    Beispielbloedsinn fuer eine Ausgabe

if (tmpC) {
  !! Hier eure Initialisierungen.
  tmpD1 = tmpD1 # "#Hallo";
  tmpD1 = tmpD1 # "#- Eins und";
  tmpD1 = tmpD1 # "#- Zwei und";
  tmpD1 = tmpD1 # "#- Drei bis Sechs.";
  tmpD1 = tmpD1 # "#  Zur Demo 2 Zeilenumbrueche:";
  tmpD1 = tmpD1 # "##";
}

if (tmpC) {
  !! Der erste Teil der Arbeit vom Script.
  tmpD1 = tmpD1 # "#  1 " # tmpB;
  tmpD1 = tmpD1 # "#  2 " # tmpB.ToTime();
  tmpD1 = tmpD1 # "#  3 " # tmpB.ToTime().Format("%S");
  tmpD1 = tmpD1 # "#  4 " # tmpB.ToTime().Format("%S").ToInteger();
  tmpD1 = tmpD1 # "#  5 " # tmpB.ToTime().Format("%S").ToInteger().Mod(2);
  tmpD1 = tmpD1 # "#  6 " # tmpB.ToTime().Format("%S").ToInteger().Mod(2).ToString(0);

  if (tmpB.ToTime().Format("%S").ToInteger().Mod(2) == 0) {
    tmpD1 = tmpD1 # "#  - Gerade Sekunde";
  } else {
    tmpD1 = tmpD1 # "#  - Ungerade Sekunde";
    tmpC  = false;  !! Ab nun soll im weiteren Script nichts mehr gemacht werden.
  }
}

if (tmpC) {
  !! Der naechste Teil der Arbeit vom Script.
  tmpD1 = tmpD1 # "#    Cool. Gerade Sekunden machen hier weiter.";
  tmpD1 = tmpD1 # "#    Und HTML-Tabellen könn(t)en per foreach() geloopt werden:";
  tmpD1 = tmpD1 # "#";

  !! 2 foreach()-Loops können dann auch HTML-Tabellen 'bauen'.
  tmpD1 = tmpD1 # "<table>";
  tmpD1 = tmpD1 # "<tr>";
  tmpD1 = tmpD1 # "<th></th>";
  tmpD1 = tmpD1 # "<th>Temperatur</th>";
  tmpD1 = tmpD1 # "<th>Rel.Feuchte</th>";
  tmpD1 = tmpD1 # "</tr>";
  tmpD1 = tmpD1 # "<tr>";
  tmpD1 = tmpD1 # "<td>Bad</td>";
  tmpD1 = tmpD1 # "<td>22,2</td>";
  tmpD1 = tmpD1 # "<td>69</td>";
  tmpD1 = tmpD1 # "</tr>";
  tmpD1 = tmpD1 # "<tr>";
  tmpD1 = tmpD1 # "<td>Küche</td>";
  tmpD1 = tmpD1 # "<td>19,5</td>";
  tmpD1 = tmpD1 # "<td>63</td>";
  tmpD1 = tmpD1 # "</tr>";
  tmpD1 = tmpD1 # "</table>";

  tmpD1 = tmpD1 # "#Warum auch immer die Ausgabe in der Tabelle weis wird?";
  tmpD1 = tmpD1 # "#Aber da können die CSS-Spezies bestimmt etwas machen.";
}

if (tmpC) {
  !! Und weitere Teile, wenn der 'Vorredner'-Code kein  tmpC  = false;  setzt.
}

!! -------------------------------------------------------------------------------
!! Abschlussaktionen:
!! - Debugtext in Debugvariable schreiben wenn sie vorhanden ist.
!! - Debugtext im "Script testen" schreiben.
tmpD1 = tmpD1 # "##" # tmpD # " - Endezeit :" # system.Date("%F %X") # ":#";
tmpD2 = "ScrDeb_" # tmpD;
tmpD3 = dom.GetObject(tmpD2);
if (! tmpD3) { tmpD1 = tmpD1 # "#Debug-SV :" # tmpD2 # ": nicht vorhanden#"; }
else         { tmpD3.State(""); !! Last-Value wird mit dem naechsten Schreiben leer.
               tmpD3.State(ProPre # tmpD1.Replace("#", "<br>") # ProSuf);    }
if (! tmpA)  { WriteLine(tmpD1.Replace("#", "\n")); }
Das Script 'ganz normal' in einem HM-Programm eintragen:
Bild

Benötigt wird eine Systemvariable:
Bild
Zu beachten ist, dass diese SV NICHT zwingend benötigt wird.
Das Script macht dann einfach nur keine Ausgabe. :lol:

Zum Ausprobieren könnt ihr dieses Script (ohne Änderungen) einfach im 'Skript testen' ausführen.
Je nach gerader oder ungerader Sekunde wird die Ausgabe dann unterschiedlich ausfallen. Sowohl in der Ausgabe vom 'Skript testen', als auch in der Systemvariablen falls ihr die angelegt habt.


Um diesen Script-Rahmen etwas besser zu verstehen, hier eine paar Beschreibungen einzelner Zeilen und deren Nutzung: (( Seid mir nicht böse, wenn ich bei der Angabe der Zeilennummern möglicherweise daneben lag. ))

Zeile 1 : var V="V01.01 Debugausgabe in Systemvariable schreiben MUSTER";
Zeile 45 : tmpD1 = tmpD1 # "##" # V # "#";
Variable "V" mit einer Programmbeschreibung.
Da Scriptzeile 1 bei der Bearbeitung eines HM-Programms in der UI angezeigt wird, macht es hier Sinn, dass im Script eben in der ersten Zeile eine Version und ein kurzer, beschreibender Text vom Script steht.
In Zeile 45 wird diese Versions-Variable auch im Debug ausgegeben, so dass man auch in der dortigen Ausgabe sehen kann, welche Scriptversion gerade durchlaufen wurde.

Zeile 21 : var tmpD = "Muster";
Zeile 116 : tmpD2 = "ScrDeb_" # tmpD;
Hier wird der 'Eigenname' vom HM-Script angegeben.
Anhand dieser Angabe wird in der 'Abschlussaktionen' ganz am Ende vom HM-Script der Name für die ausgebende Systemvariable zusammengesetzt.
(( Diese Angabe in tmpD nutze ich in anderen HM-Scripten auch für weitere Systemvariablen. (Z.B.: ScrHis_ Historie / ScrCnf_ Config / ..) ))

Zeile 24 : var ProPre = "<pre style='text-align:left !important; font-family:Courier,monospace !important'>";
Zeile 25 : var ProSuf = "</pre>";
Zeile 120 : tmpD3.State(ProPre # tmpD1.Replace("#", "<br>") # ProSuf); }
HTML-Tags um die Debugausgabe per Style so einzustellen, dass unser Debug-Text komplett erhalten bleibt. Das erfolgt mit dem HTML-TAG <pre>.
Es bleiben vor allem alle Leerzeichen in unserem Debugtext erhalten. (Und damit kann per Einrückung mit Leerzeichen schon einiges 'formatiert' werden.)
Die weitere Angabe zum Schriftfont sorgt dafür, dass alle Buchstaben hübsch die gleiche Breite behalten und somit auch tabellarische Ausgaben nett aussehen.

Zeile 31 : var tmpA = dom.GetObject("$src$");
Zeile 121 : if (! tmpA) { WriteLine(tmpD1.Replace("#", "\n")); }
tmpA enthält ein Objekt, WENN das HM-Script innerhalb eines HM-Programms durch eine 'Bedingung' im HM-Programm gestartet wurde.
Entsprechend ist KEIN Objekt vorhanden, wenn das HM-Script in 'Skript testen' ausgeführt wird.
Genau darauf basiert dann die Ausgabe in Zeile 121 per WriteLine(). Entsprechend wird diese dann sinnlose Ausgabe nicht gemacht, wenn das HM-Script in einem HM-Programm ausgeführt wird.

Zeile 32 : var tmpB = system.Date("%F %X");
Zeile 43 : tmpD1 = tmpD1 # tmpD # " - Startzeit :" # tmpB # ":";
Zeile 115 : tmpD1 = tmpD1 # "##" # tmpD # " - Endezeit :" # system.Date("%F %X") # ":#";
Datums-/Zeit-String, der auch vom HM-Script in die Debugausgabe geschrieben wird.
In Zeile 115 wird natürlich die Datums-/Zeit-Angabe für das Ende vom HM-Script neu ermittelt.
(( Ich mache bewusst keine Formatierung auf ein deutsches Zeitformat. So kann der textuelle Zeit-Inhalt in tmpB nämlich im Script auch mit tmpB.ToTime() wieder benutzt werden. ))


Zur Demonstration sind die Zeilen 51 bis 109 in diesem Muster-Script.

Zu beachten ist ein Sonderzeichen, welches ihr dann später als Zeilenumbruch in der Debugausgabe haben möchtet:
Der Buchstabe '#' innerhalb des auszugebenden Textes erzeugt den Zeilenumbruch.


Wichtig ist hier noch, dass im Text KEINE Buchstaben '<' '>' vorhanden sein sollen. Die werden im Ausgabe-Element der UI als HTML-Zeichen benutzt.
Um nun diesen 'Zeilenumbruch'-Buchstaben am Scriptende in der 'Abschlussaktion' ab Zeile 112 seine Aufgabe machen zu lassen, gibt es dort ZWEI Varianten:
Zeile 120 : tmpD3.State(ProPre # tmpD1.Replace("#", "<br>") # ProSuf); }
ersetzt für die Ausgabe in der UI-HTML-Umgebung das '#' mit '<br>'
Zeile 121 : if (! tmpA) { WriteLine(tmpD1.Replace("#", "\n")); }
ersetzt für die Ausgabe in der 'Skript testen'-UNIX-Umgebung das '#' mit '\n'

Zu beachten ist auch noch, dass das Script immer bis zur 'Abschlussaktion' kommen muss.
Wenn also euer Script-Code aus beliebigen Gründen nicht mehr weiter machen soll, dann muss der folgende Code übergangen werden.
Dafür sind die im Muster benutzen Zeilen 51, 61, 78 und 107 mit dem "if (tmpC) {" vorhanden.
In diesem Muster-Beispiel sorgt Zeile 74 mit "tmpC = false;" dann dafür, dass nichts Weiteres passiert.



Jetzt sind es ja doch 1000 Worte geworden ;)
Ich hoffe, dass ihr etwas damit machen könnt.

Und als 'Echt'-Beispiel eine Ausgabe aus meiner Test-CCU in der allerdings nur ein Thermostat (HM-TC-IT-WM-W-EU) werkelt:
Bild


Gruß
Sternthaler

-- Editiert um die Bilder gegen JPG's zu tauschen. PNG wird auf meinem Tablet nicht angezeigt [Hmm, ?? Google-Dings. Mal sehn, ob es jetzt funktioniert.]
-- Editiert für fehlende Erfolgsmeldung: Auch jetzt mit JPG keine Google-Dings-Tablet-Anzeige. [Frust]
CCU2 (Produktiv-System): 59 Geräte
CCU3 (Test-System): 21 Geräte
Scripte schreiben ist cool. Script-Syntax ist erbärmlich.

Antworten

Zurück zu „HomeMatic Tipps & Tricks - keine Fragen!“