Loxone picoC Skripte lokal entwickeln

Entwicklungshilfe mit Loxmock, Debugging und Live-Loxone Daten

Im Folgenden findet sich eine Zusammenfassung aller Artikel zu den Themen der lokalen picoC Entwicklung, Loxmock und seinen Möglichkeiten und natürlich dem Debugging von picoC Skripten. Manches bekannt, manches ergänzt und neu, in jedem Fall aber nun alles an einer Stelle inkl. dem Download von picoc4lm (v4.4) und dem loxmock (v4).

Inhalt

  1. Loxmock – Was ist das?
  2. Getting started – lokale Ausführung des eigenen Skripts mit Loxmock
  3. Konfiguration des Loxmock
  4. Dateninput
  5. Datenoutput
  6. Debugging
  7. Download

Loxmock – Was ist das?

Wer sich mit der picoC Entwicklung in der Loxone Welt befasst, der winkt möglicherweise schnell ab. Zu fehleranfällig, kompliziert und undurchsichtig ist die Entwicklung oft. Zudem holt man sich sehr schnell Instabilitäten in sein System. Seien es Memoryleaks, vergessene sleep-Statements und damit Ressourcenverbräuche u.v.m. Ich habe mir mal die Mühe gemacht sämtliche angebotenen Loxone-Funktionen unter picoC zu mocken. Das Ergebnis: Skript-Entwicklung kann fortan lokal erfolgen mit allen Loxone-Funktionen inkl. des arbeitens mit Loxone live Daten falls gewünscht. Memory-Leaks suchen und finden, Heapanzeige, Debugging – alles lokal möglich. Dazu habe ich picoC ein wenig „massiert“. Ist zwar picoC geblieben, jedoch kann picoC nun u.a. auch im Netz sprechen. Also HTTP, TCP und UDP. Auch dazu später mehr.

Generell stellt Loxone ja für die individuelle Erweiterung seiner Umgebung picoC von Zik Saleeba zur Verfügung. Zwar in einer älteren, lieblos erstellten Version, aber ausreichend für sämtliche Zwecke(zumindest für die meinen). Für die Miniserver-Umgebung wurden zusätzlich noch einige Loxone spezifische Funktionen implementiert. Diese verhindern natürlich die lokale Ausführung auf dem eigenen PC, da sie eben nur in der Loxone-Umgebung verfügbar sind. Außer der mäßigen Dokumentation stellt Loxone hier leider auch nichts zur Verfügung. Das erschwert eigene Erweiterungen dann doch sehr. Aber nur bis jetzt! Alles ist nun komplett lokal verfügbar – zumindest gemocked. Also: Kein Problem ohne eine Lösung. 🙂

Neben der Ermöglichung einer lokalen Ausführung der picoC Skripte, sind alle picoC-Funktionen aus der Loxone-Dokumentation funktional nachimplementiert. Jede aufgerufene gemockte Funktion kann dabei eine Ausgabe machen, dass sie aufgerufen wurde und ermöglicht somit eine Unterstützung beim Debuggen. Die Funktionen arbeiten 1:1 wie in Loxone (soweit ich das beurteilen kann 🙂 ) und können wahlweise arbeiten mit:

  • hartcodierten, vorgegebenen Daten
  • Loxone live-Daten
  • sonstigen über TCP/UDP erreichbaren Devices (Stichwort STREAM)

Gefundene Bugs habe ich belassen, um wie die Loxone-Umgebung zu reagieren.

Aber nun für die Ungeduldigen. Was muss man tun, um sein Skript lokal laufen zu lassen?

Getting started – lokale Ausführung des eigenen Skripts mit Loxmock

  1. Folgende Dinge müssen in ein Verzeichnis gelegt werden:
    • picoc4lm bzw. picoc4lm.exe samt .dll-Dateien
    • Die beiden Loxmock-script-Dateien loxmock.c und loxmock_func.c. Wir werden durch spätere Konfigurationen ausschließlich loxmock.c anfassen…
    • Das eigene Skript, im Weiteren myscript.c genannt.
  2. Am Anfang von myscript.c wird nun der nachstehende Code eingefügt. Dieser kann im Skript verbleiben, auch wenn das Skript auf den Loxoneserver kopiert wird. Er wird dort ignoriert werden.
