Kosten von Funktionsaufrufen

Bei der Programmierung geht es nicht ums Geld, wenn man von Kosten spricht

Computer arbeiten die Befehle in ihren Programmen der Reihe nach ab – das ist eine Binsenweisheit. Für jeden Befehl braucht der Computer eine gewisse Zeit. Solange man nicht programmiert, muss man darüber auch nicht nachdenken, doch wenn es um den flüssigen Programmablauf für ein Smart Home geht, dann kommt man kaum daran vorbei.

Variablen in HomeMatic-Scripten

Die CCU hatte früher ein Limit von maximal 200 Variablen, das mit Firmware-Version 2.29.18 aufgehoben wurde. Dieses Limit bezog sich auf alle Variablen, die in allen Scripten verwendet werden. Gemeint sind Variablen, die direkt im Script definiert werden: object x; object y; var z;

Wenn Scripte nicht mehr funktionieren und bei der Prüfung unerklärliche Syntax-Fehler auftreten, sollte versuchsweise dieses Programm wieder gelöscht oder deaktiviert werden – oder, noch besser, auf die aktuelle Firmware-Version aktualisiert werden.

Strings in HomeMatic-Scripten

Durch String-Verwendung in HomeMatic-Scripten kann die CCU fehlerhaft arbeiten, instabil werden oder sogar abstürzen. Grundsätzlich gilt: Je öfter mit Strings hantiert wird, desto eher führt dies zu Problemen.

Ich empfehle daher, nach Umsetzung dieser Anleitung die CCU unter Beobachtung zu halten.

Teure und billige Funktionen

Die Bedingungen und Befehle, die am Ende ein Programm ergeben, können unterschiedlich lange Zeiten zur Abarbeitung benötigen.

Befehle, die auf die Funkschnittstelle zugreifen, sind „teuer“: Der Befehl muss gesendet werden, die CCU muss auf Antwort warten, dann kann die Programmausführung erst fortgesetzt werden. Die Kosten können sich noch erhöhen, wenn der Befehl aufgrund von Funkstörungen wiederholt werden muss.

Die Ausführungsdauer mehrerer Befehle hintereinander summiert sich. Wer seine Beleuchtung programmiert hat, der sieht es sofort: Alle Leuchten werden verzögert nacheinander eingeschaltet, auch wenn keine zusätzliche Verzögerung programmiert wurde.

Ein sehr schneller Befehl dagegen ist zum Beispiel die Prüfung von Bedingungen. Im folgenden Programm wird geprüft, ob ein Bewegungsmelder ausgelöst hat.

Der Zustand des Bewegungsmelders befindet sich bereits im Arbeitsspeicher der CCU. Sie muss nur noch überprüfen, ob eine Speicherzelle einen bestimmten Wert hat, und kann anschließend den nächsten Befehl abarbeiten.

Aufgrund der Logik von WebUI-Programmen gilt das auch, wenn der Bewegungsmelder selbst das Programm auslöst: Die Auslösung erfolgt natürlich erst, wenn der Melder seinen aktuellen Zustand übertragen hat – aber die Programmausführung beginnt auch nicht vorher, daher bleibt die Prüfung der Bedingung aus Sicht der CCU in jedem Fall „billig“.

Nur die nötigsten Befehle senden

Ziel der Programmierung ist es also, teure Funktionen zu vermeiden. Manchmal ist es besser, ein Programm etwas komplizierter zu schreiben, weil selbst eine Vielzahl billiger Funktionen schneller bearbeitet wird als eine einzelne teure. Aus Geschwindigkeitsgründen lohnt es sich, nur die nötigsten Befehle zu senden und dafür mehr Aufwand in die Prüfung zu stecken.

Ein einfaches Licht-aus-Script könnte so aussehen:

! HomeMatic-Script
! KOSTEN VON FUNKTIONSAUFRUFEN
! http://www.christian-luetgens.de/homematic/programmierung/theorie/kosten/Teure_Programme.htm

