weil ja im Bug-Forum über das Easy-Display gesprochen wird (welches ich nicht getestet habe):
Ich habe mir die homestatus-App für Android gekauft, und mal damit gespielt, weil mir am HM-StatusDisplay einfach diverse Möglichkeiten fehlen.
HomeStatus ist ganz nett, wahrscheinlich ähnlich wie das Easy-Display, um es aber mit HPCL und der CCU zu nutzen, muss man sich ganz schön verrenken (meine Meinung).
Da sich das homeStatus-Display ALLES aus Systemvariablen der CCU zieht, die einem bestimmten Namensschema entsprechen müssen, habe ich mir schnell was parametrierbares in VBA zusammengedengelt, was mir ein HM-Script erstellt und in die Zwischanablage kopiert, das dann die Systemvariablen auf der CCU per Skript-Testen-Fenster erzeugt (ich war schon immer ein fauler Hund , aber das erzeugen der Systemvariablen ist eigentlich nur ein mal notwendig)
Das in meinen Augen größte Problem ist, das aus HPCL Systemvariablen gesetzt werden müssen, und im optimalfall das Display nur bei Bedarf "getriggert" wird, um sich die Änderungen abzuholen (Das Display kann auch "pollen" und einfach alle x-Sekunden alle Systemvariablen aus der CCU auslesen, und dann ggf. die Darstellung aktualisieren)
Systemvariablen setzen bedeutet aber, das "verzögerungen" im Programmablauf auftreten, und das ist etwas, was ich überhaupt nicht mag. Um das zu umgehen, habe ich soetwas wie ein Makro erstellt, was bis zu 10 Worker-Threads (übertrieben, aber trifft es) anspricht, die das Setzen der Systemvariablen asyncron übernehmen und das Display triggern, damit laufen meine Makros nach wie vor absolut syncron ab.
Wie mache ich das mit den Worker-Threads:
Noch mal kurz der Unterschied zwischen den Aufrufen für Makros:
AUFRUFEN: das aufgerufene Makro wird syncron erledigt, solange keine verzögernden Befehle genutzt werden, im optimalfall kann man sich das so vorstellen, wie wenn die Befehle im aufrufenden Makro selbst stehen würden.
STARTE: die Ausführung des gestarteten Makros wird erst begonnen, wenn zum aktuellen Zeitpunkt "nichts mehr" zu tun ist, weil alles syncron abgearbeitet wurde, oder weil in syncron ablaufenden Makros eine Verzögerung auftritt (warten, Systemvariablen, getsite, Sprung zu einer Marke OBERHALB im Makro,...)
Ich habe ein zentrales Makro (dieses wird AUFGERUFEN), in diesem Makro werden ein paar Variablen vom aufrufenden Makro gesetzt, und das zentrale Makro "verteilt" Round-Robin auf 10 weitere Makros die erhaltenen Daten, und STARTET dann den Worker-Thread. Der kann jetzt asyncron arbeiten, und die Systemvariable setzen, wenn das passiert ist, triggert der Worker Thread dann noch das Display (ich habe es erst mit getsite gemacht, aber das dauert mir zu lange, dann mache ich es lieber Shell-wget, das Ergebnis vom wget interessiert mich eh nicht).
Und da sind wir bei einer Sache, die beim HomeStaus-Display in meinen Augen doof ist:
Der Weg zurück vom Display zu HPCL geht praktisch nur mit den 50 virtuellen BidCos-RF-Tastern, das Display selbst hat keinerlei "Intelligenz", für jede Reaktion auf einen Knopfdruck auf dem Display muss per virtuellem Taster ein Makro in HPCL auslösen, Systemvariablen setzen, und das Display wieder triggern.
Die Zeit, die dabei vergeht, bezeichne ich mal als lag (engl. für Nachhinken, Verzögerung). Und bis auf einen Tastendruck etwas passiert und die Rückmeldung auf dem Display da ist, dauert es eine gefühlte Ewigkeit (ich schätze ~5 Sekunden). Schalten geht deutlich schneller, das ist max. 1-2 Sekunden Verzögerung.
Wie vom Hersteller der App auch angedacht kann man es eher für die "Visualisierung" nutzen, für die Interaktion mit dem Bediener ist es für ungeduldige weniger geeignet, es hat aber "witzige" Features, wie das Text-To-Speech, die ich so noch nicht hatte (ich habe z.B. keinen MP3-HM-Funkgong, und da müsste man auch die Ansagen selber einsprechen oder die MP3s im Vorfeld irgendwie erstellen).
Programmieraufwand:
Ich würde sagen, für Interaktion mit dem Benutzer und flexiblen Menüs ist es sehr viel Aufwand, nur für die Visualisierung taugt es aber, und mit ein bisschen strukturierten Makros zur Unterstützung bleibt es übersichtlich. Es mag sein, das sich bei der Nutzung von HM-Script die Reaktionszeit verbessern ließe, aber sicher bin ich mir da auch nicht.
Ich mag meine CCU1 nicht gegen die Wand fahren, indem ich das Display (oder auch mehrere) auf einen Refresh-Intervall von 1 Sekunde stelle, weil das Display dann wahrscheinlich 83000x pro Tag unnötig die CCU nervt, weil sich eh nichts getan hat, bei 10 Sekunden Refresh platzt die CCU1 wahrscheinlich nicht, auch wenn immer noch 8000 unnötige Anfragen vom Display gestellt werden, aber die Reaktionszeit wäre mir zu lang, deswegen meine Lösung mit dem getriggerten Refresh und meinen Worker-Threads.
Und ganz wichtig:
Ich habe noch keine (sinnvolle) Möglichkeit gefunden, das ganze so von "aussen" parametrierbar zu machen, das nicht dauerndes Neukompilieren/Übertragen/Starten notwendig ist, weil irgendwo dann doch eine Systemvariable falsch gesetzt wurde (Klar, ich könnte in meinem HPCL-Projekt nochmal 100te neue Zeichenkettenvariablen anlegen, und die als Vorgaben nutzen, dann kann man die von aussen setzen oder auch per Ini-File "einlesen"). Man kann zwar z.B. mit der WebUI-HQ die Darstellung der Buttons relativ schnell testen, aber für Interaktion braucht es dann doch die Änderungen im HPCL-Projekt.
Wenn ich was Präsentationsfähiges habe, mache ich mal ein paar Screenshots vom Tablet-Bildschirm, zur Zeit sind das eher Machbarkeitsstudien, wie sich das so darstellt. Und dabei merke ich immer wieder, das HPCL ein paar ganz wichtige Dinge fehlen, mir vor allem so etwas wie Arrays, wo ich mit einem Index in einer Variablen EINFACH auf ein Array von Werten zugreifen kann, und am besten soetwas schönes wie Listen, die man mit for ... each durchiterieren kann
Neugierde geweckt?
Hier mal das Zentrale Makro, was auf die Worker-Threads verteilt:
Code: Alles auswählen
//! ============================================================
//! OBJEKT dm_SetAction
//! ============================================================
//! OBJEKT-TYP : Makro
//! BEZEICHNUNG : dm_SetAction
//! STARTWERT :
//! ------------------------------------------------------------
//! AUSFÜHRUNGSINTERVALL : nein
//! AUSFÜHRUNG BEI EINGABE : ja
//! AUSFÜHRUNG BEI EMPFANG : nein
//! AUSFÜHRUNG BEI ÄNDERUNG : nein
//! ------------------------------------------------------------
//! KONF.:ALLV=0
//!
//! ============================================================
//! VARIABLENDEFINITIONEN
//! ============================================================
//! NAME TYP STARTWERT
//! ------------------------------------------------------------
//! lngRoundRobin Zahl 0
//! strButton Zeichen
//! strSysVarName Zeichen
//! strValue Zeichen
//! lngDisplay Zahl 0
//! lngDebug Zahl 1
//! lngActive Zahl 1
//! lngTriggerMethod Zahl 0
// Von Aussen wird
// lngDisplay auf 1 ... x gesetzt (auf jeden Fall notwendig, damit der Worker-Thread weis, welches Display getriggert werden muss)
// strButton, alternativ direkt strSysVarName gesetzt
// strValue ist der Wert, der in die Systemvariable geschrieben werden soll
// Das ganze kann man auch einfach abschalten, wenn es hintendran knallen sollte...
wenn lngActive <> 1 dann
strButton := ""
strValue := ""
strSysVarName := ""
lngDisplay := 0
verlassen
endewenn
// Display-Set-Actions per Round-Robin auf 10 Tasks verteilen, weil SysVar-Setzen und Display-Triggern Zeit verbraucht.
lngRoundRobin := lngRoundRobin + 1
wenn lngRoundRobin < 0 oder lngRoundRobin > 9 dann
lngRoundRobin := 0
endewenn
wenn lngRoundRobin = 9 dann
wenn strSysVarName = "" dann
dm_SetAction9.strButton := strButton
strButton := ""
sonst
dm_SetAction9.strButton := ""
dm_SetAction9.strSysVarName := strSysVarName
strSysVarName := ""
endewenn
dm_SetAction9.strValue := strValue
strValue := ""
dm_SetAction9.lngDisplay := lngDisplay
lngDisplay := 0
starte dm_SetAction9
verlassen
endewenn
// Diese Blöcke für lngRoundRobin = 8 ... 1, wobei halt dann in dm_SetAction8...1 geschrieben wird
wenn lngRoundRobin = 1 dann
wenn strSysVarName = "" dann
dm_SetAction1.strButton := strButton
strButton := ""
sonst
dm_SetAction1.strButton := ""
dm_SetAction1.strSysVarName := strSysVarName
strSysVarName := ""
endewenn
dm_SetAction1.strButton := strButton
strButton := ""
dm_SetAction1.strValue := strValue
strValue := ""
dm_SetAction1.lngDisplay := lngDisplay
lngDisplay := 0
starte dm_SetAction1
verlassen
endewenn
//wenn lngRoundRobin = 0 dann
wenn strSysVarName = "" dann
dm_SetAction0.strButton := strButton
strButton := ""
sonst
dm_SetAction0.strButton := ""
dm_SetAction0.strSysVarName := strSysVarName
strSysVarName := ""
endewenn
dm_SetAction0.strValue := strValue
strValue := ""
dm_SetAction0.lngDisplay := lngDisplay
lngDisplay := 0
starte dm_SetAction0
verlassen
//endewenn
Code: Alles auswählen
//! ============================================================
//! OBJEKT dm_SetAction0
//! ============================================================
//! OBJEKT-TYP : Makro
//! BEZEICHNUNG : dm_SetAction0
//! STARTWERT :
//! ------------------------------------------------------------
//! AUSFÜHRUNGSINTERVALL : nein
//! AUSFÜHRUNG BEI EINGABE : ja
//! AUSFÜHRUNG BEI EMPFANG : nein
//! AUSFÜHRUNG BEI ÄNDERUNG : nein
//! ------------------------------------------------------------
//! KONF.:ALLV=0
//!
//! ============================================================
//! VARIABLENDEFINITIONEN
//! ============================================================
//! NAME TYP STARTWERT
//! ------------------------------------------------------------
//! strButton Zeichen
//! strValue Zeichen
//! lngDisplay Zahl 0
//! strSysVarName Zeichen
//! strURL Zeichen
//! strReturn Zeichen
wenn strSysVarName = "" dann
strSysVarName := "hs_display" + lngDisplay + ":" + strButton
endewenn
wenn dm_SetAction.lngDebug > 0 dann
// ins Syslog schreiben
syslog.optNoWrite := 1
syslog.strMessage := "dm_SetAction0: Start Set-Action fuer Display "+lngDisplay+", setze "+strSysVarName+" auf '" + strValue+ "'"
aufrufen syslog
endewenn
// Zeitverbrauch...
setccusysvar(strSysVarName,strValue,settings.SysVarHost)
strSysVarName := ""
strValue := ""
// Eine RefreshUrl ist z.B. 192.168.1.123/refresh
// Man könnte auch direkt http://192.168.1.123:8080/refresh speichern
// dann kann nur wieder getsite nichts damit anfangen... Deswegen habe ich die getsite-Kompatible Form gespeichert,
// bei Nutzung von wget wird das entsprechend umgebaut.
wenn lngDisplay = 1 dann
strUrl := settings.RefreshUrlDisp1
endewenn
wenn lngDisplay = 2 dann
strUrl := settings.RefreshUrlDisp2
endewenn
wenn lngDisplay = 3 dann
strUrl := settings.RefreshUrlDisp3
endewenn
wenn lngDisplay = 4 dann
strUrl := settings.RefreshUrlDisp4
endewenn
wenn lngDisplay = 5 dann
strUrl := settings.RefreshUrlDisp5
endewenn
wenn dm_SetAction.lngDebug > 0 dann
// ins Syslog schreiben
syslog.optNoWrite := 1
syslog.strMessage := "dm_SetAction0: trigger Display "+lngDisplay+" mit URL '" + strURL + "'"
aufrufen syslog
endewenn
wenn dm_SetAction.lngTriggerMethod = 1 dann
// Zeitverbrauch...
getsite(strURL, 8080, strverlassen)
wenn dm_SetAction.lngDebug > 0 dann
// ins Syslog schreiben
syslog.optNoWrite := 1
syslog.strMessage := "dm_SetAction0: Display "+lngDisplay+" wurde getriggert, Antwort: "+strverlassen
aufrufen syslog
endewenn
sonst
strUrl := "http://" + getstrpar(strURL, 1, "/") + ":8080/" + getstrpar(strURL, 2, "/")
// m_execute macht im Prinzip nichts anderes als Startprogramm
m_execute.strCommand := "/usr/bin/wget -q -O /dev/nul "+strURL
m_execute.lngNoSyslog := 1 - dm_SetAction.lngDebug
aufrufen m_execute
endewenn
lngDisplay := 0
Code: Alles auswählen
//! ============================================================
//! OBJEKT dm_InitDisplay
//! ============================================================
//! OBJEKT-TYP : Makro
//! BEZEICHNUNG : dm_InitDisplay
//! STARTWERT :
//! ------------------------------------------------------------
//! AUSFÜHRUNGSINTERVALL : nein
//! AUSFÜHRUNG BEI EINGABE : ja
//! AUSFÜHRUNG BEI EMPFANG : nein
//! AUSFÜHRUNG BEI ÄNDERUNG : nein
//! ------------------------------------------------------------
//! KONF.:ALLV=0/MAV=0
//!
//! ============================================================
//! VARIABLENDEFINITIONEN
//! ============================================================
//! NAME TYP STARTWERT
//! ------------------------------------------------------------
// Button Homestatus-Display einstellen
dm_SetAction.lngDisplay := 3
dm_SetAction.strButton := "B4" // z.B. A1, A2 oder D4
dm_SetAction.strValue := "{text: Uhrzeit<br> ansagen}"
dm_SetAction.strValue := dm_SetAction.strValue + "{color:black}"
dm_SetAction.strValue := dm_SetAction.strValue + "{backgroundColor:grey}"
dm_SetAction.strValue := dm_SetAction.strValue + "{img:clock_" + settings.PicSize + ".png}"
dm_SetAction.strValue := dm_SetAction.strValue + "{action:BidCoS-RF:49,PRESS_SHORT}"
dm_SetAction.strValue := dm_SetAction.strValue + "{fontSize:"+settings.FontSize2Rows+"}"
// sofort ausführen und erst nach Rückkehr weitermachen, im dm_SetAction wird ein neuer "Thread" gestartet
aufrufen dm_SetAction
// .....
// Sprache auf einem HomeStatus-Display ausgeben
dm_Say.lngDisplay := 3
dm_Say.strText := "Display wurde neu initialisiert."
// Das Makro dm_Say startet wiederum einen Workerthread per dm_SetAction, um die Say-Aktion ans Tablet zu schicken
aufrufen dm_Say
Was meine Lösung gar nicht berücksichtigt ist z.B. die Möglichkeit, mehrere Displays mit einer Systemvariablen zu steuern, und wenn man mehr als 10 Systemvariablen in kurzer Zeit setzen will, müsste man evtl auch die Anzahl der Workerthreads erhöhen, bei z.B. 24 Buttons (4x6) währe man mit 30 Workerthreads auf der sicheren Seite , falls beim erstmaligen Initialisieren des Displays alle 24 Buttons auf einmal gesetzt werden...
Viel Zeugs, gell?
Der Familienvater