#ifdef LOXMOCK
    #include "loxmock.c"
#endif

Fertig! Das Ganze ist nun bereit zur lokalen Ausführung. Einfach eine Kommandozeilen-Shell öffnen, in das Verzeichnis wechseln und myscript.c ausführen:

  • Unter Windows: picoc4lm.exe -s myscript.c
  • Unter Linux: picoc4lm -s myscript.c

Damit ist das eigene Skript schon einmal lauffähig. Fehlen nur noch die Daten. Dazu muss der Loxmock konfiguriert werden. Bisher wurden ja nur die Downloads im Verzeichnis abgelegt…

Konfiguration des Loxmock

Für die Justierung des grundsätzlichen Verhaltens, gibt es folgende Einstellungsmöglichkeiten in loxmock.c

BezeichnerWertebereichBeschreibung
LOXMOCK_PRINT_DEBUG0/1Aufrufe von gemockten Loxonefunktionen ausgeben?
LOXMOCK_EXECUTE_SLEEP0/1sleep()-Aufrufe ausführen und warten oder einfach durchlaufen?
LOXMOCK_LOCAL_TZDIFF_TO_UTC-23 to 23Zeitzonen Unterschied zur lokalen Zeit in Stunden
LOXMOCK_LOCAL_IS_SUMMERTIME0/1Haben wir Sommerzeit?
LOXMOCK_ENABLE_NETWORKING0/1Netzwerkkommunikation einschalten oder auf lokalen Daten arbeiten?
LOXMOCK_ENABLE_WRITEBACK0/1Ausgabewerte an verbundene Loxoneobjekte zurücksenden?
LOXMOCK_LOXONESERVERip:portAdresse des Loxoneservers
LOXMOCK_LOXONEAUTHuser:passwordBasicauth Informationen. Ggfls. auf die Berechtigungen des Users achten.

Dateninput

Es gibt für picoC Skripte in Loxone 3 Möglichkeiten an Daten zu kommen:

  1. Verwendung der Inputkanäle über die getinput()-Funktionen und damit die Übernahme der Daten der dort angeschlossenen Objekte
  2. Datenabrufe über getio() direkt im Code
  3. Datenabrufe von externen Datenquellen über HTTP (httpget(), localwebservice()) und/oder TCP/FILE über STREAM.

https kann über picoC leider nicht direkt verwendet werden. Falls notwendig, muss man hier einen Umweg über Loxberry machen.

Auf jede der 3 Möglichkeiten wird nun im Folgenden eingegangen. Die dargestellten Codefragmente finden sich entsprechend in loxmock.c wieder und können dort mit Leben gefüllt werden.

Inputkanäle

Die hier dargestellte Konfiguration steuert die Eingabe-Daten, die im Skript über getinput() und getinputtext() abgerufen werden.

Jeder Inputkanal eines Programm-Bausteins kann belegt werden. Entweder mit einer hartcodierten Vorgabe (im Bild aktuell immer 0) oder direkt mit einer Referenz zu einem Loxone-Objekt. Loxone Objekte können mit Ihrer ID, UUID oder ihrem Bezeichner angegeben werden. Sollte ein Bezeichner Leerstellen enthalten, so gilt folgendes: "so toll" wird zu "so%20toll".

Die Möglichkeiten sind nachstehend nochmals dargestellt. Das dort gezeigte Beispielskript liest lediglich die Input-Kanäle aus. Je nach Loxmock-Konfiguration(im linken Teil des Bildes zu sehen) werden die entsprechenden Werte ausgegeben, also die entweder die hartcodierten(im Bild als letztes unten) oder im Netzwerkmodus die live Daten.

Beim Erstellen gab es eine Latenz zwischen Loxoneanzeige, meinem Skriptstart und einem gemeinsamen Screenshot… 🙂 Aber die Idee sollte klar werden.

Events