string l = dom.GetObject("Licht").EnumUsedIDs();
string i;
object o;

foreach (i, l) {
  o = dom.GetObject(i);
  if (o.HssType() == "SWITCH") {
    o.State(false);
  }
  if (o.HssType() == "DIMMER") {
    o.State (0.0);
  }
}

!  Ende des Scripts

Das Programm ist ganz einfach: Das Gewerk Licht wird von Anfang bis Ende abgearbeitet und jede einzelne Leuchte wird ausgeschaltet. Das bedeutet freilich auch, dass jeder einzelne Kanal einen Ausschaltbefehl bekommt und die CCU jedes Mal auf die Antwort wartet, bevor der nächste Befehl gesendet wird.

Eine kleine Änderung sorgt dafür, dass im Licht-aus-Script stattdessen billige Funktionen verwendet werden und die teuren nur, wenn es unbedingt sein muss.

! HomeMatic-Script
! KOSTEN VON FUNKTIONSAUFRUFEN
! http://www.christian-luetgens.de/homematic/programmierung/theorie/kosten/Teure_Programme.htm

string l = dom.GetObject("Licht").EnumUsedIDs();
string i;
object o;

foreach (i, l) {
  o = dom.GetObject(i);
  if ((o.HssType() == "SWITCH") && (o.DPs().GetAt(0).Value())) {
    o.State(false);
  }
  if ((o.HssType() == "DIMMER") && (o.DPs().GetAt(0).Value() > 0)) {
    o.State (0.0);
  }
}

!  Ende des Scripts

Das Script prüft nun, ob der Kanal eingeschaltet ist, bevor es ggf. den Ausschaltbefehl sendet. Die Verarbeitungsgeschwindigkeit steigt dadurch beträchtlich, denn die Daten für die Prüfung liegen im Arbeitsspeicher und die Prüfung ist billig.

Man kann auch dieses Programm wieder verteuern, und zwar massiv.

! HomeMatic-Script
! KOSTEN VON FUNKTIONSAUFRUFEN
! http://www.christian-luetgens.de/homematic/programmierung/theorie/kosten/Teure_Programme.htm

string l = dom.GetObject("Licht").EnumUsedIDs();
string i;
object o;

foreach (i, l) {
  o = dom.GetObject(i);
  if ((o.HssType() == "SWITCH") && (o.DPs().GetAt(0).State())) {
    o.State(false);
  }
  if ((o.HssType() == "DIMMER") && (o.DPs().GetAt(0).State() > 0)) {
    o.State (0.0);
  }
}

!  Ende des Scripts

Auf den ersten Blick sieht alles unverändert aus. Auf den zweiten erkennt man, dass bei der Prüfung des aktuellen Status nicht mehr .Value() zum Einsatz kommt, sondern .State(). Der Unterschied besteht darin, dass .State() den aktuellen Status direkt beim Gerät abfragt – eine der teuersten Funktionen, die hier für jeden Kanal aufgerufen wird. Die Ausschaltbefehle fallen dann schon fast nicht mehr ins Gewicht.

Man braucht wirklich einen sehr guten Grund, wenn man einen Status mit .State() abfragt.

Vier Fliegen mit einem Befehl

Wenn regelmäßig mehrere Aktoren gleichzeitig geschaltet werden sollen, kann man eine direkte Verknüpfung anlegen.

Im Prinzip handelt es sich auch hier um eine teure Funktion, denn auch der Sendebefehl für die direkte Verknüpfung geht über die Funkschnittstelle. Die Ersparnis besteht darin, dass mehrere Empfänger auf denselben Befehl reagieren. Statt nacheinander vier Befehle für vier Aktoren zu senden, wird nur ein Befehl gesendet, auf den alle Aktoren reagieren: Wir haben 75 % Kostenersparnis und die Leuchten schalten sich auch noch gleichzeitig ein, nicht verzögert.