Zu guter Letzt kann man auch mit Inputevents arbeiten. Heißt: Änderungen an den Kanälen der Eingangswerte werden über eine Bitmask abgebildet. Hierzu ist nichts separates zu konfigurieren. Wie in Loxone auch werden diese Bitmasks automatisch gepflegt bzw. nach Abruf über getinputevent() zurückgesetzt.

Virtuelle Eingänge

Des weiteren gibt es dann noch virtuelle Ein- und Ausgänge, die über getio() abgefragt werden können. Bei der Arbeit mit lokalen Daten müssen Endpunkte definiert werden. Hierzu findet sich in loxmock.c das Folgende:

#define LOXMOCK_VBLOCKS 5
char *vblocknames[LOXMOCK_VBLOCKS] = { "VI0", "VI1", "VI38", "anyname", "foo4" };

Zu definieren sind also Menge und Namen der virtuellen Punkte. Nach dem Start können diese dann mittels getio()/setio() wie gewohnt angesprochen werden.

Für den Netzwerkmodus ist diese Konfiguration natürlich irrelevant. Die Anfragen mit den Namen gehen direkt an Loxone und müssen dort vorhanden sein oder es gibt eben eine 0 zurück.

Netzwerkstreams lokal und live

Im Netzwerkmodus arbeiten die dargestellten Funktionen wie man es erwarten würde mittels request/response. Deshalb wird in den Screenshots lediglich auf die Testdatengenerierung für das Arbeiten im lokalen Modus eingegangen. Hierzu wird das Tool cli_tcp eingesetzt.

  • httpget()-Aufruf: Der Rückgabewert dieses Aufrufs ist inklusive des http-headers anzugeben. Das Vorgehen von der Datenabfrage bis zum Einfügen in die Konfiguration sieht dann wie folgt aus.
  • localwebservice()-Aufruf: Der Rückgabewert dieses Aufrufs ist ein reines XML oder JSON, jeweils ohne http-Protokollheader. Das Vorgehen von der Datenabfrage bis zum Einfügen in die Konfiguration sieht dann hier wie folgt aus. Es werden exemplarisch beide Abfragen für XML und JSON dargestellt.
  • Arbeiten mit STREAM

Testdaten für binäre Streams können mit einem beliebiegen Modbus-TCP Tool geholt werden oder wie in meinem Beispiel mit cli_tcp. Das nachstehende Bild zeigt die Konfiguration einmal mit der Arbeit auf byte-Ebene und einmal mit der Arbeit auf ascii-Ebene. Die vorgegebenen hex-Werte werden intern in die entsprechenden byte-Werte umgewandelt.

Outputkanäle

Ausgabekanäle werden in picoC unter Loxone typischerweise mittels der setoutput()-Funktionen angesprochen. Man kann seine Ausgaben lokal belassen oder auf Wunsch auch direkt nach Loxone zurückschreiben. Da das auch gefährlich sein kann, muss diese Option neben der Netzwerkaktivierung explizit eingeschaltet werden über den oben beschriebenen Parameter LOXMOCK_ENABLE_WRITEBACK. Zudem können die Funktionen setio() und natürlich die entsprechenden STREAM-Funktionen verwendet werden. Für setoutput() wird das Ganze mit dem Bild dargestellt.

Die Verknüpfung eines Ausgabekanals erfolgt über dessen Namen, genauso wie die Verknüpfung bei den Eingangskanälen. Die hierzu verwendete Funktion heißt lm_connectOutput(). Nachstehend mal exemplarisch dargestellt.

Debugging

Memoryleaks von außen betrachtet

Bevor wir ins Detail gehen, zunächst eine grobe Betrachtung: Das Verhalten des Loxmock ist bzgl. der Speicherallokation wie die Loxonefunktionen. Bei Aufrufen von z.B. getinputtext(), strdup(), malloc() etc. mit entsprechenden Rückgabezeigern werden Werte zurückgeliefert, die mittels free() auch wieder freigegeben werden müssen. Erfolgt das nicht, folgt irgendwann die Quittung. Selbst verschuldete Leaks haben natürlich den gleichen Effekt. Wird alles nur oft genug wiederholt, steigt das Programm irgendwann eben aus. Man sieht das unter Windows recht schön im Taskmanager. Startet man sein picoC Skript und lässt Loxmock auch nicht bei den sleep()-Statements warten, werden viele Iterationen in kurzer Zeit durchgeführt. Steigt der Ressourcenverbrauch kontinuierlich, so empfehle ich von einem Produktionsdeployment abzusehen… 😉