Intelligenz auslagern

Im vorigen Beispiel ging es um ein Treppenhauslicht, bei dem eine Einschaltdauer festgelegt wird. Bei Einzelbefehlen muss diese Einschaltdauer vor jedem Einschalten separat übertragen werden. Das macht für vier Leuchten acht Befehle, die alle der Reihe nach gesendet werden müssen.

Bei einer direkten Verknüpfung wird die Einschaltdauer dagegen fest im Empfänger hinterlegt. Tatsächlich haben wir also sogar sieben von acht teuren Funkbefehlen eingespart.

Für effiziente Programme ist es also hilfreich, sich auch ein wenig mit der Technik auszukennen, die in der HomeMatic steckt. Auch darum versuche ich, auf meinen Seiten immer wieder die Interna zu erklären, soweit sie für bessere Programmierung hilfreich sind.

Wichtiges zuerst, unwichtiges später

Manchmal geht es nur um Psychologie. Kommen wir noch einmal auf das Licht-aus-Script zurück.

Das schnelle Script prüft den Zustand anhand der Stati, die die CCU intern für jeden Kanal gespeichert hat. Was, wenn ein Status nicht stimmt, also z. B. eine Leuchte eingeschaltet ist, in der CCU aber als „aus“ gespeichert wurde? So etwas kann durch Funkstörungen durchaus passieren.

In diesem Fall würde das Script sie einfach nicht ausschalten. Also doch alle Leuchten schalten, auch wenn es länger dauert?

Die Lösung lautet, einfach beide Programme laufen zu lassen.

! HomeMatic-Script
! KOSTEN VON FUNKTIONSAUFRUFEN
! http://www.christian-luetgens.de/homematic/programmierung/theorie/kosten/Teure_Programme.htm

string l = dom.GetObject("Licht").EnumUsedIDs();
string i;
object o;

foreach (i, l) {
  o = dom.GetObject(i);
  if ((o.HssType() == "SWITCH") && (o.DPs().GetAt(0).Value())) {
    o.State(false);
  }
  if ((o.HssType() == "DIMMER") && (o.DPs().GetAt(0).Value() > 0)) {
    o.State (0.0);
  }
}

foreach (i, l) {
  o = dom.GetObject(i);
  if (o.HssType() == "SWITCH") {
    o.State(false);
  }
  if (o.HssType() == "DIMMER") {
    o.State (0.0);
  }
}

!  Ende des Scripts

Wenn man auf einen Schalter drückt, dann erwartet man, dass sofort danach irgendwas passiert. Der erste Teil des Programms kümmert sich also darum, so schnell wie möglich alle Leuchten auszuschalten, die eingeschaltet sind. Prompte Reaktion: Das gefällt auch der Mutti.

Nachdem die Mutti glücklich ist, geht der zweite Teil noch einmal alle Kanäle durch und schaltet unabhängig vom bisherigen Zustand alles aus. In der Regel wird Mutti das gar nicht bemerken, denn in der Regel stimmt der zwischengespeicherte Status. Die CCU ist nur noch ein wenig damit beschäftigt, ausgeschaltete Leuchten auszuschalten.

Auf diesem Weg hat man beides: Schnelle Reaktion, indem nur die nötigsten teuren Funktionen verwendet werden, und sicheres Ausschalten, indem anschließend nicht mehr auf den Preis geachtet wird.

Die CCU ist natürlich dennoch beschäftigt, bis alle Kanäle einmal ausgeschaltet wurden, auch wenn Mutti schon glücklich ist. Andere Programme werden in dieser Zeit nicht ausgeführt und der Funkverkehr kann zu Kommunikationsstörungen führen. In diesem Fall ist es daher sinnvoller, dafür zu sorgen, dass möglichst immer korrekte Stati in der CCU gespeichert sind. Dann kann man sich den zweiten Programmteil auch sparen.

Navigation