Achtung bei Windows: Das ausführende picoc-Programm wird wirklich als picoc.exe angezeigt und ist nicht zu verwecheln mit der „Windows-Befehlsprozessor“ oder „cmd.exe“ o.ä. Anzeige.

Entsprechende Anzeigen gibts unter den anderen Betriebssystemen natürlich auch.

Leakforcierung

Zum Erzeugen und Betrachten eines Leaks einfach mal 10 Minuten laufen lassen und beobachten. Ein zurückgegebener Leerstring "" enthält 1 Byte. Dauert also, aber irgendwann…

#ifdef LOXMOCK
    #include "loxmock.c"
#endif
while (TRUE) {
    char *cleverInput = getinputtext(0);
    // Ooops, forgot to free(cleverInput);
}

Features des Debuggers

Die nun beschriebenen Punkte sind im danach folgenden großen Bild dargestellt. Damit bekommt man einen guten Eindruck, wie das aussehen kann. Also, was gibts?

  • Heap-Tracker: Man kann sich die allokierten Pointer anzeigen lassen inkl. der Zeilenangabe im Skript, wo der Pointer allokiert wurde. Wird also ein free() vergessen, so bleibt der Pointer auf dem Heap. Bei vielen Iterationen wächst dieser an und sieht dann doch sehr auffällig in der Heapanzeige aus. Dieses Feature gibts leider nur in der Linux-Variante von picoc4lm.
  • Breakpoints: Man kann Haltepunkte im Skript setzen. Hat man sein Skript in einem Editor vor Augen und setzt an einer bestimmten Zeile einen Haltepunkt, so wird dort automatisch unterbrochen. Gerade bei Iterationen, kann man so kontrolliert fortsetzen, bis man z.B. eine bestimmte Menge an Durchläufen erreicht hat o.ä.
  • Steuerung während der lokalen Ausführung des Skripts gibt es folgende Tastenkombinationen:
UnterbrechenCtrl+C
FortsetzenCtrl+D
BeendenCtrl+4

Bei einer Unterbrechung wird eine kleine Commandline mitsamt einer Hilfe angezeigt:

  • Werteänderungen zur Laufzeit: Bei Unterbrechungen mittels Ctrl+C können am Prompt beliebige picoC Kommandos aufgerufen werden. Also z.B. getinput(); oder alles was man sich so denken kann(vor allem die in der Hilfe angezeigten Kommandos). Für Werteänderungen von hartcodierten Daten stellt Loxmock folgende Funktionen zur Verfügung, die oben bereits allesamt erklärt wurden.
              lm_setinputtext(0, ""); // Kanal, Wert
              lm_setinput(0,  0, ""); // Kanal, Wert, "ggfls. Name Loxone Objekt"
              setLoxmock_local_httpget("");
              setLoxmock_local_resultStream("");
              setLoxmock_local_localwebservice("");
              setio("VI1", 3.1415);

Nun zu einem Beispiel. Nachstehend sieht man die Konsolenausgabe chronologisch von oben nach unten. Nach 3 Iterationen des rechts im Bild sichtbaren Skriptcodes unterbreche ich mit Ctrl+C und gebe den Heap aus(pam();). Danach setze ich einen Breakpoint, setze fort und bekomme an der gewünschten Stelle meinen Interrupt. Man sieht an der blauen Markierung die nach 3 Iterationen entstandenen 3 Leaks.

Der Härtetest besteht natürlich im längeren Dauerlauf der Schleifen. Man kann dann unterbrechen und die Heapentwicklung betrachten oder das Ganze „von außen“ wie oben gezeigt, anschauen.

Download

picoc4lm für Linux

picoc4lm für Windows

Loxmock für picoc4lm


Wie immer gilt: Fortschritt, nicht Perfektion… Feedback ist also jederzeit willkommen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert